Move tests, create makefile action to run tests on examples (#433)

* Move tests, create makefile action to run tests on examples

* Correct import file for html files

* Build environment for tests

* Fix the CI

* rearrange CI

* fix find cmd and make sure we don't delete the folder implicitly

* more rearranging

* fix folder permissions and custom sed for subfolders

* add toga wheels files

* re-add missing file

* mirror latest changes in alpha ci

* fix find cmd

* try different fix for find

* remove redundant build

Co-authored-by: mariana <marianameireles@protonmail.com>
Co-authored-by: pww217 <pwilson@anaconda.com>
Co-authored-by: Fabio Pliger <fabio.pliger@gmail.com>
This commit is contained in:
Mariana Meireles
2022-05-26 18:31:38 -03:00
committed by GitHub
parent 2c5ef95027
commit a9470ed9c1
111 changed files with 23 additions and 3969 deletions

View File

@@ -12,6 +12,7 @@ env := $(CONDA_ENV)
conda_run := $(CONDA_EXE) run -p $(env)
setup:
npm install
$(CONDA_EXE) env $(shell [ -d $(env) ] && echo update || echo create) -p $(env) --file environment.yml
$(conda_run) playwright install
$(CONDA_EXE) install -c anaconda pytest
@@ -33,7 +34,17 @@ dev:
build:
npm run build
example:
mkdir -p ./examples
cp -r ../examples/* ./examples
chmod -R 755 examples
find ./examples/toga -type f -name '*.html' -exec sed -i '' s+https://pyscript.net/alpha/+../build/+g {} \;
find ./examples/webgl -type f -name '*.html' -exec sed -i '' s+https://pyscript.net/alpha/+../../build/+g {} \;
find ./examples -type f -name '*.html' -exec sed -i '' s+https://pyscript.net/alpha/+./build/+g {} \;
test:
make example
npm run build
$(conda_run) pytest -vv $(ARGS) tests/ --log-cli-level=warning
test-py:

View File

@@ -1,60 +0,0 @@
<html>
<head>
<title>Altair</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-env>
- altair
- pandas
- vega_datasets
</py-env>
</head>
<body>
<div id="altair" style="width: 100%; height: 100%"></div>
<py-script output="altair">
import altair as alt
from vega_datasets import data
source = data.movies.url
pts = alt.selection(type="single", encodings=['x'])
rect = alt.Chart(data.movies.url).mark_rect().encode(
alt.X('IMDB_Rating:Q', bin=True),
alt.Y('Rotten_Tomatoes_Rating:Q', bin=True),
alt.Color('count()',
scale=alt.Scale(scheme='greenblue'),
legend=alt.Legend(title='Total Records')
)
)
circ = rect.mark_point().encode(
alt.ColorValue('grey'),
alt.Size('count()',
legend=alt.Legend(title='Records in Selection')
)
).transform_filter(
pts
)
bar = alt.Chart(source).mark_bar().encode(
x='Major_Genre:N',
y='count()',
color=alt.condition(pts, alt.ColorValue("steelblue"), alt.ColorValue("grey"))
).properties(
width=550,
height=200
).add_selection(pts)
alt.vconcat(
rect + circ,
bar
).resolve_legend(
color="independent",
size="independent"
)
</py-script>
</body>
</html>

View File

@@ -1,20 +0,0 @@
<html>
<head>
<title>Antigravity</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<py-env>
- paths:
- ./antigravity.py
</py-env>
<body>
<b>Based on xkcd: antigravity https://xkcd.com/353/.</b>
<py-script>
import antigravity
antigravity.fly()
</py-script>
</body>
</html>

View File

@@ -1,45 +0,0 @@
import random
import sys
from js import DOMParser, document, setInterval
from pyodide import create_proxy
from pyodide.http import open_url
class Antigravity:
url = "./antigravity.svg"
def __init__(self, target=None, interval=10, append=True, fly=False):
target = target or sys.stdout._out
self.target = (
document.getElementById(target) if isinstance(target, str) else target
)
doc = DOMParser.new().parseFromString(
open_url(self.url).read(), "image/svg+xml"
)
self.node = doc.documentElement
if append:
self.target.append(self.node)
else:
self.target.replaceChildren(self.node)
self.xoffset, self.yoffset = 0, 0
self.interval = interval
if fly:
self.fly()
def fly(self):
setInterval(create_proxy(self.move), self.interval)
def move(self):
char = self.node.getElementsByTagName("g")[1]
char.setAttribute("transform", f"translate({self.xoffset}, {-self.yoffset})")
self.xoffset += random.normalvariate(0, 1) / 20
if self.yoffset < 50:
self.yoffset += 0.1
else:
self.yoffset += random.normalvariate(0, 1) / 20
_auto = Antigravity(append=True)
fly = _auto.fly

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 212 KiB

View File

@@ -1,48 +0,0 @@
<html><head>
<title>Bokeh Example</title>
<meta charset="iso-8859-1">
<link rel="icon" type="image/x-icon" href="./favicon.png">
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.2.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<py-env>
- bokeh
- numpy
</py-env>
<h1>Bokeh Example</h1>
<div id="myplot"></div>
<py-script id="main">
import json
import pyodide
from js import Bokeh, console, JSON
from bokeh.embed import json_item
from bokeh.plotting import figure
from bokeh.resources import CDN
# create a new plot with default tools, using figure
p = figure(plot_width=400, plot_height=400)
# add a circle renderer with x and y coordinates, size, color, and alpha
p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=15, line_color="navy", fill_color="orange", fill_alpha=0.5)
p_json = json.dumps(json_item(p, "myplot"))
Bokeh.embed.embed_item(JSON.parse(p_json))
</py-script>
</body>
</html>

View File

@@ -1,98 +0,0 @@
<html><head>
<title>Bokeh Example</title>
<meta charset="iso-8859-1">
<link rel="icon" type="image/x-icon" href="./favicon.png">
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.2.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<py-env>
- bokeh
- numpy
</py-env>
<h1>Bokeh Example</h1>
<div id="myplot"></div>
<py-script id="main">
import asyncio
import json
import pyodide
from js import Bokeh, console, JSON
from bokeh import __version__
from bokeh.document import Document
from bokeh.embed.util import OutputDocumentFor, standalone_docs_json_and_render_items
from bokeh.models import Slider, Div
from bokeh.layouts import Row
from bokeh.protocol.messages.patch_doc import process_document_events
# create a new plot with default tools, using figure
p = Slider(start=0.1, end=10, value=1, step=.1, title="Amplitude")
div = Div(text=f'Amplitude is: {p.value}')
def callback(attr, old, new):
div.text = f'Amplitude is: {new}'
p.on_change('value', callback)
row = Row(children=[p, div])
def doc_json(model, target):
with OutputDocumentFor([model]) as doc:
doc.title = ""
docs_json, _ = standalone_docs_json_and_render_items(
[model], suppress_callback_warning=True
)
doc_json = list(docs_json.values())[0]
root_id = doc_json['roots']['root_ids'][0]
return doc, json.dumps(dict(
target_id = target,
root_id = root_id,
doc = doc_json,
version = __version__,
))
def _link_docs(pydoc, jsdoc):
def jssync(event):
if getattr(event, 'setter_id', None) is not None:
return
events = [event]
json_patch = jsdoc.create_json_patch_string(pyodide.to_js(events))
pydoc.apply_json_patch(json.loads(json_patch))
jsdoc.on_change(pyodide.create_proxy(jssync), pyodide.to_js(False))
def pysync(event):
json_patch, buffers = process_document_events([event], use_buffers=True)
buffer_map = {}
for (ref, buffer) in buffers:
buffer_map[ref['id']] = buffer
jsdoc.apply_json_patch(JSON.parse(json_patch), pyodide.to_js(buffer_map), setter_id='js')
pydoc.on_change(pysync)
async def show(plot, target):
pydoc, model_json = doc_json(plot, target)
views = await Bokeh.embed.embed_item(JSON.parse(model_json))
jsdoc = views[0].model.document
_link_docs(pydoc, jsdoc)
await show(row, 'myplot')
</py-script>
</body>
</html>

View File

@@ -1,169 +0,0 @@
<html>
<head>
<title>d3: JavaScript & PyScript visualizations side-by-side</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<style>
.loading {
display: inline-block;
width: 50px;
height: 50px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: black;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<b>
Based on <i><a href="https://observablehq.com/@d3/learn-d3-shapes?collection=@d3/learn-d3>">Learn D3: Shapes</a></i> tutorial.
</b>
<div style="display: flex; flex-direction: row">
<div>
<div style="text-align: center">JavaScript version</div>
<div id="js" style="width: 400px; height: 400px">
<div class="loading"></div>
</div>
</div>
<div>
<div style="text-align: center">PyScript version</div>
<div id="py" style="width: 400px; height: 400px">
<div class="loading"></div>
</div>
</div>
</div>
<script type="importmap">
{
"imports": {
"d3": "https://cdn.skypack.dev/d3@7"
}
}
</script>
<script type="module">
import * as d3 from "d3"
const fruits = [
{name: "🍊", count: 21},
{name: "🍇", count: 13},
{name: "🍏", count: 8},
{name: "🍌", count: 5},
{name: "🍐", count: 3},
{name: "🍋", count: 2},
{name: "🍎", count: 1},
{name: "🍉", count: 1},
]
const fn = (d) => d.count
const data = d3.pie().value(fn)(fruits)
const arc = d3.arc()
.innerRadius(210)
.outerRadius(310)
.padRadius(300)
.padAngle(2 / 300)
.cornerRadius(8)
const js = d3.select("#js")
js.select(".loading").remove()
const svg = js
.append("svg")
.attr("viewBox", "-320 -320 640 640")
.attr("width", "400")
.attr("height", "400")
for (const d of data) {
svg.append("path")
.style("fill", "steelblue")
.attr("d", arc(d))
const text = svg.append("text")
.style("fill", "white")
.attr("transform", `translate(${arc.centroid(d).join(",")})`)
.attr("text-anchor", "middle")
text.append("tspan")
.style("font-size", "24")
.attr("x", "0").text(d.data.name)
text.append("tspan")
.style("font-size", "18")
.attr("x", "0")
.attr("dy", "1.3em")
.text(d.value)
}
</script>
<py-script>
from pyodide import create_proxy, to_js
import d3
fruits = [
dict(name="🍊", count=21),
dict(name="🍇", count=13),
dict(name="🍏", count=8),
dict(name="🍌", count=5),
dict(name="🍐", count=3),
dict(name="🍋", count=2),
dict(name="🍎", count=1),
dict(name="🍉", count=1),
]
fn = create_proxy(lambda d, *_: d["count"])
data = d3.pie().value(fn)(to_js(fruits))
arc = (d3.arc()
.innerRadius(210)
.outerRadius(310)
.padRadius(300)
.padAngle(2 / 300)
.cornerRadius(8))
py = d3.select("#py")
py.select(".loading").remove()
svg = (py
.append("svg")
.attr("viewBox", "-320 -320 640 640")
.attr("width", "400")
.attr("height", "400"))
for d in data:
d_py = d.to_py()
(svg.append("path")
.style("fill", "steelblue")
.attr("d", arc(d)))
text = (svg.append("text")
.style("fill", "white")
.attr("transform", f"translate({arc.centroid(d).join(',')})")
.attr("text-anchor", "middle"))
(text.append("tspan")
.style("font-size", "24")
.attr("x", "0")
.text(d_py["data"]["name"]))
(text.append("tspan")
.style("font-size", "18")
.attr("x", "0")
.attr("dy", "1.3em")
.text(d_py["value"]))
</py-script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,49 +0,0 @@
<html>
<head>
<title>Folium</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-env>
- folium
- pandas
</py-env>
</head>
<body>
<div id="folium" style="width: 100%; height: 100%"></div>
<py-script output="folium">
import folium
import json
import pandas as pd
from pyodide.http import open_url
url = (
"https://raw.githubusercontent.com/python-visualization/folium/master/examples/data"
)
state_geo = f"{url}/us-states.json"
state_unemployment = f"{url}/US_Unemployment_Oct2012.csv"
state_data = pd.read_csv(open_url(state_unemployment))
geo_json = json.loads(open_url(state_geo).read())
m = folium.Map(location=[48, -102], zoom_start=3)
folium.Choropleth(
geo_data=geo_json,
name="choropleth",
data=state_data,
columns=["State", "Unemployment"],
key_on="feature.id",
fill_color="YlGn",
fill_opacity=0.7,
line_opacity=0.2,
legend_name="Unemployment Rate (%)",
).add_to(m)
folium.LayerControl().add_to(m)
m
</py-script>
</body>
</html>

View File

@@ -1,139 +0,0 @@
import numpy as np
from numpy.polynomial import Polynomial
def mandelbrot(
width: int,
height: int,
*,
x: float = -0.5,
y: float = 0,
zoom: int = 1,
max_iterations: int = 100
) -> np.array:
"""
https://www.learnpythonwithrune.org/numpy-compute-mandelbrot-set-by-vectorization
"""
# To make navigation easier we calculate these values
x_width, y_height = 1.5, 1.5 * height / width
x_from, x_to = x - x_width / zoom, x + x_width / zoom
y_from, y_to = y - y_height / zoom, y + y_height / zoom
# Here the actual algorithm starts
x = np.linspace(x_from, x_to, width).reshape((1, width))
y = np.linspace(y_from, y_to, height).reshape((height, 1))
c = x + 1j * y
# Initialize z to all zero
z = np.zeros(c.shape, dtype=np.complex128)
# To keep track in which iteration the point diverged
div_time = np.zeros(z.shape, dtype=int)
# To keep track on which points did not converge so far
m = np.full(c.shape, True, dtype=bool)
for i in range(max_iterations):
z[m] = z[m] ** 2 + c[m]
diverged = np.greater(
np.abs(z), 2, out=np.full(c.shape, False), where=m
) # Find diverging
div_time[diverged] = i # set the value of the diverged iteration number
m[np.abs(z) > 2] = False # to remember which have diverged
return div_time
def julia(
width: int,
height: int,
*,
c: complex = -0.4 + 0.6j,
x: float = 0,
y: float = 0,
zoom: int = 1,
max_iterations: int = 100
) -> np.array:
"""
https://www.learnpythonwithrune.org/numpy-calculate-the-julia-set-with-vectorization
"""
# To make navigation easier we calculate these values
x_width, y_height = 1.5, 1.5 * height / width
x_from, x_to = x - x_width / zoom, x + x_width / zoom
y_from, y_to = y - y_height / zoom, y + y_height / zoom
# Here the actual algorithm starts
x = np.linspace(x_from, x_to, width).reshape((1, width))
y = np.linspace(y_from, y_to, height).reshape((height, 1))
z = x + 1j * y
# Initialize z to all zero
c = np.full(z.shape, c)
# To keep track in which iteration the point diverged
div_time = np.zeros(z.shape, dtype=int)
# To keep track on which points did not converge so far
m = np.full(c.shape, True, dtype=bool)
for i in range(max_iterations):
z[m] = z[m] ** 2 + c[m]
m[np.abs(z) > 2] = False
div_time[m] = i
return div_time
Range = tuple[float, float]
def newton(
width: int,
height: int,
*,
p: Polynomial,
a: complex,
xr: Range = (-2.5, 1),
yr: Range = (-1, 1),
max_iterations: int = 100
) -> tuple[np.array, np.array]:
""" """
# To make navigation easier we calculate these values
x_from, x_to = xr
y_from, y_to = yr
# Here the actual algorithm starts
x = np.linspace(x_from, x_to, width).reshape((1, width))
y = np.linspace(y_from, y_to, height).reshape((height, 1))
z = x + 1j * y
# Compute the derivative
dp = p.deriv()
# Compute roots
roots = p.roots()
epsilon = 1e-5
# Set the initial conditions
a = np.full(z.shape, a)
# To keep track in which iteration the point diverged
div_time = np.zeros(z.shape, dtype=int)
# To keep track on which points did not converge so far
m = np.full(a.shape, True, dtype=bool)
# To keep track which root each point converged to
r = np.full(a.shape, 0, dtype=int)
for i in range(max_iterations):
z[m] = z[m] - a[m] * p(z[m]) / dp(z[m])
for j, root in enumerate(roots):
converged = (np.abs(z.real - root.real) < epsilon) & (
np.abs(z.imag - root.imag) < epsilon
)
m[converged] = False
r[converged] = j + 1
div_time[m] = i
return div_time, r

File diff suppressed because one or more lines are too long

View File

@@ -1,122 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="../https://pyscript.net/alpha/pyscript.css" />
<script defer src="../https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<py-script>
from js import handTrack, requestAnimationFrame
from pyodide import create_once_callable
import asyncio
context = canvas.element.getContext("2d")
isVideo = False
model = None
modelParams = {
"flipHorizontal": True, # flip e.g for video
"maxNumBoxes": 20, # maximum number of boxes to detect
"iouThreshold": 0.5, # ioU threshold for non-max suppression
"scoreThreshold": 0.6, # confidence threshold for predictions.
}
def toggle_video(evt):
global isVideo
if (not isVideo):
update_note.write("Starting video")
pyscript.run_until_complete(start_video())
else:
update_note.write("Stopping video")
handTrack.stopVideo(video.element)
isVideo = False
update_note.write("Video stopped")
async def start_video():
global isVideo
update_note.write("Inside sstart video")
status = await handTrack.startVideo(video.element)
console.log("video started", status)
if status:
update_note.write("Video started. Now tracking")
isVideo = True
console.log( "Calling RUN DETECTION")
y = await run_detection()
else:
update_note.write( "Please enable video")
def sync_run_detection(evt):
pyscript.run_until_complete(run_detection())
async def run_detection():
console.log("in RUN DETECTION: ");
global model
global isVideo
console.log("...1")
predictions = await model.detect(video.element)
console.log("done...1")
console.log("Predictions: ", predictions);
model.renderPredictions(predictions, canvas.element, context, video.element);
console.log("is Video?", isVideo)
if (isVideo):
console.log("requestingAnimation!")
await requestAnimationFrame(create_once_callable(sync_run_detection));
console.log("...2")
def run_detection_image(img):
console.log("in RUN DETECTION IMAGE", predictions);
global model
def detect(predition):
console.log("Predictions: ", predictions);
model.renderPredictions(predictions, canvas, context, img);
console.log("...3")
model.detect(img).then(detect)
console.log("...4")
def handle_model(lmodel):
global model
model = lmodel
update_note.write("Loaded Model!")
async def start():
console.log("creating x")
console.log("calling x")
model = await handTrack.load(modelParams)#.then(handle_model)
console.log("loaded model!")
console.log(model)
handle_model(model)
print(dir(x))
print(x)
pyscript.run_until_complete(start())
#});
</py-script>
<div class="mb10">
<button id="trackbutton" class="bx--btn bx--btn--secondary" type="button" pys-onClick="toggle_video">
Toggle Video
</button>
<button id="nextimagebutton" class="mt10 bx--btn bx--btn--secondary" type="button" disabled>
Next Image
</button>
<div id="update-note" py-mount class="updatenote mt10">loading model ..</div>
</div>
<div>
<video autoplay="autoplay" id="myvideo" py-mount="video"></video>
<canvas id="canvas" py-mount class="border canvasbox"></canvas>
</div>
<script src="lib/handtrack.min.js"> </script>
</html>

View File

@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>PyScript Hello World</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
Hello world! <br>
This is the current date and time, as computed by Python:
<py-script>
from datetime import datetime
now = datetime.now()
now.strftime("%m/%d/%Y, %H:%M:%S")
</py-script>
</body>
</html>

View File

@@ -1,106 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>PyScript demo</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
</head>
<body class="container font-mono">
<h1 class="text-4xl font-bold">PyScript demos</h1>
<br>
<h2 class="text-3xl font-bold">Basic examples</h2>
<hr/>
<h2 class="text-2xl font-bold text-blue-600"><a href="./hello_world.html" target=”_blank”>Hello world</a></h2>
<p>A static demo of the <code>&lt;py-script&gt;</code> tag</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./simple_clock.html" target=”_blank”>Simple clock</a></h2>
<p>A dynamic demo of the <code>&lt;py-script&gt;</code> tag</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./repl.html" target=”_blank”>REPL</a></h2>
<p>A Python REPL (Read Eval Print Loop). </p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./repl2.html" target=”_blank”>REPL2</a></h2>
<p>A Python REPL (Read Eval Print Loop) with slightly better formatting.</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./todo.html" target=”_blank”>TODO App</a></h2>
<p>Demo showing how would a Simple TODO App would look like in PyScript</code> tag</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./todo-pylist.html" target=”_blank”>PyScript Native TODO App</a></h2>
<p>Demo showing how would a Simple TODO App would look like only using PyScript native elements</code> tag</p>
<br>
<h2 class="text-3xl font-bold">MIME Rendering</h2>
<hr/>
<h2 class="text-2xl font-bold text-blue-600"><a href="./matplotlib.html" target=”_blank”>Matplotlib</a></h2>
<p>Demonstrates rendering <a href="https://matplotlib.org/" class="text-blue-600" target=”_blank”>Matplotlib</a> figure as output of the py-script tag</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./altair.html" target=”_blank”>Altair</a></h2>
<p>Demonstrates rendering <a href="https://altair-viz.github.io/" class="text-blue-600" target=”_blank”>Altair</a> plot as output of the py-script tag</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./folium.html" target=”_blank”>Folium</a></h2>
<p>Demonstrates rendering <a href="https://python-visualization.github.io/folium/" class="text-blue-600" target=”_blank”>Folium</a> map as output of the py-script tag</p>
<br>
<h2 class="text-3xl font-bold">JS Interaction</h2>
<hr/>
<h2 class="text-2xl font-bold text-blue-600"><a href="./d3.html" target=”_blank”>Simple d3 visualization</a></h2>
<p>Minimal <a href="https://d3js.org/" class="text-blue-600" target=”_blank”>D3</a> demo demonstrating how to create a visualization</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./webgl/raycaster/index.html" target=”_blank”>Webgl Icosahedron Example</a></h2>
<p>Demo showing how a Simple <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API" class="text-blue-600" target=”_blank”>WebGL</a> scene would work in PyScript</code> tag</p>
<br>
<h2 class="text-3xl font-bold">Visualizations & Dashboards</h2>
<hr/>
<h2 class="text-2xl font-bold text-blue-600"><a href="./bokeh.html" target=”_blank”>Simple Static Bokeh Plot</a></h2>
<p>Minimal Bokeh demo demonstrating how to create a simple <a href="https://bokeh.org/" class="text-blue-600" target=”_blank”>Bokeh</a> plot from code</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./bokeh_interactive.html" target=”_blank”>Bokeh Interactive</a></h2>
<p>Interactive demo using a <a href="https://bokeh.org/" class="text-blue-600" target=”_blank”>Bokeh</a> slider widget to dynamically change a value in the page
WARNING: This examples takes a little longer to load. So be patient :)
</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./panel_kmeans.html" target=”_blank”>KMeans Demo in Panel</a></h2>
<p>Interactive KMeans Chart using <a href="https://panel.holoviz.org/" class="text-blue-600" target=”_blank”>Panel</a>
WARNING: This examples takes a little longer to load. So be patient :)
</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./panel_stream.html" target=”_blank”>Streaming Demo in Panel</a></h2>
<p>Interactive Streaming Table and Bokeh plot using <a href="https://panel.holoviz.org/" class="text-blue-600" target=”_blank”>Panel</a>
WARNING: This examples takes a little longer to load. So be patient :)
</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./panel.html" target=”_blank”>Simple Panel Demo</a></h2>
<p>Simple demo showing <a href="https://panel.holoviz.org/" class="text-blue-600" target=”_blank”>Panel</a> widgets interacting with parts of the page
WARNING: This examples takes a little longer to load. So be patient :)
</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./panel_deckgl.html" target=”_blank”>NYC Taxi Data Panel DeckGL Demo</a></h2>
<p>Interactive application exploring the NYC Taxi dataset using <a href="https://panel.holoviz.org/" class="text-blue-600" target=”_blank”>Panel</a> and <a href="https://deck.gl/" class="text-blue-600" target=”_blank”>DeckGL</a>
WARNING: This examples takes a little longer to load. So be patient :)
</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./toga/freedom.html" target=”_blank”>Freedom Units!</a></h2>
<p>A <a href="https://beeware.org/project/projects/libraries/toga/" class="text-blue-600" target=”_blank”>Toga</a> application (a Fahrenheit to Celsius converter), rendered as a Single Page App</p>
<h2 class="text-2xl font-bold text-blue-600"><a href="./numpy_canvas_fractals.html" target=”_blank”>Fractals with NumPy and canvas</a></h2>
<p>Visualization of Mandelbrot and Julia sets with <a href="https://numpy.org/" class="text-blue-600" target=”_blank”>Numpy</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API" class="text-blue-600" target=”_blank”>HTML5 canvas</a></p>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -1,32 +0,0 @@
html, body, ul, li {
margin: 0;
border: 0;
padding: 0;
}
canvas {
display: block;
width: 762;
margin: 0 auto;
background-color: blue;
}
p {
text-align: center;
}
body {
overflow: hidden;
height: 100%;
}
html {
overflow: hidden;
height: 100%;
}
.info {
position: absolute;
top: 0;
left: 0;
}

View File

@@ -1,45 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Bcoin = Mario.Bcoin = function(pos) {
Mario.Entity.call(this, {
pos: pos,
sprite: level.bcoinSprite(),
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Bcoin, Mario.Entity);
//I'm not sure whether it makes sense to use an array for vel and acc here
//in order to keep with convention, or to just use a single value, since
//it's literally impossible for these to move left or right.
Bcoin.prototype.spawn = function() {
sounds.coin.currentTime = 0.05;
sounds.coin.play();
this.idx = level.items.length;
level.items.push(this);
this.active = true;
this.vel = -12;
this.targetpos = this.pos[1] - 32;
}
Bcoin.prototype.update = function(dt) {
if (!this.active) return;
if (this.vel > 0 && this.pos[1] >= this.targetpos) {
player.coins += 1;
//spawn a score thingy.
delete level.items[this.idx];
}
this.acc = 0.75;
this.vel += this.acc;
this.pos[1] += this.vel;
this.sprite.update(dt);
}
Bcoin.prototype.checkCollisions = function() {;}
})();

View File

@@ -1,81 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//TODO: clean up the logic for sprite switching.
//TODO: There's a weird bug with the collision logic. Look into it.
var Block = Mario.Block = function(options) {
this.item = options.item;
this.usedSprite = options.usedSprite;
this.bounceSprite = options.bounceSprite;
this.breakable = options.breakable;
Mario.Entity.call(this, {
pos: options.pos,
sprite: options.sprite,
hitbox: [0,0,16,16]
});
this.standing = true;
}
Mario.Util.inherits(Block, Mario.Floor);
Block.prototype.break = function() {
sounds.breakBlock.play();
(new Mario.Rubble()).spawn(this.pos);
var x = this.pos[0] / 16, y = this.pos[1] / 16;
delete level.blocks[y][x];
}
Block.prototype.bonk = function(power) {
sounds.bump.play();
if (power > 0 && this.breakable) {
this.break();
} else if (this.standing){
this.standing = false;
if (this.item) {
this.item.spawn();
this.item = null;
}
this.opos = [];
this.opos[0] = this.pos[0];
this.opos[1] = this.pos[1];
if (this.bounceSprite) {
this.osprite = this.sprite;
this.sprite = this.bounceSprite;
} else {
this.sprite = this.usedSprite;
}
this.vel[1] = -2;
}
}
Block.prototype.update = function(dt, gameTime) {
if (!this.standing) {
if (this.pos[1] < this.opos[1] - 8) {
this.vel[1] = 2;
}
if (this.pos[1] > this.opos[1]) {
this.vel[1] = 0;
this.pos = this.opos;
if (this.osprite) {
this.sprite = this.osprite;
}
this.standing = true;
}
} else {
if (this.sprite === this.usedSprite) {
var x = this.pos[0] / 16, y = this.pos[1] / 16;
level.statics[y][x] = new Mario.Floor(this.pos, this.usedSprite);
delete level.blocks[y][x];
}
}
this.pos[1] += this.vel[1];
this.sprite.update(dt, gameTime);
}
})();

View File

@@ -1,47 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Coin = Mario.Coin = function(pos, sprite) {
Mario.Entity.call(this, {
pos: pos,
sprite: sprite,
hitbox: [0,0,16,16]
});
this.idx = level.items.length
}
Mario.Util.inherits(Coin, Mario.Entity);
Coin.prototype.isPlayerCollided = function() {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [player.pos[0] + player.hitbox[0], player.pos[1] + player.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+player.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+player.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
this.collect();
}
}
}
Coin.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
//money is not affected by gravity, you see.
Coin.prototype.update = function(dt) {
this.sprite.update(dt);
}
Coin.prototype.checkCollisions = function() {
this.isPlayerCollided();
}
Coin.prototype.collect = function() {
sounds.coin.currentTime = 0.05;
sounds.coin.play();
player.coins += 1;
delete level.items[this.idx]
}
})();

View File

@@ -1,34 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Entity = Mario.Entity = function(options) {
this.vel = [0,0];
this.acc = [0,0];
this.standing = true;
this.pos = options.pos;
this.sprite = options.sprite;
this.hitbox = options.hitbox;
this.left = false;
}
Entity.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY)
}
Entity.prototype.collideWall = function(wall) {
//the wall will always be a 16x16 block with hitbox = [0,0,16,16].
if (this.pos[0] > wall.pos[0]) {
//from the right
this.pos[0] = wall.pos[0] + wall.hitbox[2] - this.hitbox[0];
this.vel[0] = Math.max(0, this.vel[0]);
this.acc[0] = Math.max(0, this.acc[0]);
} else {
this.pos[0] = wall.pos[0] + wall.hitbox[0] - this.hitbox[2] - this.hitbox[0];
this.vel[0] = Math.min(0, this.vel[0]);
this.acc[0] = Math.min(0, this.acc[0]);
}
}
Entity.prototype.bump = function() {;}
})();

View File

@@ -1,126 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Fireball = Mario.Fireball = function(pos) {
this.hit = 0;
this.standing = false;
Mario.Entity.call(this, {
pos: pos,
sprite: new Mario.Sprite('sprites/items.png', [96, 144], [8,8], 5, [0,1,2,3]),
hitbox: [0,0,8,8]
});
}
Mario.Util.inherits(Fireball, Mario.Entity);
Fireball.prototype.spawn = function(left) {
sounds.fireball.currentTime = 0;
sounds.fireball.play();
if (fireballs[0]) {
this.idx = 1;
fireballs[1] = this;
} else {
this.idx = 0;
fireballs[0] = this;
}
this.vel[0] = (left ? -5 : 5);
this.standing = false;
this.vel[1] = 0;
}
Fireball.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
Fireball.prototype.update = function(dt) {
if (this.hit == 1) {
this.sprite.pos = [96, 160];
this.sprite.size = [16,16];
this.sprite.frames = [0,1,2];
this.sprite.speed = 8;
this.hit += 1;
return;
} else if (this.hit == 5) {
delete fireballs[this.idx];
player.fireballs -= 1;
return;
} else if (this.hit) {
this.hit += 1;
return;
}
//In retrospect, the way collision is being handled is RIDICULOUS
//but I don't have to use some horrible kludge for this.
if (this.standing) {
this.standing = false;
this.vel[1] = -4;
}
this.acc[1] = 0.5;
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
if (this.pos[0] < vX || this.pos[0] > vX + 256) {
this.hit = 1;
}
this.sprite.update(dt);
}
Fireball.prototype.collideWall = function() {
if (!this.hit) this.hit = 1;
}
Fireball.prototype.checkCollisions = function() {
if (this.hit) return;
var h = this.pos[1] % 16 < 8 ? 1 : 2;
var w = this.pos[0] % 16 < 8 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete fireballs[this.idx];
player.fireballs -= 1;
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
var that = this;
level.enemies.forEach(function(enemy){
if (enemy.flipping || enemy.pos[0] - vX > 336){ //stop checking once we get to far away dudes.
return;
} else {
that.isCollideWith(enemy);
}
});
}
Fireball.prototype.isCollideWith = function(ent) {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [ent.pos[0] + ent.hitbox[0], ent.pos[1] + ent.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
this.hit = 1;
ent.bump();
}
}
};
Fireball.prototype.bump = function() {;}
})();

View File

@@ -1,74 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Fireflower = Mario.Fireflower = function(pos) {
this.spawning = false;
this.waiting = 0;
Mario.Entity.call(this, {
pos: pos,
sprite: level.fireFlowerSprite,
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Fireflower, Mario.Entity);
Fireflower.prototype.render = function(ctx, vX, vY) {
if (this.spawning > 1) return;
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
Fireflower.prototype.spawn = function() {
sounds.itemAppear.play();
this.idx = level.items.length;
level.items.push(this);
this.spawning = 12;
this.targetpos = [];
this.targetpos[0] = this.pos[0];
this.targetpos[1] = this.pos[1] - 16;
}
Fireflower.prototype.update = function(dt) {
if (this.spawning > 1) {
this.spawning -= 1;
if (this.spawning == 1) this.vel[1] = -.5;
return;
}
if (this.spawning) {
if (this.pos[1] <= this.targetpos[1]) {
this.pos[1] = this.targetpos[1];
this.vel[1] = 0;
this.spawning = 0;
}
}
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
}
Fireflower.prototype.checkCollisions = function() {
if (this.spawning) {return;}
this.isPlayerCollided();
}
Fireflower.prototype.isPlayerCollided = function() {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [player.pos[0] + player.hitbox[0], player.pos[1] + player.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+player.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+player.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
player.powerUp(this.idx);
}
}
}
//This should never be called, but just in case.
Fireflower.prototype.bump = function() {;}
})();

View File

@@ -1,47 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
Flag = Mario.Flag = function(pos) {
//afaik flags always have the same height and Y-position
this.pos = [pos, 49];
this.hitbox = [0,0,0,0];
this.vel = [0,0];
this.acc = [0,0];
}
Flag.prototype.collideWall = function() {;
}
Flag.prototype.update = function(dt){
if (!this.done && this.pos[1] >= 170) {
this.vel = [0,0];
this.pos[1] = 170;
player.exit();
this.done = true;
}
this.pos[1] += this.vel[1];
}
Flag.prototype.checkCollisions = function() {
this.isPlayerCollided();
}
Flag.prototype.isPlayerCollided = function() {
if (this.hit) return;
if (player.pos[0] + 8 >= this.pos[0]) {
music.overworld.pause();
sounds.flagpole.play();
setTimeout(function() {
music.clear.play();
}, 2000);
this.hit = true;
player.flag();
this.vel = [0, 2];
}
}
Flag.prototype.render = function() {
level.flagpoleSprites[2].render(ctx, this.pos[0]-8, this.pos[1], vX, vY);
}
})();

View File

@@ -1,56 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Floor = Mario.Floor = function(pos, sprite) {
Mario.Entity.call(this, {
pos: pos,
sprite: sprite,
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Floor, Mario.Entity);
Floor.prototype.isCollideWith = function (ent) {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [Math.floor(this.pos[0] + this.hitbox[0]), Math.floor(this.pos[1] + this.hitbox[1])];
var hpos2 = [Math.floor(ent.pos[0] + ent.hitbox[0]), Math.floor(ent.pos[1] + ent.hitbox[1])];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
if (!this.standing) {
ent.bump();
} else {
//if the entity is over the block, it's basically floor
var center = hpos2[0] + ent.hitbox[2] / 2;
if (Math.abs(hpos2[1] + ent.hitbox[3] - hpos1[1]) <= ent.vel[1]) {
if (level.statics[(this.pos[1] / 16) - 1][this.pos[0] / 16]) {return};
ent.vel[1] = 0;
ent.pos[1] = hpos1[1] - ent.hitbox[3] - ent.hitbox[1];
ent.standing = true;
if (ent instanceof Mario.Player) {
ent.jumping = 0;
}
} else if (Math.abs(hpos2[1] - hpos1[1] - this.hitbox[3]) > ent.vel[1] &&
center + 2 >= hpos1[0] && center - 2 <= hpos1[0] + this.hitbox[2]) {
//ent is under the block.
ent.vel[1] = 0;
ent.pos[1] = hpos1[1] + this.hitbox[3];
if (ent instanceof Mario.Player) {
this.bonk(ent.power);
ent.jumping = 0;
}
} else {
//entity is hitting it from the side, we're a wall
ent.collideWall(this);
}
}
}
}
}
Floor.prototype.bonk = function() {;}
})();

View File

@@ -1,241 +0,0 @@
var requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
//create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
var updateables = [];
var fireballs = [];
var player = new Mario.Player([0,0]);
//we might have to get the size and calculate the scaling
//but this method should let us make it however big.
//Cool!
//TODO: Automatically scale the game to work and look good on widescreen.
//TODO: fiddling with scaled sprites looks BETTER, but not perfect. Hmm.
canvas.width = 762;
canvas.height = 720;
ctx.scale(3,3);
document.body.appendChild(canvas);
//viewport
var vX = 0,
vY = 0,
vWidth = 256,
vHeight = 240;
//load our images
resources.load([
'sprites/player.png',
'sprites/enemy.png',
'sprites/tiles.png',
'sprites/playerl.png',
'sprites/items.png',
'sprites/enemyr.png',
]);
resources.onReady(init);
var level;
var sounds;
var music;
//initialize
var lastTime;
function init() {
music = {
overworld: new Audio('sounds/aboveground_bgm.ogg'),
underground: new Audio('sounds/underground_bgm.ogg'),
clear: new Audio('sounds/stage_clear.wav'),
death: new Audio('sounds/mariodie.wav')
};
sounds = {
smallJump: new Audio('sounds/jump-small.wav'),
bigJump: new Audio('sounds/jump-super.wav'),
breakBlock: new Audio('sounds/breakblock.wav'),
bump: new Audio('sounds/bump.wav'),
coin: new Audio('sounds/coin.wav'),
fireball: new Audio('sounds/fireball.wav'),
flagpole: new Audio('sounds/flagpole.wav'),
kick: new Audio('sounds/kick.wav'),
pipe: new Audio('sounds/pipe.wav'),
itemAppear: new Audio('sounds/itemAppear.wav'),
powerup: new Audio('sounds/powerup.wav'),
stomp: new Audio('sounds/stomp.wav')
};
Mario.oneone();
lastTime = Date.now();
main();
}
var gameTime = 0;
//set up the game loop
function main() {
var now = Date.now();
var dt = (now - lastTime) / 1000.0;
update(dt);
render();
lastTime = now;
requestAnimFrame(main);
}
function update(dt) {
gameTime += dt;
handleInput(dt);
updateEntities(dt, gameTime);
checkCollisions();
}
function handleInput(dt) {
if (player.piping || player.dying || player.noInput) return; //don't accept input
if (input.isDown('RUN')){
player.run();
} else {
player.noRun();
}
if (input.isDown('JUMP')) {
player.jump();
} else {
//we need this to handle the timing for how long you hold it
player.noJump();
}
if (input.isDown('DOWN')) {
player.crouch();
} else {
player.noCrouch();
}
if (input.isDown('LEFT')) { // 'd' or left arrow
player.moveLeft();
}
else if (input.isDown('RIGHT')) { // 'k' or right arrow
player.moveRight();
} else {
player.noWalk();
}
}
//update all the moving stuff
function updateEntities(dt, gameTime) {
player.update(dt, vX);
updateables.forEach (function(ent) {
ent.update(dt, gameTime);
});
//This should stop the jump when he switches sides on the flag.
if (player.exiting) {
if (player.pos[0] > vX + 96)
vX = player.pos[0] - 96
}else if (level.scrolling && player.pos[0] > vX + 80) {
vX = player.pos[0] - 80;
}
if (player.powering.length !== 0 || player.dying) { return; }
level.items.forEach (function(ent) {
ent.update(dt);
});
level.enemies.forEach (function(ent) {
ent.update(dt, vX);
});
fireballs.forEach(function(fireball) {
fireball.update(dt);
});
level.pipes.forEach (function(pipe) {
pipe.update(dt);
});
}
//scan for collisions
function checkCollisions() {
if (player.powering.length !== 0 || player.dying) { return; }
player.checkCollisions();
//Apparently for each will just skip indices where things were deleted.
level.items.forEach(function(item) {
item.checkCollisions();
});
level.enemies.forEach (function(ent) {
ent.checkCollisions();
});
fireballs.forEach(function(fireball){
fireball.checkCollisions();
});
level.pipes.forEach (function(pipe) {
pipe.checkCollisions();
});
}
//draw the game!
function render() {
updateables = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = level.background;
ctx.fillRect(0, 0, canvas.width, canvas.height);
//scenery gets drawn first to get layering right.
for(var i = 0; i < 15; i++) {
for (var j = Math.floor(vX / 16) - 1; j < Math.floor(vX / 16) + 20; j++){
if (level.scenery[i][j]) {
renderEntity(level.scenery[i][j]);
}
}
}
//then items
level.items.forEach (function (item) {
renderEntity(item);
});
level.enemies.forEach (function(enemy) {
renderEntity(enemy);
});
fireballs.forEach(function(fireball) {
renderEntity(fireball);
})
//then we draw every static object.
for(var i = 0; i < 15; i++) {
for (var j = Math.floor(vX / 16) - 1; j < Math.floor(vX / 16) + 20; j++){
if (level.statics[i][j]) {
renderEntity(level.statics[i][j]);
}
if (level.blocks[i][j]) {
renderEntity(level.blocks[i][j]);
updateables.push(level.blocks[i][j]);
}
}
}
//then the player
if (player.invincibility % 2 === 0) {
renderEntity(player);
}
//Mario goes INTO pipes, so naturally they go after.
level.pipes.forEach (function(pipe) {
renderEntity(pipe);
});
}
function renderEntity(entity) {
entity.render(ctx, vX, vY);
}

View File

@@ -1,129 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//TODO: On console the hitbox is smaller. Measure it and edit this.
var Goomba = Mario.Goomba = function(pos, sprite) {
this.dying = false;
Mario.Entity.call(this, {
pos: pos,
sprite: sprite,
hitbox: [0,0,16,16]
});
this.vel[0] = -0.5;
this.idx = level.enemies.length;
};
Goomba.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
};
Goomba.prototype.update = function(dt, vX) {
if (this.pos[0] - vX > 336) { //if we're too far away, do nothing.
return;
} else if (this.pos[0] - vX < -32) {
delete level.enemies[this.idx];
}
if (this.dying) {
this.dying -= 1;
if (!this.dying) {
delete level.enemies[this.idx];
}
}
this.acc[1] = 0.2;
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
};
Goomba.prototype.collideWall = function() {
this.vel[0] = -this.vel[0];
};
Goomba.prototype.checkCollisions = function() {
if (this.flipping) {
return;
}
var h = this.pos[1] % 16 === 0 ? 1 : 2;
var w = this.pos[0] % 16 === 0 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete level.enemies[this.idx];
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
var that = this;
level.enemies.forEach(function(enemy){
if (enemy === that) { //don't check collisions with ourselves.
return;
} else if (enemy.pos[0] - vX > 336){ //stop checking once we get to far away dudes.
return;
} else {
that.isCollideWith(enemy);
}
});
this.isCollideWith(player);
};
Goomba.prototype.isCollideWith = function(ent) {
if (ent instanceof Mario.Player && (this.dying || ent.invincibility)) {
return;
}
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [ent.pos[0] + ent.hitbox[0], ent.pos[1] + ent.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
if (ent instanceof Mario.Player) { //if we hit the player
if (ent.vel[1] > 0) { //then the goomba dies
this.stomp();
} else if (ent.starTime) {
this.bump();
} else { //or the player gets hit
ent.damage();
}
} else {
this.collideWall();
}
}
}
};
Goomba.prototype.stomp = function() {
sounds.stomp.play();
player.bounce = true;
this.sprite.pos[0] = 32;
this.sprite.speed = 0;
this.vel[0] = 0;
this.dying = 10;
};
Goomba.prototype.bump = function() {
sounds.kick.play();
this.sprite.img = 'sprites/enemyr.png';
this.flipping = true;
this.pos[1] -= 1;
this.vel[0] = 0;
this.vel[1] = -2.5;
};
})();

View File

@@ -1,54 +0,0 @@
(function() {
var pressedKeys = {};
function setKey(event, status) {
var code = event.keyCode;
var key;
switch(code) {
case 32:
key = 'SPACE'; break;
case 37:
key = 'LEFT'; break;
case 38:
key = 'UP'; break;
case 39:
key = 'RIGHT'; break;
case 40:
key = 'DOWN'; break;
case 88:
key = 'JUMP'; break;
case 90:
key = 'RUN'; break;
default:
key = String.fromCharCode(code);
}
pressedKeys[key] = status;
}
document.addEventListener('keydown', function(e) {
setKey(e, true);
});
document.addEventListener('keyup', function(e) {
setKey(e, false);
});
window.addEventListener('blur', function() {
pressedKeys = {};
});
window.input = {
isDown: function(key) {
return pressedKeys[key.toUpperCase()];
},
reset: function() {
pressedKeys['RUN'] = false;
pressedKeys['LEFT'] = false;
pressedKeys['RIGHT'] = false;
pressedKeys['DOWN'] = false;
pressedKeys['JUMP'] = false;
}
};
})();

View File

@@ -1,211 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Koopa = Mario.Koopa = function(pos, sprite, para) {
this.dying = false;
this.shell = false;
this.para = para; //para. As in, is it a paratroopa?
//So, funny story. The actual hitboxes don't reach all the way to the ground.
//What that means is, as long as I use them to keep things on the floor
//making the hitboxes accurate will make enemies sink into the ground.
Mario.Entity.call(this, {
pos: pos,
sprite: sprite,
hitbox: [2,8,12,24]
});
this.vel[0] = -0.5;
this.idx = level.enemies.length;
};
Koopa.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
};
Koopa.prototype.update = function(dt, vX) {
if (this.turn) {
this.vel[0] = -this.vel[0];
if (this.shell) sounds.bump.play();
this.turn = false;
}
if (this.vel[0] != 0) {
this.left = (this.vel[0] < 0);
}
if (this.left) {
this.sprite.img = 'sprites/enemy.png';
} else {
this.sprite.img = 'sprites/enemyr.png';
}
if (this.pos[0] - vX > 336) { //if we're too far away, do nothing.
return;
} else if (this.pos[0] - vX < -32) {
delete level.enemies[this.idx];
}
if (this.dying) {
this.dying -= 1;
if (!this.dying) {
delete level.enemies[this.idx];
}
}
if (this.shell) {
if (this.vel[0] == 0) {
this.shell -= 1;
if (this.shell < 120) {
this.sprite.speed = 5;
}
if (this.shell == 0) {
this.sprite = level.koopaSprite();
this.hitbox = [2,8,12,24]
if (this.left) {
this.sprite.img = 'sprites/enemyr.png';
this.vel[0] = 0.5;
this.left = false;
} else {
this.vel[0] = -0.5;
this.left = true;
}
this.pos[1] -= 16;
}
} else {
this.shell = 360;
this.sprite.speed = 0;
this.sprite.setFrame(0);
}
}
this.acc[1] = 0.2;
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
};
Koopa.prototype.collideWall = function() {
//This stops us from flipping twice on the same frame if we collide
//with multiple wall tiles simultaneously.
this.turn = true;
};
Koopa.prototype.checkCollisions = function() {
var h = this.shell ? 1 : 2;
if (this.pos[1] % 16 !== 0) {
h += 1;
}
var w = this.pos[0] % 16 === 0 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete level.enemies[this.idx];
return;
}
if (this.flipping) {
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
var that = this;
level.enemies.forEach(function(enemy){
if (enemy === that) { //don't check collisions with ourselves.
return;
} else if (enemy.pos[0] - vX > 336){ //stop checking once we get to far away dudes.
return;
} else {
that.isCollideWith(enemy);
}
});
this.isCollideWith(player);
};
Koopa.prototype.isCollideWith = function(ent) {
if (ent instanceof Mario.Player && (this.dying || ent.invincibility)) {
return;
}
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [ent.pos[0] + ent.hitbox[0], ent.pos[1] + ent.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
if (ent instanceof Mario.Player) {
if (ent.vel[1] > 0) {
player.bounce = true;
}
if (this.shell) {
sounds.kick.play();
if (this.vel[0] === 0) {
if (ent.left) { //I'm pretty sure this isn't the real logic.
this.vel[0] = -4;
} else {
this.vel[0] = 4;
}
} else {
if (ent.bounce) {
this.vel[0] = 0;
} else ent.damage();
}
} else if (ent.vel[1] > 0) { //then we get BOPPED.
this.stomp();
} else { //or the player gets hit
ent.damage();
}
} else {
if (this.shell && (ent instanceof Mario.Goomba)) {
ent.bump();
} else this.collideWall();
}
}
}
};
Koopa.prototype.stomp = function() {
//Turn this thing into a shell if it isn't already. Kick it if it is.
player.bounce = true;
if (this.para) {
this.para = false;
this.sprite.pos[0] -= 32;
} else {
sounds.stomp.play();
this.shell = 360;
this.sprite.pos[0] += 64;
this.sprite.pos[1] += 16;
this.sprite.size = [16,16];
this.hitbox = [2,0,12,16];
this.sprite.speed = 0;
this.frames = [0,1];
this.vel = [0,0];
this.pos[1] += 16;
}
};
Koopa.prototype.bump = function() {
sounds.kick.play();
if (this.flipping) return;
this.flipping = true;
this.sprite.pos = [160, 0];
this.sprite.size = [16,16];
this.hitbox = [2, 0, 12, 16];
this.sprite.speed = 0;
this.vel[0] = 0;
this.vel[1] = -2.5;
};
})();

View File

@@ -1,219 +0,0 @@
var oneone = Mario.oneone = function() {
//The things that need to be passed in are basically just dependent on what
//tileset we're in, so it makes more sense to just make one variable for that, so
//TODO: put as much of this in the Level object definition as possible.
level = new Mario.Level({
playerPos: [56,192],
loader: Mario.oneone,
background: "#7974FF",
scrolling: true,
invincibility: [144, 192, 240],
exit: 204,
floorSprite: new Mario.Sprite('sprites/tiles.png', [0,0],[16,16],0),
cloudSprite: new Mario.Sprite('sprites/tiles.png', [0,320],[48,32],0),
wallSprite: new Mario.Sprite('sprites/tiles.png', [0, 16],[16,16],0),
brickSprite: new Mario.Sprite('sprites/tiles.png', [16, 0], [16,16], 0),
brickBounceSprite: new Mario.Sprite('sprites/tiles.png',[32,0],[16,16],0),
rubbleSprite: function () {
return new Mario.Sprite('sprites/items.png', [64,0], [8,8], 3, [0,1])
},
ublockSprite: new Mario.Sprite('sprites/tiles.png', [48, 0], [16,16],0),
superShroomSprite: new Mario.Sprite('sprites/items.png', [0,0], [16,16], 0),
fireFlowerSprite: new Mario.Sprite('sprites/items.png', [0,32], [16,16], 20, [0,1,2,3]),
starSprite: new Mario.Sprite('sprites/items.png', [0,48], [16,16], 20, [0,1,2,3]),
pipeLEndSprite: new Mario.Sprite('sprites/tiles.png', [0, 128], [16,16], 0),
pipeREndSprite: new Mario.Sprite('sprites/tiles.png', [16, 128], [16,16], 0),
pipeLMidSprite: new Mario.Sprite('sprites/tiles.png', [0, 144], [16,16], 0),
pipeRMidSprite: new Mario.Sprite('sprites/tiles.png', [16, 144], [16,16], 0),
pipeUpMid: new Mario.Sprite('sprites/tiles.png', [0, 144], [32,16], 0),
pipeSideMid: new Mario.Sprite('sprites/tiles.png', [48, 128], [16,32], 0),
pipeLeft: new Mario.Sprite('sprites/tiles.png', [32, 128], [16,32], 0),
pipeTop: new Mario.Sprite('sprites/tiles.png', [0, 128], [32,16], 0),
qblockSprite: new Mario.Sprite('sprites/tiles.png', [384, 0], [16,16], 8, [0,0,0,0,1,2,1]),
bcoinSprite: function() {
return new Mario.Sprite('sprites/items.png', [0,112],[16,16], 20,[0,1,2,3]);
},
cloudSprites:[
new Mario.Sprite('sprites/tiles.png', [0,320],[16,32],0),
new Mario.Sprite('sprites/tiles.png', [16,320],[16,32],0),
new Mario.Sprite('sprites/tiles.png', [32,320],[16,32],0)
],
hillSprites: [
new Mario.Sprite('sprites/tiles.png', [128,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [144,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [160,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [128,144],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [144,144],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [160,144],[16,16],0)
],
bushSprite: new Mario.Sprite('sprites/tiles.png', [176, 144], [48, 16], 0),
bushSprites: [
new Mario.Sprite('sprites/tiles.png', [176,144], [16,16],0),
new Mario.Sprite('sprites/tiles.png', [192,144], [16,16],0),
new Mario.Sprite('sprites/tiles.png', [208,144], [16,16],0)],
goombaSprite: function() {
return new Mario.Sprite('sprites/enemy.png', [0, 16], [16,16], 3, [0,1]);
},
koopaSprite: function() {
return new Mario.Sprite('sprites/enemy.png', [96,0], [16,32], 2, [0,1]);
},
flagPoleSprites: [
new Mario.Sprite('sprites/tiles.png', [256, 128], [16,16], 0),
new Mario.Sprite('sprites/tiles.png', [256, 144], [16,16], 0),
new Mario.Sprite('sprites/items.png', [128, 32], [16,16], 0)
]
});
ground = [[0,69],[71,86],[89,153],[155,212]];
player.pos[0] = level.playerPos[0];
player.pos[1] = level.playerPos[1];
vX = 0;
//build THE GROUND
ground.forEach(function(loc) {
level.putFloor(loc[0],loc[1]);
});
//build scenery
clouds = [[7,3],[19, 2],[56, 3],[67, 2],[87, 2],[103, 2],[152, 3],[163, 2],[200, 3]];
clouds.forEach(function(cloud){
level.putCloud(cloud[0],cloud[1]);
});
twoClouds = [[36,2],[132,2],[180,2]];
twoClouds.forEach(function(cloud){
level.putTwoCloud(cloud[0],cloud[1]);
});
threeClouds = [[27,3],[75,3],[123,3],[171,3]];
threeClouds.forEach(function(cloud){
level.putThreeCloud(cloud[0],cloud[1]);
});
bHills = [0,48,96,144,192]
bHills.forEach(function(hill) {
level.putBigHill(hill, 12);
});
sHills = [16,64,111,160];
sHills.forEach(function(hill) {
level.putSmallHill(hill, 12);
});
bushes = [23,71,118,167];
bushes.forEach(function(bush) {
level.putBush(bush, 12);
});
twoBushes = [41,89,137];
twoBushes.forEach(function(bush) {
level.putTwoBush(bush, 12);
});
threeBushes = [11,59,106];
threeBushes.forEach(function(bush) {
level.putThreeBush(bush, 12);
});
//interactable terrain
level.putQBlock(16, 9, new Mario.Bcoin([256, 144]));
level.putBrick(20, 9, null);
level.putQBlock(21, 9, new Mario.Mushroom([336, 144]));
level.putBrick(22, 9, null);
level.putQBlock(22, 5, new Mario.Bcoin([352, 80]));
level.putQBlock(23, 9, new Mario.Bcoin([368, 144]));
level.putBrick(24, 9, null);
level.putPipe(28, 13, 2);
level.putPipe(38, 13, 3);
level.putPipe(46, 13, 4);
level.putRealPipe(57, 9, 4, "DOWN", Mario.oneonetunnel);
level.putBrick(77, 9, null);
level.putQBlock(78, 9, new Mario.Mushroom([1248, 144]));
level.putBrick(79, 9, null);
level.putBrick(80, 5, null);
level.putBrick(81, 5, null);
level.putBrick(82, 5, null);
level.putBrick(83, 5, null);
level.putBrick(84, 5, null);
level.putBrick(85, 5, null);
level.putBrick(86, 5, null);
level.putBrick(87, 5, null);
level.putBrick(91, 5, null);
level.putBrick(92, 5, null);
level.putBrick(93, 5, null);
level.putQBlock(94, 5, new Mario.Bcoin([1504, 80]));
level.putBrick(94, 9, null);
level.putBrick(100, 9, new Mario.Star([1600, 144]));
level.putBrick(101, 9, null);
level.putQBlock(105, 9, new Mario.Bcoin([1680, 144]));
level.putQBlock(108, 9, new Mario.Bcoin([1728, 144]));
level.putQBlock(108, 5, new Mario.Mushroom([1728, 80]));
level.putQBlock(111, 9, new Mario.Bcoin([1776, 144]));
level.putBrick(117, 9, null);
level.putBrick(120, 5, null);
level.putBrick(121, 5, null);
level.putBrick(122, 5, null);
level.putBrick(123, 5, null);
level.putBrick(128, 5, null);
level.putQBlock(129, 5, new Mario.Bcoin([2074, 80]));
level.putBrick(129, 9, null);
level.putQBlock(130, 5, new Mario.Bcoin([2080, 80]));
level.putBrick(130, 9, null);
level.putBrick(131, 5, null);
level.putWall(134, 13, 1);
level.putWall(135, 13, 2);
level.putWall(136, 13, 3);
level.putWall(137, 13, 4);
level.putWall(140, 13, 4);
level.putWall(141, 13, 3);
level.putWall(142, 13, 2);
level.putWall(143, 13, 1);
level.putWall(148, 13, 1);
level.putWall(149, 13, 2);
level.putWall(150, 13, 3);
level.putWall(151, 13, 4);
level.putWall(152, 13, 4);
level.putWall(155, 13, 4);
level.putWall(156, 13, 3);
level.putWall(157, 13, 2);
level.putWall(158, 13, 1);
level.putPipe(163, 13, 2);
level.putBrick(168, 9, null);
level.putBrick(169, 9, null);
level.putQBlock(170, 9, new Mario.Bcoin([2720, 144]));
level.putBrick(171, 9, null);
level.putPipe(179, 13, 2);
level.putWall(181, 13, 1);
level.putWall(182, 13, 2);
level.putWall(183, 13, 3);
level.putWall(184, 13, 4);
level.putWall(185, 13, 5);
level.putWall(186, 13, 6);
level.putWall(187, 13, 7);
level.putWall(188, 13, 8);
level.putWall(189, 13, 8);
level.putFlagpole(198);
//and enemies
level.putGoomba(22, 12);
level.putGoomba(40, 12);
level.putGoomba(50, 12);
level.putGoomba(51, 12);
level.putGoomba(82, 4);
level.putGoomba(84, 4);
level.putGoomba(100, 12);
level.putGoomba(102, 12);
level.putGoomba(114, 12);
level.putGoomba(115, 12);
level.putGoomba(122, 12);
level.putGoomba(123, 12);
level.putGoomba(125, 12);
level.putGoomba(126, 12);
level.putGoomba(170, 12);
level.putGoomba(172, 12);
level.putKoopa(35, 11);
music.underground.pause();
// music.overworld.currentTime = 0;
music.overworld.play();
};

View File

@@ -1,65 +0,0 @@
var oneonetunnel = Mario.oneonetunnel = function() {
level = new Mario.Level({
playerPos: [40,16],
loader: Mario.oneonetunnel,
background: "#000000",
scrolling: false,
coinSprite: function() {
return new Mario.Sprite('sprites/items.png', [0,96],[16,16], 6,[0,0,0,0,1,2,1]);
},
floorSprite: new Mario.Sprite('sprites/tiles.png', [0,32],[16,16],0),
wallSprite: new Mario.Sprite('sprites/tiles.png', [32, 32],[16,16],0),
brickSprite: new Mario.Sprite('sprites/tiles.png', [16, 0], [16,16], 0),
brickBounceSprite: new Mario.Sprite('sprites/tiles.png',[32,0],[16,16],0),
ublockSprite: new Mario.Sprite('sprites/tiles.png', [48, 0], [16,16],0),
pipeLMidSprite: new Mario.Sprite('sprites/tiles.png', [0, 144], [16,16], 0),
pipeRMidSprite: new Mario.Sprite('sprites/tiles.png', [16, 144], [16,16], 0),
pipeLEndSprite: new Mario.Sprite('sprites/tiles.png', [0, 128], [16,16], 0),
pipeREndSprite: new Mario.Sprite('sprites/tiles.png', [16, 128], [16,16], 0),
pipeUpMid: new Mario.Sprite('sprites/tiles.png', [0, 144], [32,16], 0),
pipeSideMid: new Mario.Sprite('sprites/tiles.png', [48, 128], [16,32], 0),
pipeLeft: new Mario.Sprite('sprites/tiles.png', [32, 128], [16,32], 0),
pipeTop: new Mario.Sprite('sprites/tiles.png', [0, 128], [32,16], 0),
LPipeSprites:[
new Mario.Sprite('sprites/tiles.png', [32,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [32,144],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [48,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [48,144],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [64,128],[16,16],0),
new Mario.Sprite('sprites/tiles.png', [64,144],[16,16],0),
]
});
player.pos[0] = level.playerPos[0];
player.pos[1] = level.playerPos[1];
vX = 0;
level.putFloor(0,16);
level.putWall(0,13,11);
walls = [4,5,6,7,8,9,10];
walls.forEach(function(loc){
level.putWall(loc,13,3);
level.putWall(loc,3,1);
});
coins = [[5,5], [6,5], [7,5], [8,5], [9,5],
[4,7], [5,7], [6,7], [7,7], [8,7], [9,7], [10,7],
[4,9], [5,9], [6,9], [7,9], [8,9], [9,9], [10,9]];
coins.forEach(function(pos){
level.putCoin(pos[0],pos[1]);
});
//level.putLeftPipe(13,11);
level.putRealPipe(13,11,3,"RIGHT", function() {
Mario.oneone.call();
player.pos = [2616, 177]
player.pipe("UP", function() {;});
});
level.putPipe(15,13,13);
music.overworld.pause();
music.underground.currentTime = 0;
music.underground.play();
};

View File

@@ -1,218 +0,0 @@
(function() {
var Level = Mario.Level = function(options) {
this.playerPos = options.playerPos;
this.scrolling = options.scrolling;
this.loader = options.loader;
this.background = options.background;
this.exit = options.exit;
this.floorSprite = options.floorSprite;
this.cloudSprite = options.cloudSprite;
this.wallSprite = options.wallSprite;
this.brickSprite = options.brickSprite;
this.rubbleSprite = options.rubbleSprite;
this.brickBounceSprite = options.brickBounceSprite;
this.ublockSprite = options.ublockSprite;
this.superShroomSprite = options.superShroomSprite;
this.fireFlowerSprite = options.fireFlowerSprite;
this.starSprite = options.starSprite;
this.coinSprite = options.coinSprite;
this.bcoinSprite = options.bcoinSprite;
this.goombaSprite = options.goombaSprite;
this.koopaSprite = options.koopaSprite;
//prop pipe sprites, to be phased out
this.pipeLEndSprite = options.pipeLEndSprite;
this.pipeREndSprite = options.pipeREndSprite;
this.pipeLMidSprite = options.pipeLMidSprite;
this.pipeRMidSprite = options.pipeRMidSprite;
//real pipe sprites, use these.
this.pipeUpMid = options.pipeUpMid;
this.pipeSideMid = options.pipeSideMid;
this.pipeLeft = options.pipeLeft;
this.pipeTop = options.pipeTop;
this.flagpoleSprites = options.flagPoleSprites;
this.LPipeSprites = options.LPipeSprites;
this.cloudSprites = options.cloudSprites;
this.hillSprites = options.hillSprites;
this.bushSprite = options.bushSprite;
this.bushSprites = options.bushSprites;
this.qblockSprite = options.qblockSprite;
this.invincibility = options.invincibility;
this.statics = [];
this.scenery = [];
this.blocks = [];
this.enemies = [];
this.items = [];
this.pipes = [];
for (var i = 0; i < 15; i++) {
this.statics[i] = [];
this.scenery[i] = [];
this.blocks[i] = [];
}
};
Level.prototype.putFloor = function(start, end) {
for (var i = start; i < end; i++) {
this.statics[13][i] = new Mario.Floor([16*i,208], this.floorSprite);
this.statics[14][i] = new Mario.Floor([16*i,224], this.floorSprite);
}
};
Level.prototype.putGoomba = function(x, y) {
this.enemies.push(new Mario.Goomba([16*x, 16*y], this.goombaSprite() ));
};
Level.prototype.putKoopa = function(x, y) {
this.enemies.push(new Mario.Koopa([16*x, 16*y], this.koopaSprite(), false));
};
Level.prototype.putWall = function(x, y, height) {
//y is the bottom of the wall in this case.
for (var i = y-height; i < y; i++) {
this.statics[i][x] = new Mario.Floor([16*x, 16*i], this.wallSprite);
}
};
Level.prototype.putPipe = function(x, y, height) {
for (var i = y - height; i < y; i++) {
if (i === y - height) {
this.statics[i][x] = new Mario.Floor([16*x, 16*i], this.pipeLEndSprite);
this.statics[i][x+1] = new Mario.Floor([16*x+16, 16*i], this.pipeREndSprite);
} else {
this.statics[i][x] = new Mario.Floor([16*x, 16*i], this.pipeLMidSprite);
this.statics[i][x+1] = new Mario.Floor([16*x+16, 16*i], this.pipeRMidSprite);
}
}
};
//sometimes, pipes don't go straight up and down.
Level.prototype.putLeftPipe = function(x,y) {
this.statics[y][x] = new Mario.Floor([16*x, 16*y], this.LPipeSprites[0]);
this.statics[y+1][x] = new Mario.Floor([16*x,16*(y+1)], this.LPipeSprites[1]);
this.statics[y][x+1] = new Mario.Floor([16*(x+1),16*y], this.LPipeSprites[2]);
this.statics[y+1][x+1] = new Mario.Floor([16*(x+1),16*(y+1)], this.LPipeSprites[3]);
this.statics[y][x+2] = new Mario.Floor([16*(x+2),16*y], this.LPipeSprites[4]);
this.statics[y+1][x+2] = new Mario.Floor([16*(x+2),16*(y+1)], this.LPipeSprites[5]);
};
Level.prototype.putCoin = function(x, y) {
this.items.push(new Mario.Coin(
[x*16, y*16],
this.coinSprite()
));
};
Level.prototype.putCloud = function(x, y) {
this.scenery[y][x] = new Mario.Prop([x*16, y*16], this.cloudSprite);
};
Level.prototype.putQBlock = function(x, y, item) {
this.blocks[y][x] = new Mario.Block( {
pos: [x*16, y*16],
item: item,
sprite: this.qblockSprite,
usedSprite: this.ublockSprite
});
};
Level.prototype.putBrick = function(x,y,item) {
this.blocks[y][x] = new Mario.Block({
pos: [x*16, y*16],
item: item,
sprite: this.brickSprite,
bounceSprite: this.brickBounceSprite,
usedSprite: this.ublockSprite,
breakable: !item
});
};
Level.prototype.putBigHill = function(x, y) {
var px = x*16, py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.hillSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.hillSprites[3]);
this.scenery[y-1][x+1] = new Mario.Prop([px+16, py-16], this.hillSprites[0]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.hillSprites[4]);
this.scenery[y-1][x+2] = new Mario.Prop([px+32, py-16], this.hillSprites[3]);
this.scenery[y-2][x+2] = new Mario.Prop([px+32, py-32], this.hillSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.hillSprites[5]);
this.scenery[y-1][x+3] = new Mario.Prop([px+48, py-16], this.hillSprites[2]);
this.scenery[y][x+4] = new Mario.Prop([px+64, py], this.hillSprites[2]);
};
Level.prototype.putBush = function(x, y) {
this.scenery[y][x] = new Mario.Prop([x*16, y*16], this.bushSprite);
};
Level.prototype.putThreeBush = function(x,y) {
px = x*16;
py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.bushSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.bushSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.bushSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.bushSprites[1]);
this.scenery[y][x+4] = new Mario.Prop([px+64, py], this.bushSprites[2]);
};
Level.prototype.putTwoBush = function(x,y) {
px = x*16;
py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.bushSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.bushSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.bushSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.bushSprites[2]);
};
Level.prototype.putSmallHill = function(x, y) {
var px = x*16, py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.hillSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.hillSprites[3]);
this.scenery[y-1][x+1] = new Mario.Prop([px+16, py-16], this.hillSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.hillSprites[2]);
};
Level.prototype.putTwoCloud = function(x,y) {
px = x*16;
py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.cloudSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.cloudSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.cloudSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.cloudSprites[2]);
};
Level.prototype.putThreeCloud = function(x,y) {
px = x*16;
py = y*16;
this.scenery[y][x] = new Mario.Prop([px, py], this.cloudSprites[0]);
this.scenery[y][x+1] = new Mario.Prop([px+16, py], this.cloudSprites[1]);
this.scenery[y][x+2] = new Mario.Prop([px+32, py], this.cloudSprites[1]);
this.scenery[y][x+3] = new Mario.Prop([px+48, py], this.cloudSprites[1]);
this.scenery[y][x+4] = new Mario.Prop([px+64, py], this.cloudSprites[2]);
};
Level.prototype.putRealPipe = function(x, y, length, direction, destination) {
px = x*16;
py = y*16;
this.pipes.push(new Mario.Pipe({
pos: [px, py],
length: length,
direction: direction,
destination: destination
}));
}
Level.prototype.putFlagpole = function(x) {
this.statics[12][x] = new Mario.Floor([16*x, 192], this.wallSprite);
for (i=3; i < 12; i++) {
this.scenery[i][x] = new Mario.Prop([16*x, 16*i], this.flagpoleSprites[1])
}
this.scenery[2][x] = new Mario.Prop([16*x, 32], this.flagpoleSprites[0]);
this.items.push(new Mario.Flag(16*x));
}
})();

View File

@@ -1,118 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Mushroom = Mario.Mushroom = function(pos) {
this.spawning = false;
this.waiting = 0;
Mario.Entity.call(this, {
pos: pos,
sprite: level.superShroomSprite,
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Mushroom, Mario.Entity);
Mushroom.prototype.render = function(ctx, vX, vY) {
if (this.spawning > 1) return;
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
Mushroom.prototype.spawn = function() {
if (player.power > 0) {
//replace this with a fire flower
var ff = new Mario.Fireflower(this.pos)
ff.spawn();
return;
}
sounds.itemAppear.play();
this.idx = level.items.length;
level.items.push(this);
this.spawning = 12;
this.targetpos = [];
this.targetpos[0] = this.pos[0];
this.targetpos[1] = this.pos[1] - 16;
}
Mushroom.prototype.update = function(dt) {
if (this.spawning > 1) {
this.spawning -= 1;
if (this.spawning == 1) this.vel[1] = -.5;
return;
}
if (this.spawning) {
if (this.pos[1] <= this.targetpos[1]) {
this.pos[1] = this.targetpos[1];
this.vel[1] = 0;
this.waiting = 5;
this.spawning = 0;
this.vel[0] = 1;
}
} else {
this.acc[1] = 0.2;
}
if (this.waiting) {
this.waiting -= 1;
} else {
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
}
}
Mushroom.prototype.collideWall = function() {
this.vel[0] = -this.vel[0];
}
Mushroom.prototype.checkCollisions = function() {
if(this.spawning) {
return;
}
var h = this.pos[1] % 16 == 0 ? 1 : 2;
var w = this.pos[0] % 16 == 0 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete level.items[this.idx];
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
this.isPlayerCollided();
}
//we have access to player everywhere, so let's just do this.
Mushroom.prototype.isPlayerCollided = function() {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [player.pos[0] + player.hitbox[0], player.pos[1] + player.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+player.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+player.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
player.powerUp(this.idx);
}
}
}
Mushroom.prototype.bump = function() {
this.vel[1] = -2;
}
})();

View File

@@ -1,45 +0,0 @@
Enemy:
#collideWith
checks collision. Bounces off of walls and other enemies. Kills marios.
gets killed by block bonk from below
Specific enemy behaviors:
koopas get replaced with shells
shells get kicked if touched from the side, instead of killing
Mario:
#collideWith
gets expelled from walls, bonks blocks, destroys blocks as big Mario
note: collision with blocks is determined by which one is over the center
if you're slightly to the side, you slip past it.
Crazy-ass sprite shifting if you're in star mode!
Item
#collideWith
item pickup logic
Mushroom
#update
movement logic for mushrooms
get impulse from being block bonked
this can be implemented by giving them the appropriate accel
just if they overlap a block from the bottom.
of course, only AFTER they finish sliding out of the block.
Water levels:
make a separate class for water Marios
less gravity
swimming sprite
fireballs are the same
jump works when not on the ground
different enemies
TODO: Make level loader use hashes instead of arrays where possible.
Should be a free performance gain.
Should also make item deletion less weird.
TODO: Make gravity exist higher up instead of having a magic number.
Note from the future: Nope. Different objects have different gravity.

View File

@@ -1,160 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//there are too many possible configurations of pipe to capture in a reasonable
//set of simple variables. Joints, etc. are just too much.
//To that end, the pipe class handles simple pipes, and we'll put together
//anything more complex with individual props. OK? OK.
Pipe = Mario.Pipe = function(options) {
this.pos = options.pos
//NOTE: direction is the direction you move INTO the pipe.
this.direction = options.direction
this.destination = options.destination
this.length = options.length;
if (this.direction === "UP" || this.direction === "DOWN") {
this.hitbox = [0,0, 32, this.length * 16];
this.midsection = level.pipeUpMid;
this.endsection = level.pipeTop;
} else {
this.hitbox = [0,0, 16*this.length, 32];
this.midsection = level.pipeSideMid;
this.endsection = level.pipeLeft;
}
}
Pipe.prototype.checkPipe = function() {
if (this.destination === undefined || !input.isDown(this.direction)) return;
var h = player.power===0 ? 16 : 32;
var x = Math.floor(player.pos[0]);
var y = Math.floor(player.pos[1]);
switch (this.direction) {
case 'RIGHT': if (x === this.pos[0]-16 &&
y >= this.pos[1] &&
y+h <= this.pos[1]+32) {
player.pipe(this.direction, this.destination)
}
break;
case 'LEFT': if (x === this.pos[0]+16*this.length &&
y >= this.pos[1] &&
y+h <= this.pos[1]+32) {
player.pipe(this.direction, this.destination)
}
break;
case 'UP': if (y === this.pos[1] + 16*this.length &&
x >= this.pos[0] &&
x+16 <= this.pos[0]+32) {
player.pipe(this.direction, this.destination)
}
break;
case 'DOWN': if (y+h === this.pos[1] &&
x >= this.pos[0] &&
x+16 <= this.pos[0]+32) {
player.pipe(this.direction, this.destination);
}
break;
}
}
//Note to self: next time, decide on a convention for which thing checks for collisions
//and stick to it. This is a pain.
Pipe.prototype.checkCollisions = function() {
var that = this;
level.enemies.forEach (function(ent) {
that.isCollideWith(ent);
});
level.items.forEach (function(ent) {
that.isCollideWith(ent);
});
fireballs.forEach(function(ent){
that.isCollideWith(ent)
});
if (!player.piping) this.isCollideWith(player);
}
Pipe.prototype.isCollideWith = function (ent) {
//long story short: because we scan every item, and and one 'rubble' item is four things with separate positions
//we'll crash without this line as soon as we destroy a block. OOPS.
if (ent.pos === undefined) return;
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [Math.floor(this.pos[0] + this.hitbox[0]), Math.floor(this.pos[1] + this.hitbox[1])];
var hpos2 = [Math.floor(ent.pos[0] + ent.hitbox[0]), Math.floor(ent.pos[1] + ent.hitbox[1])];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+ent.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+ent.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
//if the entity is over the block, it's basically floor
var center = hpos2[0] + ent.hitbox[2] / 2;
if (Math.abs(hpos2[1] + ent.hitbox[3] - hpos1[1]) <= ent.vel[1]) {
ent.vel[1] = 0;
ent.pos[1] = hpos1[1] - ent.hitbox[3] - ent.hitbox[1];
ent.standing = true;
if (ent instanceof Mario.Player) {
ent.jumping = 0;
}
} else if (Math.abs(hpos2[1] - hpos1[1] - this.hitbox[3]) > ent.vel[1] &&
center + 2 >= hpos1[0] && center - 2 <= hpos1[0] + this.hitbox[2]) {
//ent is under the block.
ent.vel[1] = 0;
ent.pos[1] = hpos1[1] + this.hitbox[3];
if (ent instanceof Mario.Player) {
ent.jumping = 0;
}
} else {
//entity is hitting it from the side, we're a wall
ent.collideWall(this);
}
}
}
}
//we COULD try to write some shenanigans so that the check gets put into the
//collision code, but there won't ever be more than a handful of pipes in a level
//so the performance hit of scanning all of them is miniscule.
Pipe.prototype.update = function(dt) {
if (this.destination) this.checkPipe();
}
//http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array
//I honestly have no idea if javascript does this, but I feel like it makes sense
//stylistically to prefer branching outside of loops when possible as convention
//TODO: edit the spritesheet so UP and LEFT pipes aren't backwards.
Pipe.prototype.render = function(ctx, vX, vY) {
switch (this.direction) {
case "DOWN":
this.endsection.render(ctx, this.pos[0], this.pos[1], vX, vY);
for (var i = 1; i < this.length; i++) {
this.midsection.render(ctx, this.pos[0], this.pos[1]+i*16, vX, vY)
}
break;
case "UP":
this.endsection.render(ctx, this.pos[0], this.pos[1]+16*(this.length-1), vX, vY)
for (var i=0; i < this.length - 1; i++) {
this.midsection.render(ctx, this.pos[0], this.pos[1]+i*16, vX, vY)
}
break;
case "RIGHT":
this.endsection.render(ctx, this.pos[0], this.pos[1], vX, vY)
for (var i = 1; i < this.length; i++) {
this.midsection.render(ctx, this.pos[0]+16*i, this.pos[1], vX, vY)
}
break;
case "LEFT":
this.endsection.render(ctx, this.pos[0]+16*(this.length-1), this.pos[1], vX, vY)
for (var i = 0; i < this.legth-1; i++) {
this.midsection.render(ctx, this.pos[0], this.pos[1]+i*16, vX, vY)
}
break;
}
}
})();

View File

@@ -1,446 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Player = Mario.Player = function(pos) {
//I know, I know, there are a lot of variables tracking Mario's state.
//Maybe these can be consolidated some way? We'll see once they're all in.
this.power = 0;
this.coins = 0;
this.powering = [];
this.bounce = false;
this.jumping = 0;
this.canJump = true;
this.invincibility = 0;
this.crouching = false;
this.fireballs = 0;
this.runheld = false;
this.noInput = false;
this.targetPos = [];
Mario.Entity.call(this, {
pos: pos,
sprite: new Mario.Sprite('sprites/player.png', [80,32],[16,16],0),
hitbox: [0,0,16,16]
});
};
Mario.Util.inherits(Player, Mario.Entity);
Player.prototype.run = function() {
this.maxSpeed = 2.5;
if (this.power == 2 && !this.runheld) {
this.shoot();
}
this.runheld = true;
}
Player.prototype.shoot = function() {
if (this.fireballs >= 2) return; //Projectile limit!
this.fireballs += 1;
var fb = new Mario.Fireball([this.pos[0]+8,this.pos[1]]); //I hate you, Javascript.
fb.spawn(this.left);
this.shooting = 2;
}
Player.prototype.noRun = function() {
this.maxSpeed = 1.5;
this.moveAcc = 0.07;
this.runheld = false;
}
Player.prototype.moveRight = function() {
//we're on the ground
if (this.vel[1] === 0 && this.standing) {
if (this.crouching) {
this.noWalk();
return;
}
this.acc[0] = this.moveAcc;
this.left = false;
} else {
this.acc[0] = this.moveAcc;
}
};
Player.prototype.moveLeft = function() {
if (this.vel[1] === 0 && this.standing) {
if (this.crouching) {
this.noWalk();
return;
}
this.acc[0] = -this.moveAcc;
this.left = true;
} else {
this.acc[0] = -this.moveAcc;
}
};
Player.prototype.noWalk = function() {
this.maxSpeed = 0;
if (this.vel[0] === 0) return;
if (Math.abs(this.vel[0]) <= 0.1) {
this.vel[0] = 0;
this.acc[0] = 0;
}
};
Player.prototype.crouch = function() {
if (this.power === 0) {
this.crouching = false;
return;
}
if (this.standing) this.crouching = true;
}
Player.prototype.noCrouch = function() {
this.crouching = false;
}
Player.prototype.jump = function() {
if (this.vel[1] > 0) {
return;
}
if (this.jumping) {
this.jumping -= 1;
} else if (this.standing && this.canJump) {
this.jumping = 20;
this.canJump = false;
this.standing = false;
this.vel[1] = -6;
if (this.power === 0) {
sounds.smallJump.currentTime = 0;
sounds.smallJump.play();
} else {
sounds.bigJump.currentTime = 0;
sounds.bigJump.play();
}
}
};
Player.prototype.noJump = function() {
this.canJump = true;
if (this.jumping) {
if (this.jumping <= 16) {
this.vel[1] = 0;
this.jumping = 0;
} else this.jumping -= 1;
}
};
Player.prototype.setAnimation = function() {
if (this.dying) return;
if (this.starTime) {
var index;
if (this.starTime > 60)
index = Math.floor(this.starTime / 2) % 3;
else index = Math.floor(this.starTime / 8) % 3;
this.sprite.pos[1] = level.invincibility[index];
if (this.power == 0) {
this.sprite.pos[1] += 32;
}
this.starTime -= 1;
if (this.starTime == 0) {
switch(this.power) {
case 0: this.sprite.pos[1] = 32; break;
case 1: this.sprite.pos[1] = 0; break;
case 2: this.sprite.pos[1] = 96; break;
}
}
}
//okay cool, now set the sprite
if (this.crouching) {
this.sprite.pos[0] = 176;
this.sprite.speed = 0;
return;
}
if (this.jumping) {
this.sprite.pos[0] = 160;
this.sprite.speed = 0;
} else if (this.standing) {
if (Math.abs(this.vel[0]) > 0) {
if (this.vel[0] * this.acc[0] >= 0) {
this.sprite.pos[0] = 96;
this.sprite.frames = [0,1,2];
if (this.vel[0] < 0.2) {
this.sprite.speed = 5;
} else {
this.sprite.speed = Math.abs(this.vel[0]) * 8;
}
} else if ((this.vel[0] > 0 && this.left) || (this.vel[0] < 0 && !this.left)){
this.sprite.pos[0] = 144;
this.sprite.speed = 0;
}
} else {
this.sprite.pos[0] = 80;
this.sprite.speed = 0;
}
if (this.shooting) {
this.sprite.pos[0] += 160;
this.shooting -= 1;
}
}
if (this.flagging) {
this.sprite.pos[0] = 192;
this.sprite.frames = [0,1];
this.sprite.speed = 10;
if (this.vel[1] === 0) this.sprite.frames = [0];
}
//which way are we facing?
if (this.left) {
this.sprite.img = 'sprites/playerl.png';
} else {
this.sprite.img = 'sprites/player.png';
}
};
Player.prototype.update = function(dt, vX) {
if (this.powering.length !== 0) {
var next = this.powering.shift();
if (next == 5) return;
this.sprite.pos = this.powerSprites[next];
this.sprite.size = this.powerSizes[next];
this.pos[1] += this.shift[next];
if (this.powering.length === 0) {
delete level.items[this.touchedItem];
}
return;
}
if (this.invincibility) {
this.invincibility -= Math.round(dt * 60);
}
if (this.waiting) {
this.waiting -= dt;
if (this.waiting <= 0) {
this.waiting = 0;
} else return;
}
if (this.bounce) {
this.bounce = false;
this.standing = false;
this.vel[1] = -3;
}
if (this.pos[0] <= vX) {
this.pos[0] = vX;
this.vel[0] = Math.max(this.vel[0], 0);
}
if (Math.abs(this.vel[0]) > this.maxSpeed) {
this.vel[0] -= 0.05 * this.vel[0] / Math.abs(this.vel[0]);
this.acc[0] = 0;
}
if (this.dying){
if (this.pos[1] < this.targetPos[1]) {
this.vel[1] = 1;
}
this.dying -= 1 * dt;
if (this.dying <= 0) {
player = new Mario.Player(level.playerPos);
level.loader.call();
input.reset();
}
}
else {
this.acc[1] = 0.25
if (this.pos[1] > 240) {
this.die();
}
}
if (this.piping) {
this.acc = [0,0];
var pos = [Math.round(this.pos[0]), Math.round(this.pos[1])]
if (pos[0] === this.targetPos[0] && pos[1] === this.targetPos[1]) {
this.piping = false;
this.pipeLoc.call();
}
}
if (this.flagging) {
this.acc = [0,0];
}
if (this.exiting) {
this.left = false;
this.flagging = false;
this.vel[0] = 1.5;
if (this.pos[0] >= this.targetPos[0]) {
this.sprite.size = [0,0];
this.vel = [0,0];
window.setTimeout(function() {
player.sprite.size = player.power===0 ? [16,16] : [16,32];
player.exiting = false;
player.noInput = false;
level.loader();
if (player.power !== 0) player.pos[1] -= 16;
music.overworld.currentTime = 0;
}, 5000);
}
}
//approximate acceleration
this.vel[0] += this.acc[0];
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.setAnimation();
this.sprite.update(dt);
};
Player.prototype.checkCollisions = function() {
if (this.piping || this.dying) return;
//x-axis first!
var h = this.power > 0 ? 2 : 1;
var w = 1;
if (this.pos[1] % 16 !== 0) {
h += 1;
}
if (this.pos[0] % 16 !== 0) {
w += 1;
}
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
for (var i = 0; i < h; i++) {
if (baseY + i < 0 || baseY + i >= 15) continue;
for (var j = 0; j < w; j++) {
if (baseY < 0) { i++;}
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
};
Player.prototype.powerUp = function(idx) {
sounds.powerup.play();
this.powering = [0,5,2,5,1,5,2,5,1,5,2,5,3,5,1,5,2,5,3,5,1,5,4];
this.touchedItem = idx;
if (this.power === 0) {
this.sprite.pos[0] = 80;
var newy = this.sprite.pos[1] - 32;
this.powerSprites = [[80, newy+32], [80, newy+32], [320, newy], [80, newy], [128, newy]];
this.powerSizes = [[16,16],[16,16],[16,32],[16,32],[16,32]];
this.shift = [0,16,-16,0,-16];
this.power = 1;
this.hitbox = [0,0,16,32];
} else if (this.power == 1) {
var curx = this.sprite.pos[0];
this.powerSprites = [[curx, 96], [curx, level.invincibility[0]],
[curx, level.invincibility[1]], [curx, level.invincibility[2]],
[curx, 96]];
this.powerSizes[[16,32],[16,32],[16,32],[16,32],[16,32]];
this.shift = [0,0,0,0,0];
this.power = 2;
} else {
this.powering = [];
delete level.items[idx];
//no animation, but we play the sound and you get 5000 points.
}
};
Player.prototype.damage = function() {
if (this.power === 0) { //if you're already small, you dead!
this.die();
} else { //otherwise, you get turned into small mario
sounds.pipe.play();
this.powering = [0,5,1,5,2,5,1,5,2,5,1,5,2,5,1,5,2,5,1,5,2,5,3];
this.shift = [0,16,-16,16];
this.sprite.pos = [160, 0];
this.powerSprites = [[160,0], [240, 32], [240, 0], [160, 32]];
this.powerSizes = [[16, 32], [16,16], [16,32], [16,16]];
this.invincibility = 120;
this.power = 0;
this.hitbox = [0,0,16,16];
}
};
Player.prototype.die = function () {
//TODO: rewrite the way sounds work to emulate the channels of an NES.
music.overworld.pause();
music.underground.pause();
music.overworld.currentTime = 0;
music.death.play();
this.noWalk();
this.noRun();
this.noJump();
this.acc[0] = 0;
this.sprite.pos = [176, 32];
this.sprite.speed = 0;
this.power = 0;
this.waiting = 0.5;
this.dying = 2;
if (this.pos[1] < 240) { //falling into a pit doesn't do the animation.
this.targetPos = [this.pos[0], this.pos[1]-128];
this.vel = [0,-5];
} else {
this.vel = [0,0];
this.targetPos = [this.pos[0], this.pos[1] - 16];
}
};
Player.prototype.star = function(idx) {
delete level.items[idx];
this.starTime = 660;
}
Player.prototype.pipe = function(direction, destination) {
sounds.pipe.play();
this.piping = true;
this.pipeLoc = destination;
switch(direction) {
case "LEFT":
this.vel = [-1,0];
this.targetPos = [Math.round(this.pos[0]-16), Math.round(this.pos[1])]
break;
case "RIGHT":
this.vel = [1,0];
this.targetPos = [Math.round(this.pos[0]+16), Math.round(this.pos[1])]
break;
case "DOWN":
this.vel = [0,1];
this.targetPos = [Math.round(this.pos[0]), Math.round(this.pos[1]+this.hitbox[3])]
break;
case "UP":
this.vel = [0,-1];
this.targetPos = [Math.round(this.pos[0]), Math.round(this.pos[1]-this.hitbox[3])]
break;
}
}
Player.prototype.flag = function() {
this.noInput = true;
this.flagging = true;
this.vel = [0, 2];
this.acc = [0, 0];
}
Player.prototype.exit = function() {
this.pos[0] += 16;
this.targetPos[0] = level.exit * 16;
this.left = true;
this.setAnimation();
this.waiting = 1;
this.exiting = true;
}
})();

View File

@@ -1,15 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//props do even less than entities, so they don't need to inherit really
var Prop = Mario.Prop = function(pos, sprite) {
this.pos = pos;
this.sprite = sprite;
}
//but we will be using the same Render, more or less.
Prop.prototype.render = function(ctx, vX, vY) {
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
})();

View File

@@ -1,62 +0,0 @@
//simple resource loader
(function() {
var resourceCache = {};
var loading = [];
var readyCallbacks = [];
// Load an image url or an array of image urls
function load(urlOrArr) {
if(urlOrArr instanceof Array) {
urlOrArr.forEach(function(url) {
_load(url);
});
}
else {
_load(urlOrArr);
}
}
function _load(url) {
if(resourceCache[url]) {
return resourceCache[url];
}
else {
var img = new Image();
img.onload = function() {
resourceCache[url] = img;
if(isReady()) {
readyCallbacks.forEach(function(func) { func(); });
}
};
resourceCache[url] = false;
img.src = url;
}
}
function get(url) {
return resourceCache[url];
}
function isReady() {
var ready = true;
for(var k in resourceCache) {
if(resourceCache.hasOwnProperty(k) &&
!resourceCache[k]) {
ready = false;
}
}
return ready;
}
function onReady(func) {
readyCallbacks.push(func);
}
window.resources = {
load: load,
get: get,
onReady: onReady,
isReady: isReady
};
})();

View File

@@ -1,55 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
//TODO: make each rubble an entity, use that render and write in Entity.update
var Rubble = Mario.Rubble = function() {
this.sprites = [];
this.poss = [];
this.vels = [];
}
Rubble.prototype.spawn = function(pos) {
this.idx = level.items.length;
level.items.push(this);
this.sprites[0] = level.rubbleSprite();
this.sprites[1] = level.rubbleSprite();
this.sprites[2] = level.rubbleSprite();
this.sprites[3] = level.rubbleSprite();
this.poss[0] = pos;
this.poss[1] = [ pos[0] + 8, pos[1] ];
this.poss[2] = [ pos[0], pos[1] + 8 ];
this.poss[3] = [ pos[0] + 8, pos[1] + 8 ];
this.vels[0] = [-1.25, -5];
this.vels[1] = [1.25, -5];
this.vels[2] = [-1.25, -3];
this.vels[3] = [1.25, -3];
}
Rubble.prototype.update = function(dt) {
for(var i = 0; i < 4; i++) {
if (this.sprites[i]===undefined) continue;
this.vels[i][1] += .3;
this.poss[i][0] += this.vels[i][0];
this.poss[i][1] += this.vels[i][1];
this.sprites[i].update(dt);
if (this.poss[i][1] > 256) {
delete this.sprites[i];
}
}
if (this.sprites.every(function (el) {return !el})) {
delete level.items[this.idx];
}
}
//You might argue that things that can't collide are more like scenery
//but these move and need to be deleted, and i'd rather deal with the 1d array.
Rubble.prototype.checkCollisions = function() {;}
Rubble.prototype.render = function() {
for(var i = 0; i < 4; i++) {
if (this.sprites[i] === undefined) continue;
this.sprites[i].render(ctx, this.poss[i][0], this.poss[i][1], vX, vY);
}
}
})();

View File

@@ -1,47 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Sprite = Mario.Sprite = function(img, pos, size, speed, frames, once) {
this.pos = pos;
this.size = size;
this.speed = speed;
this._index = 0;
this.img = img;
this.once = once;
this.frames = frames;
}
Sprite.prototype.update = function(dt, gameTime) {
if (gameTime && gameTime == this.lastUpdated) return;
this._index += this.speed*dt;
if (gameTime) this.lastUpdated = gameTime;
}
Sprite.prototype.setFrame = function(frame) {
this._index = frame;
}
Sprite.prototype.render = function(ctx, posx, posy, vX, vY) {
var frame;
if (this.speed > 0) {
var max = this.frames.length;
var idx = Math.floor(this._index);
frame = this.frames[idx % max];
if (this.once && idx >= max) {
this.done = true;
return;
}
} else {
frame = 0;
}
var x = this.pos[0];
var y = this.pos[1];
x += frame*this.size[0];
ctx.drawImage(resources.get(this.img), x + (1/3),y + (1/3), this.size[0] - (2/3), this.size[1] - (2/3), Math.round(posx - vX), Math.round(posy - vY), this.size[0],this.size[1]);
}
})();

View File

@@ -1,116 +0,0 @@
(function() {
if (typeof Mario === 'undefined')
window.Mario = {};
var Star = Mario.Star = function(pos) {
this.spawning = false;
this.waiting = 0;
Mario.Entity.call(this, {
pos: pos,
sprite: level.starSprite,
hitbox: [0,0,16,16]
});
}
Mario.Util.inherits(Star, Mario.Entity);
Star.prototype.render = function(ctx, vX, vY) {
if (this.spawning > 1) return;
this.sprite.render(ctx, this.pos[0], this.pos[1], vX, vY);
}
Star.prototype.spawn = function() {
this.idx = level.items.length;
level.items.push(this);
this.spawning = 12;
this.targetpos = [];
this.targetpos[0] = this.pos[0];
this.targetpos[1] = this.pos[1] - 16;
}
Star.prototype.update = function(dt) {
if (this.spawning > 1) {
this.spawning -= 1;
if (this.spawning == 1) this.vel[1] = -.5;
return;
}
if (this.spawning) {
if (this.pos[1] <= this.targetpos[1]) {
this.pos[1] = this.targetpos[1];
this.vel[1] = 0;
this.waiting = 5;
this.spawning = 0;
this.vel[0] = 1;
}
} else {
this.acc[1] = 0.2;
}
if (this.standing) {
this.standing = false;
this.vel[1] = -3;
}
if (this.waiting) {
this.waiting -= 1;
} else {
this.vel[1] += this.acc[1];
this.pos[0] += this.vel[0];
this.pos[1] += this.vel[1];
this.sprite.update(dt);
}
}
Star.prototype.collideWall = function() {
this.vel[0] = -this.vel[0];
}
Star.prototype.checkCollisions = function() {
if(this.spawning) {
return;
}
var h = this.pos[1] % 16 == 0 ? 1 : 2;
var w = this.pos[0] % 16 == 0 ? 1 : 2;
var baseX = Math.floor(this.pos[0] / 16);
var baseY = Math.floor(this.pos[1] / 16);
if (baseY + h > 15) {
delete level.items[this.idx];
return;
}
for (var i = 0; i < h; i++) {
for (var j = 0; j < w; j++) {
if (level.statics[baseY + i][baseX + j]) {
level.statics[baseY + i][baseX + j].isCollideWith(this);
}
if (level.blocks[baseY + i][baseX + j]) {
level.blocks[baseY + i][baseX + j].isCollideWith(this);
}
}
}
this.isPlayerCollided();
}
//we have access to player everywhere, so let's just do this.
Star.prototype.isPlayerCollided = function() {
//the first two elements of the hitbox array are an offset, so let's do this now.
var hpos1 = [this.pos[0] + this.hitbox[0], this.pos[1] + this.hitbox[1]];
var hpos2 = [player.pos[0] + player.hitbox[0], player.pos[1] + player.hitbox[1]];
//if the hitboxes actually overlap
if (!(hpos1[0] > hpos2[0]+player.hitbox[2] || (hpos1[0]+this.hitbox[2] < hpos2[0]))) {
if (!(hpos1[1] > hpos2[1]+player.hitbox[3] || (hpos1[1]+this.hitbox[3] < hpos2[1]))) {
player.star(this.idx);
}
}
}
Star.prototype.bump = function() {
this.vel[1] = -2;
}
})();

View File

@@ -1,14 +0,0 @@
(function() {
if (typeof Mario === 'undefined') {
window.Mario = {};
}
var Util = Mario.Util = {};
Util.inherits = function(subclass, superclass) {
function Surrogate() {};
Surrogate.prototype = superclass.prototype;
subclass.prototype = new Surrogate();
}
})()

View File

@@ -1,144 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="../favicon.png" />
<link rel="stylesheet" href="../https://pyscript.net/alpha/pyscript.css" />
<script defer src="../https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<script type="text/javascript" src="js/util.js"></script>
<script type="text/javascript" src="js/input.js"></script>
<script type="text/javascript" src="js/resources.js"></script>
<script type="text/javascript" src="js/sprite.js"></script>
<script type="text/javascript" src="js/entity.js"></script>
<script type="text/javascript" src="js/pipe.js"></script>
<script type="text/javascript" src="js/mushroom.js"></script>
<script type="text/javascript" src="js/fireflower.js"></script>
<script type="text/javascript" src="js/star.js"></script>
<script type="text/javascript" src="js/fireball.js"></script>
<script type="text/javascript" src="js/coin.js"></script>
<script type="text/javascript" src="js/bcoin.js"></script>
<script type="text/javascript" src="js/goomba.js"></script>
<script type="text/javascript" src="js/koopa.js"></script>
<script type="text/javascript" src="js/floor.js"></script>
<script type="text/javascript" src="js/block.js"></script>
<script type="text/javascript" src="js/rubble.js"></script>
<script type="text/javascript" src="js/prop.js"></script>
<script type="text/javascript" src="js/player.js"></script>
<script type="text/javascript" src="js/flag.js"></script>
<script type="text/javascript" src="js/levels/level.js"></script>
<script type="text/javascript" src="js/levels/11.js"></script>
<script type="text/javascript" src="js/levels/11tunnel.js"></script>
<script type="text/javascript" src="js/game.js"></script>
<py-script>
from js import handTrack, setTimeout, requestAnimationFrame, player
from pyodide import create_once_callable
import asyncio
context = canvas.element.getContext("2d")
isVideo = False
model = None
last_position = 0
direction = "stop"
modelParams = {
"flipHorizontal": True, # flip e.g for video
"maxNumBoxes": 20, # maximum number of boxes to detect
"iouThreshold": 0.5, # ioU threshold for non-max suppression
"scoreThreshold": 0.6, # confidence threshold for predictions.
}
def toggle_video(evt):
global isVideo
player.jump()
if (not isVideo):
update_note.write("Starting video")
pyscript.run_until_complete(start_video())
else:
update_note.write("Stopping video")
handTrack.stopVideo(video.element)
isVideo = False
update_note.write("Video stopped")
async def start_video():
global isVideo
update_note.write("Inside sstart video")
status = await handTrack.startVideo(video.element)
console.log("video started", status)
if status:
update_note.write("Video started. Now tracking")
isVideo = True
console.log( "Calling RUN DETECTION")
y = await run_detection()
else:
update_note.write( "Please enable video")
def sync_run_detection(evt):
pyscript.run_until_complete(run_detection())
async def run_detection():
global model
global isVideo
global last_position
global direction
predictions = await model.detect(video.element)
model.renderPredictions(predictions, canvas.element, context, video.element);
if predictions:
curr_position = predictions[0].bbox[0] + (predictions[0].bbox[2] / 2)
delta = last_position - curr_position
last_position = curr_position
#console.log(delta, curr_position, last_position)
if abs(delta) < 2:
direction = "stop"
elif delta > 0:
direction = "left"
else:
direction = "right"
for prediction in predictions:
if prediction.label == 'open':
player.jump()
elif prediction.label == 'close':
player.crouch()
if (isVideo):
await requestAnimationFrame(create_once_callable(sync_run_detection));
def handle_model(lmodel):
global model
model = lmodel
update_note.write("Loaded Model!")
async def start():
model = await handTrack.load(modelParams)#.then(handle_model)
handle_model(model)
pyscript.run_until_complete(start())
</py-script>
<div class="mb10">
<p>Use < > to move, ↓ to crouch and x to jump. If video is enabled, say hi to jump as well! </p>
<button id="trackbutton" class="bx--btn bx--btn--secondary" type="button" pys-onClick="toggle_video">
Start Video
</button>
<div id="update-note" py-mount class="updatenote mt10">loading model ..</div>
</div>
<div>
<video autoplay="autoplay" id="myvideo" py-mount="video"></video>
<canvas id="canvas" py-mount class="border canvasbox"></canvas>
</div>
<script src="../handtrack/lib/handtrack.min.js"> </script>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -1,50 +0,0 @@
<html>
<head>
<title>Matplotlib</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-env>
- matplotlib
</py-env>
</head>
<body>
<div id="mpl"></div>
<py-script output="mpl">
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import numpy as np
# First create the x and y coordinates of the points.
n_angles = 36
n_radii = 8
min_radius = 0.25
radii = np.linspace(min_radius, 0.95, n_radii)
angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
angles[:, 1::2] += np.pi / n_angles
x = (radii * np.cos(angles)).flatten()
y = (radii * np.sin(angles)).flatten()
z = (np.cos(radii) * np.cos(3 * angles)).flatten()
# Create the Triangulation; no triangles so Delaunay triangulation created.
triang = tri.Triangulation(x, y)
# Mask off unwanted triangles.
triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
y[triang.triangles].mean(axis=1))
< min_radius)
fig1, ax1 = plt.subplots()
ax1.set_aspect('equal')
tpc = ax1.tripcolor(triang, z, shading='flat')
fig1.colorbar(tpc)
ax1.set_title('tripcolor of Delaunay triangulation, flat shading')
fig1
</py-script>
</body>
</html>

View File

@@ -1,53 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<py-env>
- numpy
- networkx
- matplotlib
</py-env>
<py-script>
import numpy as np
import networkx as nx
</py-script>
<p>Message passing with linear algebra: a demo.</p>
<p>Imagine we have a chain graph that looks like this:</p>
<pre><code>O --> 1 --> 2 --> 3</code></pre>
<p>In NetworkX this graph would look like the following:</p>
<pre>
<py-script>
G = nx.Graph()
nodes = list(range(4))
G.add_edges_from(zip(nodes[0:-1], nodes[1:]))
print(G.edges())
</py-script></pre>
<p>This chain graph has the following adjacency matrix:</p>
<pre>
<py-script>
adj_mat = np.eye(4, k=1)
print(f"A: {adj_mat}")
</py-script>
</pre>
<p>And imagine that we have a message that lives on the graph:</p>
<pre>
<py-script>
message = np.array([1.0, 0.0, 0.0, 0.0])
print(f"message: {message}")
</py-script>
</pre>
<p>Try out message passing below by doing any one of the following steps:</p>
<pre><code>message @ adj_mat</code></pre>
<pre><code>message @ adj_mat @ adj_mat</code></pre>
<pre><code>message @ adj_mat @ adj_mat @ adj_mat</code></pre>
<div>
<py-repl id="my-repl" auto-generate="true"> </py-repl>
</div>
</body>
</html>

View File

@@ -1,192 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="./favicon.png">
<title>micrograd</title>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-env>
- micrograd
- numpy
- matplotlib
</py-env>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
</head>
<body style="padding-top: 20px; padding-right: 20px; padding-bottom: 20px; padding-left: 20px">
<h1>Micrograd - A tiny Autograd engine (with a bite! :))</h1><br>
<div>
<p>
<a href="https://github.com/karpathy/micrograd">Micrograd</a> is a tiny Autograd engine created
by <a href="https://twitter.com/karpathy">Andrej Karpathy</a>. This app recreates the
<a href="https://github.com/karpathy/micrograd/blob/master/demo.ipynb">demo</a>
he prepared for this package using pyscript to train a basic model, written in Python, natively in
the browser. <br>
</p>
</div>
<div>
<p>
You may run each Python REPL cell interactively by pressing (Shift + Enter) or (Ctrl + Enter).
You can also modify the code directly as you wish. If you want to run all the code at once,
not each cell individually, you may instead click the 'Run All' button. Training the model
takes between 1-2 min if you decide to 'Run All' at once. 'Run All' is your only option if
you are running this on a mobile device where you cannot press (Shift + Enter). After the
model is trained, a plot image should be displayed depicting the model's ability to
classify the data. <br>
</p>
<p>
Currently the <code>&gt;</code> symbol is being imported incorrectly as <code>&ampgt;</code> into the REPL's.
In this app the <code>&gt;</code> symbol has been replaced with <code>().__gt__()</code> so you can run the code
without issue. Ex: instead of <code>a &gt; b</code>, you will see <code>(a).__gt__(b)</code> instead. <br>
</p>
<p>
<py-script>import js; js.document.getElementById('python-status').innerHTML = 'Python is now ready. You may proceed.'</py-script>
<div id="python-status">Python is currently starting. Please wait...</div>
</p>
<p>
<button id="run-all-button" class="btn btn-primary" type="submit" pys-onClick="run_all_micrograd_demo">Run All</button><br>
<py-script src="/micrograd_ai.py"></py-script>
<div id="micrograd-run-all-print-div"></div><br>
<div id="micrograd-run-all-fig1-div"></div>
<div id="micrograd-run-all-fig2-div"></div><br>
</p>
</div>
<py-repl auto-generate="false">
import random
import numpy as np
import matplotlib.pyplot as plt
</py-repl><br>
<py-repl auto-generate="false">
from micrograd.engine import Value
from micrograd.nn import Neuron, Layer, MLP
</py-repl><br>
<py-repl auto-generate="true">
np.random.seed(1337)
random.seed(1337)
</py-repl><br>
<py-repl auto-generate="true">
#An adaptation of sklearn's make_moons function https://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_moons.html
def make_moons(n_samples=100, noise=None):
n_samples_out, n_samples_in = n_samples, n_samples
outer_circ_x = np.cos(np.linspace(0, np.pi, n_samples_out))
outer_circ_y = np.sin(np.linspace(0, np.pi, n_samples_out))
inner_circ_x = 1 - np.cos(np.linspace(0, np.pi, n_samples_in))
inner_circ_y = 1 - np.sin(np.linspace(0, np.pi, n_samples_in)) - 0.5
X = np.vstack([np.append(outer_circ_x, inner_circ_x), np.append(outer_circ_y, inner_circ_y)]).T
y = np.hstack([np.zeros(n_samples_out, dtype=np.intp), np.ones(n_samples_in, dtype=np.intp)])
if noise is not None: X += np.random.normal(loc=0.0, scale=noise, size=X.shape)
return X, y
X, y = make_moons(n_samples=100, noise=0.1)
</py-repl><br>
<py-repl auto-generate="true">
y = y*2 - 1 # make y be -1 or 1
# visualize in 2D
plt.figure(figsize=(5,5))
plt.scatter(X[:,0], X[:,1], c=y, s=20, cmap='jet')
plt
</py-repl><br>
<py-repl auto-generate="true">
model = MLP(2, [16, 16, 1]) # 2-layer neural network
print(model)
print("number of parameters", len(model.parameters()))
</py-repl><br>
<div>
Line 24 has been changed from: <br>
<code>accuracy = [(yi &gt; 0) == (scorei.data &gt; 0) for yi, scorei in zip(yb, scores)]</code><br>
to: <br>
<code>accuracy = [((yi).__gt__(0)) == ((scorei.data).__gt__(0)) for yi, scorei in zip(yb, scores)]</code><br>
</div>
<py-repl auto-generate="true">
# loss function
def loss(batch_size=None):
# inline DataLoader :)
if batch_size is None:
Xb, yb = X, y
else:
ri = np.random.permutation(X.shape[0])[:batch_size]
Xb, yb = X[ri], y[ri]
inputs = [list(map(Value, xrow)) for xrow in Xb]
# forward the model to get scores
scores = list(map(model, inputs))
# svm "max-margin" loss
losses = [(1 + -yi*scorei).relu() for yi, scorei in zip(yb, scores)]
data_loss = sum(losses) * (1.0 / len(losses))
# L2 regularization
alpha = 1e-4
reg_loss = alpha * sum((p*p for p in model.parameters()))
total_loss = data_loss + reg_loss
# also get accuracy
accuracy = [((yi).__gt__(0)) == ((scorei.data).__gt__(0)) for yi, scorei in zip(yb, scores)]
return total_loss, sum(accuracy) / len(accuracy)
total_loss, acc = loss()
print(total_loss, acc)
</py-repl><br>
<py-repl auto-generate="true">
# optimization
for k in range(20): #was 100. Accuracy can be further improved w/ more epochs (to 100%).
# forward
total_loss, acc = loss()
# backward
model.zero_grad()
total_loss.backward()
# update (sgd)
learning_rate = 1.0 - 0.9*k/100
for p in model.parameters():
p.data -= learning_rate * p.grad
if k % 1 == 0:
print(f"step {k} loss {total_loss.data}, accuracy {acc*100}%")
</py-repl><br>
<div>
<p>
Please wait for the training loop above to complete. It will not print out stats until it
has completely finished. This typically takes 1-2 min. <br><br>
Line 9 has been changed from: <br>
<code>Z = np.array([s.data &gt; 0 for s in scores])</code><br>
to: <br>
<code>Z = np.array([(s.data).__gt__(0) for s in scores])</code><br>
</p>
</div>
<py-repl auto-generate="true">
h = 0.25
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
Xmesh = np.c_[xx.ravel(), yy.ravel()]
inputs = [list(map(Value, xrow)) for xrow in Xmesh]
scores = list(map(model, inputs))
Z = np.array([(s.data).__gt__(0) for s in scores])
Z = Z.reshape(xx.shape)
fig = plt.figure()
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt
</py-repl><br>
<py-repl auto-generate="true">
1+1
</py-repl><br>
</body>
</html>
<!-- Adapted by Mat Miller -->

View File

@@ -1,160 +0,0 @@
# Credit: https://github.com/karpathy/micrograd/blob/master/demo.ipynb
# cell
import datetime
import random
import matplotlib.pyplot as plt
import numpy as np
# cell
from micrograd.engine import Value
from micrograd.nn import MLP
print_statements = []
def run_all_micrograd_demo(*args, **kwargs):
result = micrograd_demo()
pyscript.write("micrograd-run-all-fig2-div", result)
def print_div(o):
o = str(o)
print_statements.append(o + " \n<br>")
pyscript.write("micrograd-run-all-print-div", "".join(print_statements))
# All code is wrapped in this run_all function so it optionally executed (called)
# from pyscript when a button is pressed.
def micrograd_demo(*args, **kwargs):
"""
Runs the micrograd demo.
*args and **kwargs do nothing and are only there to capture any parameters passed
from pyscript when this function is called when a button is clicked.
"""
# cell
start = datetime.datetime.now()
print_div("Starting...")
# cell
np.random.seed(1337)
random.seed(1337)
# cell
# An adaptation of sklearn's make_moons function
# https://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_moons.html
def make_moons(n_samples=100, noise=None):
n_samples_out, n_samples_in = n_samples, n_samples
outer_circ_x = np.cos(np.linspace(0, np.pi, n_samples_out))
outer_circ_y = np.sin(np.linspace(0, np.pi, n_samples_out))
inner_circ_x = 1 - np.cos(np.linspace(0, np.pi, n_samples_in))
inner_circ_y = 1 - np.sin(np.linspace(0, np.pi, n_samples_in)) - 0.5
X = np.vstack(
[
np.append(outer_circ_x, inner_circ_x),
np.append(outer_circ_y, inner_circ_y),
]
).T
y = np.hstack(
[
np.zeros(n_samples_out, dtype=np.intp),
np.ones(n_samples_in, dtype=np.intp),
]
)
if noise is not None:
X += np.random.normal(loc=0.0, scale=noise, size=X.shape)
return X, y
X, y = make_moons(n_samples=100, noise=0.1)
# cell
y = y * 2 - 1 # make y be -1 or 1
# visualize in 2D
plt.figure(figsize=(5, 5))
plt.scatter(X[:, 0], X[:, 1], c=y, s=20, cmap="jet")
plt
pyscript.write("micrograd-run-all-fig1-div", plt)
# cell
model = MLP(2, [16, 16, 1]) # 2-layer neural network
print_div(model)
print_div(("number of parameters", len(model.parameters())))
# cell
# loss function
def loss(batch_size=None):
# inline DataLoader :)
if batch_size is None:
Xb, yb = X, y
else:
ri = np.random.permutation(X.shape[0])[:batch_size]
Xb, yb = X[ri], y[ri]
inputs = [list(map(Value, xrow)) for xrow in Xb]
# forward the model to get scores
scores = list(map(model, inputs))
# svm "max-margin" loss
losses = [(1 + -yi * scorei).relu() for yi, scorei in zip(yb, scores)]
data_loss = sum(losses) * (1.0 / len(losses))
# L2 regularization
alpha = 1e-4
reg_loss = alpha * sum(p * p for p in model.parameters())
total_loss = data_loss + reg_loss
# also get accuracy
accuracy = [
((yi).__gt__(0)) == ((scorei.data).__gt__(0))
for yi, scorei in zip(yb, scores)
]
return total_loss, sum(accuracy) / len(accuracy)
total_loss, acc = loss()
print((total_loss, acc))
# cell
# optimization
for k in range(20): # was 100
# forward
total_loss, _ = loss()
# backward
model.zero_grad()
total_loss.backward()
# update (sgd)
learning_rate = 1.0 - 0.9 * k / 100
for p in model.parameters():
p.data -= learning_rate * p.grad
if k % 1 == 0:
# print(f"step {k} loss {total_loss.data}, accuracy {acc*100}%")
print_div(f"step {k} loss {total_loss.data}, accuracy {acc*100}%")
# cell
h = 0.25
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Xmesh = np.c_[xx.ravel(), yy.ravel()]
inputs = [list(map(Value, xrow)) for xrow in Xmesh]
scores = list(map(model, inputs))
Z = np.array([(s.data).__gt__(0) for s in scores])
Z = Z.reshape(xx.shape)
_ = plt.figure()
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
finish = datetime.datetime.now()
print_div(f"It took {(finish-start).seconds} seconds to run this code.")
plt
return plt

View File

@@ -1,318 +0,0 @@
<html>
<head>
<title>Visualization of Mandelbrot, Julia and Newton sets with NumPy and HTML5 canvas</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<style>
.loading {
display: inline-block;
width: 50px;
height: 50px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: black;
animation: spin 1s ease-in-out infinite;
}
canvas {
display: none;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<b>
</b>
<div style="display: flex; flex-direction: column; gap: 1em; width: 600px">
<div id="mandelbrot">
<div style="text-align: center">Mandelbrot set</div>
<div>
<div class="loading"></div>
<canvas></canvas>
</div>
</div>
<div id="julia">
<div style="text-align: center">Julia set</div>
<div>
<div class="loading"></div>
<canvas></canvas>
</div>
</div>
<div id="newton">
<div style="text-align: center">Newton set</div>
<fieldset style="display: flex; flex-direction: row; gap: 1em">
<div><span style="white-space: pre">p(z) = </span><input id="poly" type="text" value="z**3 - 2*z + 2"></div>
<div><span style="white-space: pre">a = </span><input id="coef" type="text" value="1" style="width: 40px"></div>
<div style="display: flex; flex-direction: row">
<span style="white-space: pre">x = [</span>
<input id="x0" type="text" value="-2.5" style="width: 80px; text-align: right">
<span style="white-space: pre">, </span>
<input id="x1" type="text" value="2.5" style="width: 80px; text-align: right">
<span style="white-space: pre">]</span>
</div>
<div style="display: flex; flex-direction: row">
<span style="white-space: pre">y = [</span>
<input id="y0" type="text" value="-5.0" style="width: 80px; text-align: right">
<span style="white-space: pre">, </span>
<input id="y1" type="text" value="5.0" style="width: 80px; text-align: right">
<span style="white-space: pre">]</span>
</div>
<div style="display: flex; flex-direction: row; gap: 1em">
<div style="white-space: pre"><input type="radio" id="conv" name="type" value="convergence" checked> convergence</div>
<div style="white-space: pre"><input type="radio" id="iter" name="type" value="iterations"> iterations</div>
</div>
</fieldset>
<div>
<div class="loading"></div>
<canvas></canvas>
</div>
</div>
</div>
<py-env>
- numpy
- sympy
- paths:
- ./palettes.py
- ./fractals.py
</py-env>
<py-script>
from pyodide import to_js, create_proxy
import numpy as np
import sympy
from palettes import Magma256
from fractals import mandelbrot, julia, newton
from js import (
console,
document,
devicePixelRatio,
ImageData,
Uint8ClampedArray,
CanvasRenderingContext2D as Context2d,
requestAnimationFrame,
)
def prepare_canvas(width: int, height: int, canvas: Element) -> Context2d:
ctx = canvas.getContext("2d")
canvas.style.width = f"{width}px"
canvas.style.height = f"{height}px"
canvas.width = width
canvas.height = height
ctx.clearRect(0, 0, width, height)
return ctx
def color_map(array: np.array, palette: np.array) -> np.array:
size, _ = palette.shape
index = (array/array.max()*(size - 1)).round().astype("uint8")
width, height = array.shape
image = np.full((width, height, 4), 0xff, dtype=np.uint8)
image[:, :, :3] = palette[index]
return image
def draw_image(ctx: Context2d, image: np.array) -> None:
data = Uint8ClampedArray.new(to_js(image.tobytes()))
width, height, _ = image.shape
image_data = ImageData.new(data, width, height)
ctx.putImageData(image_data, 0, 0)
width, height = 600, 600
async def draw_mandelbrot() -> None:
spinner = document.querySelector("#mandelbrot .loading")
canvas = document.querySelector("#mandelbrot canvas")
spinner.style.display = ""
canvas.style.display = "none"
ctx = prepare_canvas(width, height, canvas)
console.log("Computing Mandelbrot set ...")
console.time("mandelbrot")
iters = mandelbrot(width, height)
console.timeEnd("mandelbrot")
image = color_map(iters, Magma256)
draw_image(ctx, image)
spinner.style.display = "none"
canvas.style.display = "block"
async def draw_julia() -> None:
spinner = document.querySelector("#julia .loading")
canvas = document.querySelector("#julia canvas")
spinner.style.display = ""
canvas.style.display = "none"
ctx = prepare_canvas(width, height, canvas)
console.log("Computing Julia set ...")
console.time("julia")
iters = julia(width, height)
console.timeEnd("julia")
image = color_map(iters, Magma256)
draw_image(ctx, image)
spinner.style.display = "none"
canvas.style.display = "block"
def ranges():
x0_in = document.querySelector("#x0")
x1_in = document.querySelector("#x1")
y0_in = document.querySelector("#y0")
y1_in = document.querySelector("#y1")
xr = (float(x0_in.value), float(x1_in.value))
yr = (float(y0_in.value), float(y1_in.value))
return xr, yr
current_image = None
async def draw_newton() -> None:
spinner = document.querySelector("#newton .loading")
canvas = document.querySelector("#newton canvas")
spinner.style.display = ""
canvas.style.display = "none"
ctx = prepare_canvas(width, height, canvas)
console.log("Computing Newton set ...")
poly_in = document.querySelector("#poly")
coef_in = document.querySelector("#coef")
conv_in = document.querySelector("#conv")
iter_in = document.querySelector("#iter")
xr, yr = ranges()
# z**3 - 1
# z**8 + 15*z**4 - 16
# z**3 - 2*z + 2
expr = sympy.parse_expr(poly_in.value)
coeffs = [ complex(c) for c in reversed(sympy.Poly(expr, sympy.Symbol("z")).all_coeffs()) ]
poly = np.polynomial.Polynomial(coeffs)
coef = complex(sympy.parse_expr(coef_in.value))
console.time("newton")
iters, roots = newton(width, height, p=poly, a=coef, xr=xr, yr=yr)
console.timeEnd("newton")
if conv_in.checked:
n = poly.degree() + 1
k = int(len(Magma256)/n)
colors = Magma256[::k, :][:n]
colors[0, :] = [255, 0, 0] # red: no convergence
image = color_map(roots, colors)
else:
image = color_map(iters, Magma256)
global current_image
current_image = image
draw_image(ctx, image)
spinner.style.display = "none"
canvas.style.display = "block"
handler = create_proxy(lambda _event: draw_newton())
document.querySelector("#newton fieldset").addEventListener("change", handler)
canvas = document.querySelector("#newton canvas")
is_selecting = False
init_sx, init_sy = None, None
sx, sy = None, None
async def mousemove(event):
global is_selecting
global init_sx
global init_sy
global sx
global sy
def invert(sx, source_range, target_range):
source_start, source_end = source_range
target_start, target_end = target_range
factor = (target_end - target_start)/(source_end - source_start)
offset = -(factor * source_start) + target_start
return (sx - offset) / factor
bds = canvas.getBoundingClientRect()
event_sx, event_sy = event.clientX - bds.x, event.clientY - bds.y
ctx = canvas.getContext("2d")
pressed = event.buttons == 1
if is_selecting:
if not pressed:
xr, yr = ranges()
x0 = invert(init_sx, xr, (0, width))
x1 = invert(sx, xr, (0, width))
y0 = invert(init_sy, yr, (0, height))
y1 = invert(sy, yr, (0, height))
document.querySelector("#x0").value = x0
document.querySelector("#x1").value = x1
document.querySelector("#y0").value = y0
document.querySelector("#y1").value = y1
is_selecting = False
init_sx, init_sy = None, None
sx, sy = init_sx, init_sy
await draw_newton()
else:
ctx.save()
ctx.clearRect(0, 0, width, height)
draw_image(ctx, current_image)
sx, sy = event_sx, event_sy
ctx.beginPath()
ctx.rect(init_sx, init_sy, sx - init_sx, sy - init_sy)
ctx.fillStyle = "rgba(255, 255, 255, 0.4)"
ctx.strokeStyle = "rgba(255, 255, 255, 1.0)"
ctx.fill()
ctx.stroke()
ctx.restore()
else:
if pressed:
is_selecting = True
init_sx, init_sy = event_sx, event_sy
sx, sy = init_sx, init_sy
canvas.addEventListener("mousemove", create_proxy(mousemove))
import asyncio
_ = await asyncio.gather(
draw_mandelbrot(),
draw_julia(),
draw_newton(),
)
</py-script>
</body>
</html>

View File

@@ -1,263 +0,0 @@
import numpy as np
Magma256 = np.array(
[
[0x00, 0x00, 0x03],
[0x00, 0x00, 0x04],
[0x00, 0x00, 0x06],
[0x01, 0x00, 0x07],
[0x01, 0x01, 0x09],
[0x01, 0x01, 0x0B],
[0x02, 0x02, 0x0D],
[0x02, 0x02, 0x0F],
[0x03, 0x03, 0x11],
[0x04, 0x03, 0x13],
[0x04, 0x04, 0x15],
[0x05, 0x04, 0x17],
[0x06, 0x05, 0x19],
[0x07, 0x05, 0x1B],
[0x08, 0x06, 0x1D],
[0x09, 0x07, 0x1F],
[0x0A, 0x07, 0x22],
[0x0B, 0x08, 0x24],
[0x0C, 0x09, 0x26],
[0x0D, 0x0A, 0x28],
[0x0E, 0x0A, 0x2A],
[0x0F, 0x0B, 0x2C],
[0x10, 0x0C, 0x2F],
[0x11, 0x0C, 0x31],
[0x12, 0x0D, 0x33],
[0x14, 0x0D, 0x35],
[0x15, 0x0E, 0x38],
[0x16, 0x0E, 0x3A],
[0x17, 0x0F, 0x3C],
[0x18, 0x0F, 0x3F],
[0x1A, 0x10, 0x41],
[0x1B, 0x10, 0x44],
[0x1C, 0x10, 0x46],
[0x1E, 0x10, 0x49],
[0x1F, 0x11, 0x4B],
[0x20, 0x11, 0x4D],
[0x22, 0x11, 0x50],
[0x23, 0x11, 0x52],
[0x25, 0x11, 0x55],
[0x26, 0x11, 0x57],
[0x28, 0x11, 0x59],
[0x2A, 0x11, 0x5C],
[0x2B, 0x11, 0x5E],
[0x2D, 0x10, 0x60],
[0x2F, 0x10, 0x62],
[0x30, 0x10, 0x65],
[0x32, 0x10, 0x67],
[0x34, 0x10, 0x68],
[0x35, 0x0F, 0x6A],
[0x37, 0x0F, 0x6C],
[0x39, 0x0F, 0x6E],
[0x3B, 0x0F, 0x6F],
[0x3C, 0x0F, 0x71],
[0x3E, 0x0F, 0x72],
[0x40, 0x0F, 0x73],
[0x42, 0x0F, 0x74],
[0x43, 0x0F, 0x75],
[0x45, 0x0F, 0x76],
[0x47, 0x0F, 0x77],
[0x48, 0x10, 0x78],
[0x4A, 0x10, 0x79],
[0x4B, 0x10, 0x79],
[0x4D, 0x11, 0x7A],
[0x4F, 0x11, 0x7B],
[0x50, 0x12, 0x7B],
[0x52, 0x12, 0x7C],
[0x53, 0x13, 0x7C],
[0x55, 0x13, 0x7D],
[0x57, 0x14, 0x7D],
[0x58, 0x15, 0x7E],
[0x5A, 0x15, 0x7E],
[0x5B, 0x16, 0x7E],
[0x5D, 0x17, 0x7E],
[0x5E, 0x17, 0x7F],
[0x60, 0x18, 0x7F],
[0x61, 0x18, 0x7F],
[0x63, 0x19, 0x7F],
[0x65, 0x1A, 0x80],
[0x66, 0x1A, 0x80],
[0x68, 0x1B, 0x80],
[0x69, 0x1C, 0x80],
[0x6B, 0x1C, 0x80],
[0x6C, 0x1D, 0x80],
[0x6E, 0x1E, 0x81],
[0x6F, 0x1E, 0x81],
[0x71, 0x1F, 0x81],
[0x73, 0x1F, 0x81],
[0x74, 0x20, 0x81],
[0x76, 0x21, 0x81],
[0x77, 0x21, 0x81],
[0x79, 0x22, 0x81],
[0x7A, 0x22, 0x81],
[0x7C, 0x23, 0x81],
[0x7E, 0x24, 0x81],
[0x7F, 0x24, 0x81],
[0x81, 0x25, 0x81],
[0x82, 0x25, 0x81],
[0x84, 0x26, 0x81],
[0x85, 0x26, 0x81],
[0x87, 0x27, 0x81],
[0x89, 0x28, 0x81],
[0x8A, 0x28, 0x81],
[0x8C, 0x29, 0x80],
[0x8D, 0x29, 0x80],
[0x8F, 0x2A, 0x80],
[0x91, 0x2A, 0x80],
[0x92, 0x2B, 0x80],
[0x94, 0x2B, 0x80],
[0x95, 0x2C, 0x80],
[0x97, 0x2C, 0x7F],
[0x99, 0x2D, 0x7F],
[0x9A, 0x2D, 0x7F],
[0x9C, 0x2E, 0x7F],
[0x9E, 0x2E, 0x7E],
[0x9F, 0x2F, 0x7E],
[0xA1, 0x2F, 0x7E],
[0xA3, 0x30, 0x7E],
[0xA4, 0x30, 0x7D],
[0xA6, 0x31, 0x7D],
[0xA7, 0x31, 0x7D],
[0xA9, 0x32, 0x7C],
[0xAB, 0x33, 0x7C],
[0xAC, 0x33, 0x7B],
[0xAE, 0x34, 0x7B],
[0xB0, 0x34, 0x7B],
[0xB1, 0x35, 0x7A],
[0xB3, 0x35, 0x7A],
[0xB5, 0x36, 0x79],
[0xB6, 0x36, 0x79],
[0xB8, 0x37, 0x78],
[0xB9, 0x37, 0x78],
[0xBB, 0x38, 0x77],
[0xBD, 0x39, 0x77],
[0xBE, 0x39, 0x76],
[0xC0, 0x3A, 0x75],
[0xC2, 0x3A, 0x75],
[0xC3, 0x3B, 0x74],
[0xC5, 0x3C, 0x74],
[0xC6, 0x3C, 0x73],
[0xC8, 0x3D, 0x72],
[0xCA, 0x3E, 0x72],
[0xCB, 0x3E, 0x71],
[0xCD, 0x3F, 0x70],
[0xCE, 0x40, 0x70],
[0xD0, 0x41, 0x6F],
[0xD1, 0x42, 0x6E],
[0xD3, 0x42, 0x6D],
[0xD4, 0x43, 0x6D],
[0xD6, 0x44, 0x6C],
[0xD7, 0x45, 0x6B],
[0xD9, 0x46, 0x6A],
[0xDA, 0x47, 0x69],
[0xDC, 0x48, 0x69],
[0xDD, 0x49, 0x68],
[0xDE, 0x4A, 0x67],
[0xE0, 0x4B, 0x66],
[0xE1, 0x4C, 0x66],
[0xE2, 0x4D, 0x65],
[0xE4, 0x4E, 0x64],
[0xE5, 0x50, 0x63],
[0xE6, 0x51, 0x62],
[0xE7, 0x52, 0x62],
[0xE8, 0x54, 0x61],
[0xEA, 0x55, 0x60],
[0xEB, 0x56, 0x60],
[0xEC, 0x58, 0x5F],
[0xED, 0x59, 0x5F],
[0xEE, 0x5B, 0x5E],
[0xEE, 0x5D, 0x5D],
[0xEF, 0x5E, 0x5D],
[0xF0, 0x60, 0x5D],
[0xF1, 0x61, 0x5C],
[0xF2, 0x63, 0x5C],
[0xF3, 0x65, 0x5C],
[0xF3, 0x67, 0x5B],
[0xF4, 0x68, 0x5B],
[0xF5, 0x6A, 0x5B],
[0xF5, 0x6C, 0x5B],
[0xF6, 0x6E, 0x5B],
[0xF6, 0x70, 0x5B],
[0xF7, 0x71, 0x5B],
[0xF7, 0x73, 0x5C],
[0xF8, 0x75, 0x5C],
[0xF8, 0x77, 0x5C],
[0xF9, 0x79, 0x5C],
[0xF9, 0x7B, 0x5D],
[0xF9, 0x7D, 0x5D],
[0xFA, 0x7F, 0x5E],
[0xFA, 0x80, 0x5E],
[0xFA, 0x82, 0x5F],
[0xFB, 0x84, 0x60],
[0xFB, 0x86, 0x60],
[0xFB, 0x88, 0x61],
[0xFB, 0x8A, 0x62],
[0xFC, 0x8C, 0x63],
[0xFC, 0x8E, 0x63],
[0xFC, 0x90, 0x64],
[0xFC, 0x92, 0x65],
[0xFC, 0x93, 0x66],
[0xFD, 0x95, 0x67],
[0xFD, 0x97, 0x68],
[0xFD, 0x99, 0x69],
[0xFD, 0x9B, 0x6A],
[0xFD, 0x9D, 0x6B],
[0xFD, 0x9F, 0x6C],
[0xFD, 0xA1, 0x6E],
[0xFD, 0xA2, 0x6F],
[0xFD, 0xA4, 0x70],
[0xFE, 0xA6, 0x71],
[0xFE, 0xA8, 0x73],
[0xFE, 0xAA, 0x74],
[0xFE, 0xAC, 0x75],
[0xFE, 0xAE, 0x76],
[0xFE, 0xAF, 0x78],
[0xFE, 0xB1, 0x79],
[0xFE, 0xB3, 0x7B],
[0xFE, 0xB5, 0x7C],
[0xFE, 0xB7, 0x7D],
[0xFE, 0xB9, 0x7F],
[0xFE, 0xBB, 0x80],
[0xFE, 0xBC, 0x82],
[0xFE, 0xBE, 0x83],
[0xFE, 0xC0, 0x85],
[0xFE, 0xC2, 0x86],
[0xFE, 0xC4, 0x88],
[0xFE, 0xC6, 0x89],
[0xFE, 0xC7, 0x8B],
[0xFE, 0xC9, 0x8D],
[0xFE, 0xCB, 0x8E],
[0xFD, 0xCD, 0x90],
[0xFD, 0xCF, 0x92],
[0xFD, 0xD1, 0x93],
[0xFD, 0xD2, 0x95],
[0xFD, 0xD4, 0x97],
[0xFD, 0xD6, 0x98],
[0xFD, 0xD8, 0x9A],
[0xFD, 0xDA, 0x9C],
[0xFD, 0xDC, 0x9D],
[0xFD, 0xDD, 0x9F],
[0xFD, 0xDF, 0xA1],
[0xFD, 0xE1, 0xA3],
[0xFC, 0xE3, 0xA5],
[0xFC, 0xE5, 0xA6],
[0xFC, 0xE6, 0xA8],
[0xFC, 0xE8, 0xAA],
[0xFC, 0xEA, 0xAC],
[0xFC, 0xEC, 0xAE],
[0xFC, 0xEE, 0xB0],
[0xFC, 0xF0, 0xB1],
[0xFC, 0xF1, 0xB3],
[0xFC, 0xF3, 0xB5],
[0xFC, 0xF5, 0xB7],
[0xFB, 0xF7, 0xB9],
[0xFB, 0xF9, 0xBB],
[0xFB, 0xFA, 0xBD],
[0xFB, 0xFC, 0xBF],
],
dtype="uint8",
)

View File

@@ -1,32 +0,0 @@
<html>
<head>
<title>Panel Example</title>
<meta charset="iso-8859-1">
<link rel="icon" type="image/x-icon" href="./favicon.png">
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@holoviz/panel@0.13.1/dist/panel.min.js"></script>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<py-env>
- bokeh
- numpy
- panel==0.13.1
</py-env>
<body>
<h1>Panel Example</h1>
<div id="simple_app"></div>
<py-script>
import panel as pn
slider = pn.widgets.FloatSlider(start=0, end=10, name='Amplitude')
def callback(new):
return f'Amplitude is: {new}'
pn.Row(slider, pn.bind(callback, slider)).servable(target='simple_app');
</py-script>
</body>
</html>

View File

@@ -1,222 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="icon" type="image/x-icon" href="./favicon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="theme-color" content="#0072b5">
<meta name="name" content="PyScript/Panel DeckGL Demo">
<title>PyScript/Panel DeckGL Demo</title>
<link rel="icon" type="image/x-icon" href="./favicon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/css/widgets.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/css/markdown.css" type="text/css" />
<script type="text/javascript" src="https://unpkg.com/h3-js@3.7.2/dist/h3-js.umd.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/deck.gl@8.6.7/dist.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@deck.gl/json@8.6.7/dist.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@loaders.gl/csv@3.1.7/dist/dist.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@loaders.gl/json@3.1.7/dist/dist.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@loaders.gl/3d-tiles@3.1.7/dist/dist.min.js"></script>
<script type="text/javascript" src="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@holoviz/panel@0.13.1/dist/panel.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/bundled/bootstraptemplate/bootstrap.css">
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/bundled/defaulttheme/default.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<style>
#sidebar {
width: 400px;
padding: 0;
}
</style>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<py-env>
- bokeh
- numpy
- pandas
- panel==0.13.1
</py-env>
<body>
<div class="container-fluid d-flex flex-column vh-100 overflow-hidden" id="container">
<nav class="navbar navbar-expand-md navbar-dark sticky-top shadow" id="header" style="background-color: #000000;">
<button type="button" class="navbar-toggle collapsed" id="sidebarCollapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="app-header">
<a class="navbar-brand app-logo" href="/">
<img src="./logo.png" class="app-logo">
</a>
<a class="title" href="" style="color: #f0ab3c;">Panel DeckGL NYC Taxi Demo</a>
</div>
</nav>
<div class="d-flex flex-nowrap" id="content">
<div class="sidenav" id="sidebar">
<ul class="nav flex-column" >
<div class="bk-root" id="widgets"></div>
<py-repl id="my-repl" auto-generate="true"> </py-repl>
</ul>
</div>
<div class="col mh-100" style="padding: 0">
<div class="bk-root" id="plot"></div>
</div>
</div>
</div>
<py-script>
import panel as pn
import pandas as pd
import param
from pyodide.http import open_url
MAPBOX_KEY = "pk.eyJ1IjoicGFuZWxvcmciLCJhIjoiY2s1enA3ejhyMWhmZjNobjM1NXhtbWRrMyJ9.B_frQsAVepGIe-HiOJeqvQ"
class App(param.Parameterized):
data = param.DataFrame(precedence=-1)
view = param.DataFrame(precedence=-1)
arc_view = param.DataFrame(precedence=-1)
radius = param.Integer(default=50, bounds=(20, 1000))
elevation = param.Integer(default=10, bounds=(0, 50))
hour = param.Integer(default=0, bounds=(0, 23))
speed = param.Integer(default=1, bounds=(0, 10), precedence=-1)
play = param.Event(label='▷')
def __init__(self, **params):
self.deck_gl = None
super().__init__(**params)
self.deck_gl = pn.pane.DeckGL(
dict(self.spec),
mapbox_api_key=MAPBOX_KEY,
throttle={'click': 10},
sizing_mode='stretch_both',
margin=0
)
self.deck_gl.param.watch(self._update_arc_view, 'click_state')
self._playing = False
self._cb = pn.state.add_periodic_callback(
self._update_hour, 1000//self.speed, start=False
)
@property
def spec(self):
return {
"initialViewState": {
"bearing": 0,
"latitude": 40.7,
"longitude": -73.9,
"maxZoom": 15,
"minZoom": 5,
"pitch": 40.5,
"zoom": 11
},
"layers": [self.hex_layer, self.arc_layer],
"mapStyle": "mapbox://styles/mapbox/dark-v9",
"views": [
{"@@type": "MapView", "controller": True}
]
}
@property
def hex_layer(self):
return {
"@@type": "HexagonLayer",
"autoHighlight": True,
"coverage": 1,
"data": self.data if self.view is None else self.view,
"elevationRange": [0, 100],
"elevationScale": self.elevation,
"radius": self.radius,
"extruded": True,
"getPosition": "@@=[pickup_x, pickup_y]",
"id": "8a553b25-ef3a-489c-bbe2-e102d18a3211"
}
@property
def arc_layer(self):
return {
"@@type": "ArcLayer",
"id": 'arc-layer',
"data": self.arc_view,
"pickable": True,
"getWidth": 1,
"getSourcePosition": "@@=[pickup_x, pickup_y]",
"getTargetPosition": "@@=[dropoff_x, dropoff_y]",
"getSourceColor": [0, 255, 0, 180],
"getTargetColor": [240, 100, 0, 180]
}
def _update_hour(self):
self.hour = (self.hour+1) % 24
@param.depends('view', watch=True)
def _update_arc_view(self, event=None):
data = self.data if self.view is None else self.view
if not self.deck_gl or not self.deck_gl.click_state:
self.arc_view = data.iloc[:0]
return
lon, lat = self.deck_gl.click_state['coordinate']
tol = 0.001
self.arc_view = data[
(df.pickup_x>=float(lon-tol)) &
(df.pickup_x<=float(lon+tol)) &
(df.pickup_y>=float(lat-tol)) &
(df.pickup_y<=float(lat+tol))
]
@param.depends('hour', watch=True, on_init=True)
def _update_hourly_view(self):
self.view = self.data[self.data.hour==self.hour]
@param.depends('speed', watch=True)
def _update_speed(self):
self._cb.period = 1000//self.speed
@param.depends('play', watch=True)
def _play_pause(self):
if self._playing:
self._cb.stop()
self.param.play.label = '▷'
self.param.speed.precedence = -1
else:
self._cb.start()
self.param.play.label = '❚❚'
self.param.speed.precedence = 1
self._playing = not self._playing
@param.depends('view', 'radius', 'elevation', 'arc_view', watch=True)
def update_spec(self):
self.deck_gl.object = dict(self.spec)
url = 'https://s3.eu-west-1.amazonaws.com/assets.holoviews.org/data/nyc_taxi_wide.csv'
df = pd.read_csv(open_url(url))
app = App(data=df)
controls = pn.Param(app.param, sizing_mode='stretch_width', show_name=False)
app.deck_gl.servable(target='plot')
controls.servable(target='widgets');
</py-script>
</body>
</html>

View File

@@ -1,173 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="icon" type="image/x-icon" href="./favicon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="theme-color" content="#000000">
<meta name="name" content="PyScript/Panel KMeans Demo">
<title>Pyscript/Panel KMeans Demo</title>
<link rel="icon" type="image/x-icon" href="./favicon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/css/widgets.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/css/markdown.css" type="text/css" />
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@4.9.3/dist/js/tabulator.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@holoviz/panel@0.13.1/dist/panel.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/bundled/bootstraptemplate/bootstrap.css">
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/bundled/defaulttheme/default.css">
<style>
#sidebar {
width: 350px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<py-env>
- altair
- numpy
- pandas
- scikit-learn
- panel==0.13.1
</py-env>
<body>
<div class="container-fluid d-flex flex-column vh-100 overflow-hidden" id="container">
<nav class="navbar navbar-expand-md navbar-dark sticky-top shadow" id="header" style="background-color: #000000;">
<button type="button" class="navbar-toggle collapsed" id="sidebarCollapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="app-header">
<a class="navbar-brand app-logo" href="/">
<img src="./logo.png" class="app-logo">
</a>
<a class="title" href="" style="color: #f0ab3c;">Panel KMeans Clustering Demo</a>
</div>
</nav>
<div class="row overflow-hidden" id="content">
<div class="sidenav" id="sidebar">
<ul class="nav flex-column">
<div class="bk-root" id="x-widget"></div>
<div class="bk-root" id="y-widget"></div>
<div class="bk-root" id="n-widget"></div>
</ul>
</div>
<div class="col mh-100 float-left" id="main">
<div class="bk-root" id="intro"></div>
<div class="bk-root" id="cluster-plot"></div>
<div class="bk-root" id="table"></div>
</div>
</div>
</div>
<py-script>
import altair as alt
import panel as pn
import pandas as pd
from sklearn.cluster import KMeans
from pyodide.http import open_url
pn.config.sizing_mode = 'stretch_width'
url = 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-07-28/penguins.csv'
penguins = pd.read_csv(open_url(url)).dropna()
cols = list(penguins.columns)[2:6]
x = pn.widgets.Select(name='x', options=cols, value='bill_depth_mm').servable(target='x-widget')
y = pn.widgets.Select(name='y', options=cols, value='bill_length_mm').servable(target='y-widget')
n_clusters = pn.widgets.IntSlider(name='n_clusters', start=1, end=5, value=3).servable(target='n-widget')
brush = alt.selection_interval(name='brush') # selection of type "interval"
def get_clusters(n_clusters):
kmeans = KMeans(n_clusters=n_clusters)
est = kmeans.fit(penguins[cols].values)
df = penguins.copy()
df['labels'] = est.labels_.astype('str')
return df
def get_chart(x, y, df):
centers = df.groupby('labels').mean()
return (
alt.Chart(df)
.mark_point(size=100)
.encode(
x=alt.X(x, scale=alt.Scale(zero=False)),
y=alt.Y(y, scale=alt.Scale(zero=False)),
shape='labels',
color='species'
).add_selection(brush).properties(width=800) +
alt.Chart(centers)
.mark_point(size=250, shape='cross', color='black')
.encode(x=x+':Q', y=y+':Q')
)
intro = pn.pane.Markdown("""
This app provides an example of **building a simple dashboard using
Panel**.\n\nIt demonstrates how to take the output of **k-means
clustering on the Penguins dataset** using scikit-learn,
parameterizing the number of clusters and the variables to
plot.\n\nThe plot and the table are linked, i.e. selecting on the plot
will filter the data in the table.\n\n The **`x` marks the center** of
the cluster.
""").servable(target='intro')
chart = pn.pane.Vega().servable(target='cluster-plot')
table = pn.widgets.Tabulator(pagination='remote', page_size=10).servable(target='table')
def update_table(event=None):
table.value = get_clusters(n_clusters.value)
n_clusters.param.watch(update_table, 'value')
@pn.depends(x, y, n_clusters, watch=True)
def update_chart(*events):
chart.object = get_chart(x.value, y.value, table.value)
chart.selection.param.watch(update_filters, 'brush')
def update_filters(event=None):
filters = []
for k, v in (getattr(event, 'new') or {}).items():
filters.append(dict(field=k, type='>=', value=v[0]))
filters.append(dict(field=k, type='<=', value=v[1]))
table.filters = filters
update_table()
update_chart()
</py-script>
<script>
$(document).ready(function () {
$('#sidebarCollapse').on('click', function () {
$('#sidebar').toggleClass('active')
$(this).toggleClass('active')
var interval = setInterval(function () { window.dispatchEvent(new Event('resize')); }, 10);
setTimeout(function () { clearInterval(interval) }, 210)
});
});
</script>
</body>
</html>

View File

@@ -1,118 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="icon" type="image/x-icon" href="./favicon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="theme-color" content="#000000">
<meta name="name" content="PyScript/Panel Streaming Demo">
<title>PyScript/Panel Streaming Demo</title>
<link rel="icon" type="image/x-icon" href="./favicon.ico">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/css/widgets.css" type="text/css" />
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/css/markdown.css" type="text/css" />
<script type="text/javascript" src="https://unpkg.com/tabulator-tables@4.9.3/dist/js/tabulator.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@holoviz/panel@0.13.1/dist/panel.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/bundled/bootstraptemplate/bootstrap.css">
<link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.1/dist/bundled/defaulttheme/default.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<py-env>
- bokeh
- numpy
- pandas
- panel==0.13.1
</py-env>
<body>
<div class="container-fluid d-flex flex-column vh-100 overflow-hidden" id="container">
<nav class="navbar navbar-expand-md navbar-dark sticky-top shadow" id="header" style="background-color: #000000;">
<button type="button" class="navbar-toggle collapsed" id="sidebarCollapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="app-header">
<a class="navbar-brand app-logo" href="/">
<img src="./logo.png" class="app-logo">
</a>
<a class="title" href="" style="color: #f0ab3c;">Panel Streaming Demo</a>
</div>
</nav>
<div class="row overflow-hidden" id="content">
<div class="col mh-100 float-left" id="main">
<div class="bk-root" id="controls"></div>
<div class="row">
<div class="bk-root" id="table"></div>
<div class="bk-root" id="plot"></div>
</div>
</div>
</div>
</div>
<py-script>
import panel as pn
import numpy as np
import pandas as pd
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
df = pd.DataFrame(np.random.randn(10, 4), columns=list('ABCD')).cumsum()
rollover = pn.widgets.IntInput(name='Rollover', value=15)
follow = pn.widgets.Checkbox(name='Follow', value=True, align='end')
tabulator = pn.widgets.Tabulator(df, height=450, width=400).servable(target='table')
def color_negative_red(val):
"""
Takes a scalar and returns a string with
the css property `'color: red'` for negative
strings, black otherwise.
"""
color = 'red' if val < 0 else 'green'
return 'color: %s' % color
tabulator.style.applymap(color_negative_red)
p = figure(height=450, width=600)
cds = ColumnDataSource(data=ColumnDataSource.from_df(df))
p.line('index', 'A', source=cds, line_color='red')
p.line('index', 'B', source=cds, line_color='green')
p.line('index', 'C', source=cds, line_color='blue')
p.line('index', 'D', source=cds, line_color='purple')
def stream():
data = df.iloc[-1] + np.random.randn(4)
tabulator.stream(data, rollover=rollover.value, follow=follow.value)
value = {k: [v] for k, v in tabulator.value.iloc[-1].to_dict().items()}
value['index'] = [tabulator.value.index[-1]]
cds.stream(value)
cb = pn.state.add_periodic_callback(stream, 200)
pn.pane.Bokeh(p).servable(target='plot')
pn.Row(cb.param.period, rollover, follow, width=400).servable(target='controls')
</py-script>
</body>
</html>

View File

@@ -1,19 +0,0 @@
from datetime import datetime as dt
class PyItem(PyItemTemplate):
def on_click(self, evt=None):
self.data["done"] = not self.data["done"]
self.strike(self.data["done"])
self.select("input").element.checked = self.data["done"]
class PyList(PyListTemplate):
item_class = PyItem
def add(self, item):
if isinstance(item, str):
item = {"content": item, "done": False, "created_at": dt.now()}
super().add(item, labels=["content"], state_key="done")

View File

@@ -1 +0,0 @@
# [Pyscript Examples](https://pyscript.net/examples/)

View File

@@ -1,16 +0,0 @@
#output > div {
font-family: 'monospace';
background-color: #e5e5e5;
border: 1px solid lightgray;
border-top: 0;
font-size: 0.875rem;
padding: 0.5rem;
}
#output > div:first-child {
border-top: 1px solid lightgray;
}
#output > div:nth-child(even) {
border: 0;
}

View File

@@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>REPL</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<py-env>
- paths:
- ./antigravity.py
</py-env>
<body>
<h1><b>pyscript REPL</b></h1>
Tip: press Shift-ENTER to evaluate a cell
<br>
<div>
<py-repl id="my-repl" auto-generate="true"> </py-repl>
</div>
</body>
</html>

View File

@@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Custom REPL Example</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<link rel="stylesheet" href="repl.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<py-env>
- bokeh
- numpy
- paths:
- ./utils.py
- ./antigravity.py
</py-env>
<body>
<h1 class="font-semibold text-2xl ml-5">Custom REPL</h1>
<py-box widths="1/2;1/2">
<py-repl id="my-repl" auto-generate="true" std-out="output" std-err="err-div"> </py-repl>
<div id="output" class="p-4"></div>
</py-box>
<footer id="err-div" class="bg-red-700 text-white text-center border-t-4 border-green-500 fixed inset-x-0 bottom-0 p-4 hidden">
</footer>
</body>
</html>

View File

@@ -1,128 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>PyScript — Simple Bioinformatics Example</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='stylesheet' type='text/css' media='screen' href='https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css'>
<link rel="icon" type="image/png" href="https://user-images.githubusercontent.com/49681382/166738771-d0c26557-426c-4688-9641-8db5e6b08348.png" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<!-- Header -->
<section class="hero is-primary is-small">
<div class="hero-body">
<p class="title is-3">PyScript — Simple Bioinformatics Example <span class="tag is-white">v.1</span></p>
<p class="subtitle is-6">
Demonstrates the simple use of <a href="https://pyscript.net/" target="_blank"><code>PyScript</code></a>
in <strong>Bioinformatics/Computational Biology</strong> fields!
</p>
</div>
</section>
<!-- Main Content -->
<div class="container is-white is-fluid" style="margin-top:45px">
<p class="title is-3">🧬 DNA Sequence Tool</p>
<!--- DNA Sequence Input -->
<div class="field">
<label class="label">Input DNA Sequence</label>
<div class="control">
<textarea class="textarea" placeholder="GATTACA" id="dna_seq"></textarea>
</div>
</div>
<!-- Operation Selection -->
<label class="label">Pick the operation to be applied</label>
<div class="field has-addons">
<div class="control is-expanded">
<div class="select is-fullwidth">
<select name="operation" id="operation">
<option value="Reverse">Compute the reverse counterpart</option>
<option value="Complement">Compute the complement counterpart</option>
<option value="ReverseComplement">Compute the reverse complement counterpart</option>
</select>
</div>
</div>
<div class="control">
<button id="run" type="button" class="button is-primary" pys-onClick="run">Run!</button>
<button id="clear" type="button" class="button is-danger" pys-onClick="clear">Clear</button>
</div>
</div>
<hr>
<!--- DNA Sequence Output -->
<label class="label">Output for the <code id="operation_name_output">given operation</code></label>
<div id="output"></div>
</div>
<br><br>
<!-- Footer -->
<footer class="footer">
<div class="content has-text-centered">
<p>
Developed by <a href="mailto:furkanmtorun@gmail.com"><strong>Furkan M. Torun (@furkanmtorun)</strong></a>
<br>
<a href="https://github.com/furkanmtorun">GitHub</a>
| <a href="https://scholar.google.com/citations?user=d5ZyOZ4AAAAJ">Google Scholar</a>
| <a href="https://twitter.com/furkanmtorun">Twitter</a>
| <a href="https://www.linkedin.com/in/furkanmtorun/">LinkedIn</a>
</p>
</div>
</footer>
<py-script>
# Define HTML elements and inputs
dna_alphabet = "ATGC"
output = Element("output")
dna_seq_element = Element("dna_seq")
operation_element = Element("operation")
operation_name_output_element = Element("operation_name_output")
# DNA Sequene Operations
def return_reverse(dna_seq):
return dna_seq[::-1]
def return_complement(dna_seq):
return dna_seq.translate(str.maketrans("ATCG", "TAGC"))
def return_reverse_complement(dna_seq):
return dna_seq.translate(str.maketrans("ATCG", "TAGC"))[::-1]
# Check DNA seq is valid
def check_dna_seq(dna_seq):
return all(letter in dna_alphabet for letter in dna_seq.upper())
# Clear the form and output
def clear(*args, **kwargs):
dna_seq_element.clear()
output.clear()
# Run
def run(*args, **kwargs):
dna_seq = dna_seq_element.value
is_dna_seq_valid = check_dna_seq(dna_seq)
if is_dna_seq_valid:
operation_name = operation_element.value
operation_name_output_element.write(operation_name)
# Compute the desired outputs
if operation_name == "Reverse":
output_dna_seq = return_reverse(dna_seq)
elif operation_name == "Complement":
output_dna_seq = return_complement(dna_seq)
elif operation_name == "ReverseComplement":
output_dna_seq = return_reverse_complement(dna_seq)
# Output the result
output.write(output_dna_seq)
elif (dna_seq.strip() == "") or (dna_seq is None):
output.write("No DNA sequence provided")
else:
output.write("Invalid DNA sequence entered")
</py-script>
</body>
</html>

View File

@@ -1,59 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Simple Clock Demo</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-env>
- paths:
- ./utils.py
</py-env>
<py-config>
- autoclose_loader: false
- runtimes:
-
src: "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js"
name: pyodide-0.20
lang: python
</py-config>
</head>
<body>
<div class="font-mono">start time: <label id="outputDiv"></label></div>
<div id="outputDiv2" class="font-mono"></div>
<div id="outputDiv3" class="font-mono"></div>
<py-script output="outputDiv">
# demonstrates how use the global PyScript pyscript_loader
# to send operation log messages to it
import utils
utils.now()
</py-script>
<py-script>
from utils import now
import asyncio
async def foo():
while True:
await asyncio.sleep(1)
output = now()
pyscript.write("outputDiv2", output)
out3 = Element("outputDiv3")
if output[-1] in ["0", "4", "8"]:
out3.write("It's espresso time!")
else:
out3.clear()
# close the global PyScript pyscript_loader
pyscript_loader.close()
pyscript.run_until_complete(foo())
</py-script>
</body>
</html>

View File

@@ -1,50 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Todo App</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-env>
- paths:
- ./utils.py
</py-env>
<py-register-widget src="./pylist.py" name="py-list" klass="PyList"></py-register-widget>
<py-script>
def add_task(*ags, **kws):
# create a new dictionary representing the new task
task = { "content": new_task_content.value, "done": False, "created_at": dt.now() }
# add a new task to the list and tell it to use the `content` key to show in the UI
# and to use the key `done` to sync the task status with a checkbox element in the UI
myList.add(task)
# clear the inputbox element used to create the new task
new_task_content.clear()
</py-script>
</head>
<body>
<py-title>To Do List</py-title>
<py-box widths="4/5;1/5">
<py-inputbox id="new-task-content">
def on_keypress(e):
if (e.code == "Enter"):
add_task()
</py-inputbox>
<py-button id="new-task-btn" label="Add Task!">
def on_click(evt):
add_task()
</py-button>
</py-box>
<py-list id="myList"></py-list>
<py-repl id="my-repl" auto-generate="true"> </py-repl>
</body>
</html>

View File

@@ -1,52 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Todo App</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-env>
- paths:
- ./utils.py
</py-env>
</head>
<body class="container">
<!-- <py-repl id="my-repl" auto-generate="true"> </py-repl> -->
<py-script src="./todo.py"> </py-script>
<main class="max-w-xs mx-auto mt-4">
<section>
<div class="text-center w-full mb-8">
<h1 class="text-3xl font-bold text-gray-800 uppercase tracking-tight">To Do List</h1>
</div>
<div>
<input id="new-task-content" class="border flex-1 mr-3 border-gray-300 p-2 rounded" type="text">
<button id="new-task-btn" class="p-2 text-white bg-blue-600 border border-blue-600 rounded" type="submit" pys-onClick="add_task">
Add task
</button>
</div>
<py-list id="myList"></py-list>
<div id="list-tasks-container" class="flex flex-col-reverse mt-4">
</div>
<template id="task-template">
<section class="task bg-white my-1">
<label for="flex items-center p-2 ">
<input class="mr-2" type="checkbox" class="task-check">
<p class="m-0 inline"></p>
</label>
</section>
</template>
</section>
</main>
</body>
</html>

View File

@@ -1,53 +0,0 @@
from datetime import datetime as dt
from utils import add_class, remove_class
tasks = []
# define the task template that will be use to render new templates to the page
task_template = Element("task-template").select(".task", from_content=True)
task_list = Element("list-tasks-container")
new_task_content = Element("new-task-content")
def add_task(*ags, **kws):
# ignore empty task
if not new_task_content.element.value:
return None
# create task
task_id = f"task-{len(tasks)}"
task = {
"id": task_id,
"content": new_task_content.element.value,
"done": False,
"created_at": dt.now(),
}
tasks.append(task)
# add the task element to the page as new node in the list by cloning from a
# template
task_html = task_template.clone(task_id, to=task_list)
task_html_content = task_html.select("p")
task_html_content.element.innerText = task["content"]
task_html_check = task_html.select("input")
task_list.element.appendChild(task_html.element)
def check_task(evt=None):
task["done"] = not task["done"]
if task["done"]:
add_class(task_html_content, "line-through")
else:
remove_class(task_html_content, "line-through")
new_task_content.clear()
task_html_check.element.onclick = check_task
def add_task_event(e):
if e.key == "Enter":
add_task()
new_task_content.element.onkeypress = add_task_event

View File

@@ -1,79 +0,0 @@
# Freedom Units!
This is a demo Toga app implementing a Fahrenheit to Celsius converter.
## Initial setup
1. Create and activate a virtual environment:
$ python -m venv venv
$ . ./venv/bin/activate
2. Install the demo requirements:
$ pip install -r requirements.txt
### Development details
This demo symbolically links the pyscript build generated by the npm server
(`../build`) into the `./static/pyscript` directory. This symbolic link should
be restored automatically by git. This also means you'll need to run the main
pyscript demos at least once before running the server-rendered version of the
app. If you experience problems loading pyscript, check whether this link
exists, and the `build/pyscript.js` file has been compiled.
It also includes an vendored experimental version of toga-core, toga-web and
toga-flask, packaged as wheels in the `./static/wheels` directory. If any changes
are made to the Toga sources, these vendored resources will need to be updated.
## Web app
This app can be viewed as a Single Page App (SPA); this version of the app is
linked from the main PyScript demo pages.
It can also be viewed as a server-rendered app. This version of the app is
delivered by a demo Flask server, serving a version of app at the root URL. To
run the web demo server:
$ cd server
$ PYTHONPATH=../freedom/src python -m demo
then point your browser at http://localhost:8081/
Enter a value in the "Fahrenheit" input, and click the "calculate" button.
It may take a few seconds for this button to become live; look for the
"Collecting nodes..." entry in the console log.
## Desktop app
To run this app in development mode:
$ briefcase dev
To build and run an app bundle:
$ briefcase run
If you're on an M1 macOS, this will raise an error on first run; if you get this error, run:
$ briefcase package -p app --adhoc-sign
then re-run `briefcase run`
## iOS app
To run this in the iOS simulator, run:
$ briefcase run iOS
To run this in the Android simulator, run:
$ briefcase run android
Note that these builds have extensive requirements that must be installed -
Xcode for iOS, and the Android SDKs for Android. These are multiple gigabyte
downloads. Briefcase will detect when these tools aren't available, and either
prompt you to download them, or perform a download for you. As a result, your
first build may take up to 20 minutes to complete, depending on the speed of
your connection.

View File

@@ -1,49 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" type="image/png" href="./static/logo-32.png"/>
<!-- Bootstrap CSS -->
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk"
crossorigin="anonymous">
<link rel="stylesheet" href="./static/toga.css">
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<title>Loading...</title>
</head>
<body>
<div id="toga-placeholder">Loading...</div>
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"
integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI"
crossorigin="anonymous">
</script>
</body>
<py-env>
- './static/wheels/travertino-0.1.3-py3-none-any.whl'
- './static/wheels/toga_core-0.3.0.dev33-py3-none-any.whl'
- './static/wheels/toga_web-0.3.0.dev33-py3-none-any.whl'
- './static/wheels/freedom-0.0.1-py3-none-any.whl'
</py-env>
<py-script>
from toga_web.dom import handle as dom_handle
from freedom.__main__ import main
app = main().main_loop(spa=True)
</py-script>
</html>

View File

@@ -1,7 +0,0 @@
#!/bin/sh
mkdir -p ../static/wheels
cd src
unzip ../base-wheel.zip
zip ../../static/wheels/freedom-0.0.1-py3-none-any.whl -r freedom*
rm -rf freedom-0.0.1.dist-info

View File

@@ -1,48 +0,0 @@
[build-system]
requires = ["briefcase"]
[tool.briefcase]
project_name = "Freedom Units"
bundle = "org.beeware"
version = "0.0.1"
url = "https://beeware.org"
license = "BSD license"
author = 'Tiberius Yak'
author_email = "tiberius@beeware.org"
[tool.briefcase.app.freedom]
formal_name = "Freedom Units"
description = "A testing app"
sources = ['src/freedom']
requires = [
'../static/wheels/toga_core-0.3.0.dev33-py3-none-any.whl',
]
[tool.briefcase.app.freedom.macOS]
requires = [
'git+https://github.com/beeware/toga.git#egg=toga-cocoa&subdirectory=src/cocoa',
'std-nslog>=1.0.0',
]
[tool.briefcase.app.freedom.linux]
requires = [
'git+https://github.com/beeware/toga.git#egg=toga-gtk&subdirectory=src/gtk',
]
[tool.briefcase.app.freedom.windows]
requires = [
'git+https://github.com/beeware/toga.git#egg=toga-winforms&subdirectory=src/winforms',
]
# Mobile deployments
[tool.briefcase.app.freedom.iOS]
requires = [
'git+https://github.com/beeware/toga.git#egg=toga-iOS&subdirectory=src/iOS',
'std-nslog>=1.0.0',
]
[tool.briefcase.app.freedom.android]
requires = [
'git+https://github.com/beeware/toga.git#egg=toga-android&subdirectory=src/android',
]

View File

@@ -1,6 +0,0 @@
from freedom.app import main
print("IN FREEDOM", main)
if __name__ == "__main__":
main().main_loop()

View File

@@ -1,60 +0,0 @@
import toga
from toga.style.pack import COLUMN, LEFT, RIGHT, ROW, Pack
class FreedomApp(toga.App):
def calculate(self, widget):
try:
self.c_input.value = (float(self.f_input.value) - 32.0) * 5.0 / 9.0
except ValueError:
self.c_input.value = "???"
def startup(self):
self.main_window = toga.MainWindow(title=self.name)
c_box = toga.Box()
f_box = toga.Box()
box = toga.Box()
self.c_input = toga.TextInput(id="c_input", readonly=True)
self.f_input = toga.TextInput(id="f_input")
c_label = toga.Label("Celsius", style=Pack(text_align=LEFT))
f_label = toga.Label("Fahrenheit", style=Pack(text_align=LEFT))
join_label = toga.Label("is equivalent to", style=Pack(text_align=RIGHT))
button = toga.Button("Calculate", id="calculate", on_press=self.calculate)
f_box.add(self.f_input)
f_box.add(f_label)
c_box.add(join_label)
c_box.add(self.c_input)
c_box.add(c_label)
box.add(f_box)
box.add(c_box)
box.add(button)
box.style.update(direction=COLUMN, padding_top=10)
f_box.style.update(direction=ROW, padding=5)
c_box.style.update(direction=ROW, padding=5)
self.c_input.style.update(flex=1)
self.f_input.style.update(flex=1, padding_left=160)
c_label.style.update(width=100, padding_left=10)
f_label.style.update(width=100, padding_left=10)
join_label.style.update(width=150, padding_right=10)
button.style.update(padding=15, flex=1)
self.main_window.content = box
self.main_window.show()
def main():
return FreedomApp("Freedom Units", "org.beeware.freedom", version="0.0.1")
if __name__ == "__main__":
main().main_loop()

View File

@@ -1,9 +0,0 @@
setuptools
wheel
check-manifest
briefcase
flask==2.1.1
static/wheels/toga_core-0.3.0.dev33-py3-none-any.whl
static/wheels/toga_flask-0.3.0.dev33-py3-none-any.whl
static/wheels/toga_web-0.3.0.dev33-py3-none-any.whl
static/wheels/travertino-0.1.3-py3-none-any.whl

View File

@@ -1,11 +0,0 @@
from flask import Flask
from freedom import app as freedom
from toga_flask import TogaApp
app = Flask(__name__, static_folder="../static")
app.add_url_rule("/", view_func=TogaApp.as_view("foo", app_module=freedom))
if __name__ == "__main__":
app.run(port=8081, debug=True)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,9 +0,0 @@
main.toga.window {
margin-top: 5em;
display: flex;
flex-direction: column;
}
div.toga.box {
display: flex;
}

View File

@@ -1 +0,0 @@
Wheels will go here.

Some files were not shown because too many files have changed in this diff Show More