Compare commits

...

48 Commits

Author SHA1 Message Date
Andrea Giammarchi
6a3e2834b6 Fix #1890 - Apply latest patch that was lost (#1891) 2023-12-07 10:29:45 +01:00
Fabio Pliger
c0d45d368b Pydom add better support for select element (#1887)
* add tests for select options

* add classes to support select and options management

* fix add methond and implement clear on options

* fix optionsproxy.add

* fix select.add method

* add test adding a second option to select

* add tests around adding options in multiple flavors

* add test to add an option by passing the option it wants to be added before

* complete test around adding options

* add select to add test on remove

* add tests and support for selected item

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Andrea Giammarchi <andrea.giammarchi@gmail.com>
2023-12-06 15:56:14 -08:00
Nicholas Tollervey
f18ec3d20a Fix broken links, and put community information at the top of our READE (#1889)
* Fix broken links, and put community information at the top of our README.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-12-06 14:46:39 +00:00
Andrea Giammarchi
b0377cc7ab Fix #1745 - Throw on multiple configs (#1885)
This MR shows errors and exit in these conditions:

  * multiple `<py-config>` or `<mpy-config>` found on the page
  * both `<py-config>` and `<script type="py" config="file.toml">` found on main
  * different `<script type="py" config="a.toml">`  and `<script type="py" config="b.toml">` configs found on main
2023-12-06 14:12:22 +01:00
Andrea Giammarchi
96e671b55f Amend #1880 - Bootstrap pyscript module ASAP (#1888)
* bootstrap *pyscript* module ASAP as stdlib
  * improve the test to be sure `js.document` exists **before** our module is imported
2023-12-06 11:16:05 +01:00
Andrea Giammarchi
40e99abbdf Py editor (#1860)
* added a *py-editor* plugin based on *codemirror*
  * use a `<script type="py-editor">` wrapper to bootstrap code
  * tested that all is good via smoke-test in test/py-editor.html
2023-12-06 09:53:10 +01:00
Fabio Pliger
8b6b055681 remove examples folder (#1884)
* remove examples folder

* remove examples tests
2023-12-05 13:19:11 -08:00
Andrea Giammarchi
8e5605fa42 Patch js.document out of the box (#1880)
* Patch js.document out of the box

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-12-04 17:53:13 +01:00
Peter W
06e1fdecc2 fix more docs urls (#1882) 2023-12-01 14:15:31 -06:00
Peter W
a82e8334d6 Fix README.md broken link and update for GH pages docs (#1881) 2023-12-01 13:17:00 -06:00
Andrea Giammarchi
539bc2ae0e Expose js_modules via pyscript module (#1877)
* Expose js_modules via pyscript module

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-30 16:13:47 +01:00
Andrea Giammarchi
0711acd30e Updated Polyscript to its latest (#1876) 2023-11-30 13:49:08 +01:00
Madhur Tandon
1476131ab4 update references (#1874) 2023-11-29 21:03:34 +05:30
Madhur Tandon
89902a440c remove examples from core repository (#1868)
* remove examples from core repository

* remove icosahedron example
2023-11-29 20:01:46 +05:30
Madhur Tandon
156c23d550 Update README.md (#1872) 2023-11-28 15:53:23 +05:30
Andrea Giammarchi
30396ba79a Fix #1861 - Use addon to fit lines instead of truncating (#1867) 2023-11-27 15:05:23 +01:00
Andrea Giammarchi
a4343c62ca Updated dev/dependencies w/ polyscript & coincident (#1864) 2023-11-22 15:21:57 +01:00
Antonio Cuni
4b89c84692 use UTC time (#1859) 2023-11-15 16:00:09 +01:00
Antonio Cuni
df68449b82 Improve README and and mention the community calls (#1858)
Improve the readme in two ways:

- remove the mention to <py-repl>, and shows a quick summary of the various ways of running Python code

- add a link to the google calendar which contains the community calls
2023-11-15 12:10:52 +01:00
Andrea Giammarchi
48e3383f66 Fix #1841 - Provide a better error when input is used (#1857) 2023-11-14 15:25:17 +01:00
Fabio Pliger
e750fa7393 Add Deprecation message when loading from latest (#1848)
* add tests to verify if we show an error banner when users load from latest

* add deprecation manager to take care of showing a notification in case script src is being loaded from latest

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* make sure deprecation warning also register onWorker

* restore tests for banner in worker

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add a wait selector when testing banner since worker seems to take too long to render in CI

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix test_deprecate_loading_scripts_from_latest: I think that the
previous failure was because we actually TRIED to execute the js from
latest/core.js and it conflicted with our local copy.

But to trigger the warning is enough to have a script pointing to
pyscript.net/latest, there is no need to execute it: modify it with
type="ignore-me" and an URL which doesn't exist.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Antonio Cuni <anto.cuni@gmail.com>
2023-11-10 08:18:30 -08:00
Fabio Pliger
5a15199a3a Fix Click test example (#1849)
* fix type in div id

* add types
2023-11-08 11:09:15 -08:00
Fabio Pliger
1801472fc4 Remove when from pydom (#1850)
* bye bye when

* fix create to drop parent and actually pass the other arguments through

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* update types

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-08 09:28:58 -08:00
Nicholas Tollervey
ab15ac37ff Add playwright to dependency installation steps. (#1852) 2023-11-08 13:31:12 +00:00
Nicholas Tollervey
0955a6be49 Re-add CHANGELOG.md into root of the repository. (#1851)
* Re-add CHANGELOG.md from the tip of "classic" into root of the repository. Tidy
the formatting in CHANGELOG.md. Update the PR template to reflect the new
location of the CHANGELOG.md.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Replace with specific version.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-08 13:28:12 +00:00
Jeff Glass
d58237ea15 Update link to not use /latest (#1847)
* Update link in README.me to use specific version, not /latest
2023-11-07 16:37:10 -06:00
Andrea Giammarchi
2d50ca86a6 Fix #1840 - Do not bootstrap interactive shell (#1846) 2023-11-07 19:22:08 +01:00
Andrea Giammarchi
f1a46be738 Fix #1838 - Provides all TS from all projects (#1843) 2023-11-07 17:17:40 +01:00
Andrea Giammarchi
3e2a67d434 PyTerminal: use Pyodide instead of Python (#1833) 2023-11-03 17:59:11 +01:00
Andrea Giammarchi
aef028be6e Fix #1834 - Throw an error if more than a terminal exists (#1837) 2023-11-03 15:19:30 +01:00
Andrea Giammarchi
c8ec29a3d8 Improve offline dist content (#1836) 2023-11-03 10:00:52 +01:00
Fabio Pliger
e81830a2ea Value property to PyDom.Element and ElementCollection (#1828)
* add base test for input value field

* add value property to Element

* add test for non supported element

* prevent users to set value attribute on elements that do not support it

* add test for setting value on collections

* add value property to collection and add more tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-01 08:33:38 -07:00
Nicholas Tollervey
54df7171a2 Update publish-snapshot.yml (#1827)
Ensure playwright is installed when building.
2023-11-01 10:23:33 +00:00
Andrea Giammarchi
b31af823d1 Use <py-terminal> as default target (#1826) 2023-10-31 16:58:20 +01:00
Andrea Giammarchi
72f266532b PyScript Terminal - the latest kind (#1816) 2023-10-31 15:16:15 +01:00
Andrea Giammarchi
d9bf5cae12 Fix #1814 - Basic mpy integration (#1815) 2023-10-27 15:30:21 +02:00
Andrea Giammarchi
cd95a42e5e Fix #1812 - Avoid duplicated pyscript module (#1813) 2023-10-26 19:44:31 +02:00
Andrea Giammarchi
e67eb06d8b Breaking: new Polyscript Hooks mechanism (#1811)
* Breaking: new Polyscript Hooks mechanism

* Added proper smoke test
2023-10-26 17:13:36 +02:00
Andrea Giammarchi
28d37cdead Fix #1775 - Use latest polyscript dev script (#1810) 2023-10-24 15:23:05 +02:00
Nicholas Tollervey
13604e0a47 Simplify Makefile. Remove Conda. Use requirements.txt. Remove pointless type annotations. Update CI tests.yml. (#1793) 2023-10-24 09:53:10 +01:00
Andrea Giammarchi
aeb6f1a755 Create a dist.zip artifact (#1809) 2023-10-24 10:29:29 +02:00
Madhur Tandon
92e6f711b7 fix docs for append as True (#1808) 2023-10-23 19:32:32 +05:30
Andrea Giammarchi
a24113f42b Fix #1799 - Avoid multiple bootstraps when embedded (#1800) 2023-10-23 15:54:51 +02:00
Madhur Tandon
7a6f8ab3ad Update README.md (#1806) 2023-10-23 19:06:29 +05:30
Andrea Giammarchi
6dd242f3ce Allow PyScript to fully run locally (#1805) 2023-10-20 15:02:05 +02:00
shubhalgupta
88fa82c61a Fix contributing link is README file #1782 (#1797) 2023-10-10 13:24:15 -05:00
Andrea Giammarchi
2299ba5f61 Updated npm version (#1796) 2023-10-10 15:23:18 +02:00
Andrea Giammarchi
117df6ca38 Updated to latest polyscript + coincident (#1795) 2023-10-10 12:38:01 +02:00
197 changed files with 3033 additions and 9967 deletions

View File

@@ -11,5 +11,5 @@
<!-- Note: Only user-facing changes require a changelog entry. Internal-only API changes do not require a changelog entry. Changes in documentation do not require a changelog entry. --> <!-- Note: Only user-facing changes require a changelog entry. Internal-only API changes do not require a changelog entry. Changes in documentation do not require a changelog entry. -->
- [ ] All tests pass locally - [ ] All tests pass locally
- [ ] I have updated `docs/changelog.md` - [ ] I have updated `CHANGELOG.md`
- [ ] I have created documentation for this(if applicable) - [ ] I have created documentation for this(if applicable)

View File

@@ -35,7 +35,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: NPM Install - name: NPM Install
run: npm install run: npm install && npx playwright install
- name: Build - name: Build
run: npm run build run: npm run build

View File

@@ -37,7 +37,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: npm install - name: npm install
run: npm install run: npm install && npx playwright install
- name: build - name: build
run: npm run build run: npm run build

View File

@@ -41,7 +41,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: Install Dependencies - name: Install Dependencies
run: npm install run: npm install && npx playwright install
- name: Build Pyscript.core - name: Build Pyscript.core
run: npm run build run: npm run build

View File

@@ -42,7 +42,7 @@ jobs:
${{ runner.os }}- ${{ runner.os }}-
- name: NPM Install - name: NPM Install
run: npm install run: npm install && npx playwright install
- name: Build - name: Build
run: npm run build run: npm run build

View File

@@ -57,15 +57,24 @@ jobs:
- name: setup Miniconda - name: setup Miniconda
uses: conda-incubator/setup-miniconda@v2 uses: conda-incubator/setup-miniconda@v2
- name: Setup Environment - name: Create and activate virtual environment
run: make setup run: |
python3 -m venv test_venv
source test_venv/bin/activate
echo PATH=$PATH >> $GITHUB_ENV
echo VIRTUAL_ENV=$VIRTUAL_ENV >> $GITHUB_ENV
- name: Setup dependencies in virtual environment
run: |
make setup
- name: Build - name: Build
run: make build run: make build
- name: Integration Tests - name: Integration Tests
#run: make test-integration-parallel #run: make test-integration-parallel
run: make test-integration run: |
make test-integration
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
with: with:

4
.gitignore vendored
View File

@@ -144,6 +144,8 @@ test_results
# @pyscript/core npm artifacts # @pyscript/core npm artifacts
pyscript.core/core.* pyscript.core/core.*
pyscript.core/dist pyscript.core/dist
pyscript.core/dist pyscript.core/dist.zip
pyscript.core/src/plugins.js pyscript.core/src/plugins.js
pyscript.core/src/stdlib/pyscript.js pyscript.core/src/stdlib/pyscript.js
pyscript.core/src/3rd-party/*
!pyscript.core/src/3rd-party/READMEmd

View File

@@ -42,7 +42,7 @@ repos:
rev: "v3.0.0-alpha.6" rev: "v3.0.0-alpha.6"
hooks: hooks:
- id: prettier - id: prettier
exclude: pyscript\.core/test|pyscript\.core/dist|pyscript\.core/types|pyscript.core/src/stdlib/pyscript.js|pyscript\.sw/ exclude: pyscript\.core/test|pyscript\.core/dist|pyscript\.core/types|pyscript.core/src/stdlib/pyscript.js|pyscript\.sw/|pyscript.core/src/3rd-party
args: [--tab-width, "4"] args: [--tab-width, "4"]
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort

87
CHANGELOG.md Normal file
View File

@@ -0,0 +1,87 @@
# Release Notes
## 2023.05.01
### Features
- Added the `xterm` attribute to `py-config`. When set to `True` or `xterm`, an (output-only) [xterm.js](http://xtermjs.org/) terminal will be used in place of the default py-terminal.
- The default version of Pyodide is now `0.23.2`. See the [Pyodide Changelog](https://pyodide.org/en/stable/project/changelog.html#version-0-23-2) for a detailed list of changes.
- Added the `@when` decorator for attaching Python functions as event handlers
- The `py-mount` attribute on HTML elements has been deprecated, and will be removed in a future release.
#### Runtime py- attributes
- Added logic to react to `py-*` attributes changes, removal, `py-*` attributes added to already live nodes but also `py-*` attributes added or defined via injected nodes (either appended or via `innerHTML` operations). ([#1435](https://github.com/pyscript/pyscript/pull/1435))
#### &lt;script type="py"&gt;
- Added the ability to optionally use `<script type="py">`, `<script type="pyscript">` or `<script type="py-script">` instead of a `<py-script>` custom element, in order to tackle cases where the content of the `<py-script>` tag, inevitably parsed by browsers, could accidentally contain _HTML_ able to break the surrounding page layout. ([#1396](https://github.com/pyscript/pyscript/pull/1396))
#### &lt;py-terminal&gt;
- Added a `docked` field and attribute for the `<py-terminal>` custom element, enabled by default when the terminal is in `auto` mode, and able to dock the terminal at the bottom of the page with auto scroll on new code execution.
#### &lt;py-script&gt;
- Restored the `output` attribute of `py-script` tags to route `sys.stdout` to a DOM element with the given ID. ([#1063](https://github.com/pyscript/pyscript/pull/1063))
- Added a `stderr` attribute of `py-script` tags to route `sys.stderr` to a DOM element with the given ID. ([#1063](https://github.com/pyscript/pyscript/pull/1063))
#### &lt;py-repl&gt;
- The `output` attribute of `py-repl` tags now specifies the id of the DOM element that `sys.stdout`, `sys.stderr`, and the results of a REPL execution are written to. It no longer affects the location of calls to `display()`
- Added a `stderr` attribute of `py-repl` tags to route `sys.stderr` to a DOM element with the given ID. ([#1106](https://github.com/pyscript/pyscript/pull/1106))
- Resored the `output-mode` attribute of `py-repl` tags. If `output-mode` == 'append', the DOM element where output is printed is _not_ cleared before writing new results.
- Load code from the attribute src of py-repl and preload it into the corresponding py-repl tag by use the attribute `str` in your `py-repl` tag([#1292](https://github.com/pyscript/pyscript/pull/1292))
- &lt;py-repl&gt; elements now have a `getPySrc()` method, which returns the code inside the REPL as a string.([#1516](https://github.com/pyscript/pyscript/pull/1292))
#### Plugins
- Plugins may now implement the `beforePyReplExec()` and `afterPyReplExec()` hooks, which are called immediately before and after code in a `py-repl` tag is executed. ([#1106](https://github.com/pyscript/pyscript/pull/1106))
#### Web worker support
- introduced the new experimental `execution_thread` config option: if you set `execution_thread = "worker"`, the python interpreter runs inside a web worker
- worker support is still **very** experimental: not everything works, use it at your own risk
### Bug fixes
- Fixes [#1280](https://github.com/pyscript/pyscript/issues/1280), which describes the errors on the PyRepl tests related to having auto-gen tags that shouldn't be there.
### Enhancements
- Py-REPL tests now run on both osx and non osx OSs
- migrated from _rollup_ to _esbuild_ to create artifacts
- updated `@codemirror` dependency to its latest
### Docs
- Add docs for event handlers
## 2023.03.1
### Features
### Bug fixes
- Fixed an issue where `pyscript` would not be available when using the minified version of PyScript. ([#1054](https://github.com/pyscript/pyscript/pull/1054))
- Fixed missing closing tag when rendering an image with `display`. ([#1058](https://github.com/pyscript/pyscript/pull/1058))
- Fixed a bug where Python plugins methods were being executed twice. ([#1064](https://github.com/pyscript/pyscript/pull/1064))
### Enhancements
- When adding a `py-` attribute to an element but didn't added an `id` attribute, PyScript will now generate a random ID for the element instead of throwing an error which caused the splash screen to not shutdown. ([#1122](https://github.com/pyscript/pyscript/pull/1122))
- You can now disable the splashscreen by setting `enabled = false` in your `py-config` under the `[splashscreen]` configuration section. ([#1138](https://github.com/pyscript/pyscript/pull/1138))
### Documentation
- Fixed 'Direct usage of document is deprecated' warning in the getting started guide. ([#1052](https://github.com/pyscript/pyscript/pull/1052))
- Added reference documentation for the `py-splashscreen` plugin ([#1138](https://github.com/pyscript/pyscript/pull/1138))
- Adds doc for installing tests ([#1156](https://github.com/pyscript/pyscript/pull/1156))
- Adds docs for custom Pyscript attributes (`py-*`) that allow you to add event listeners to an element ([#1125](https://github.com/pyscript/pyscript/pull/1125))
### Deprecations and Removals
- The py-config `runtimes` to specify an interpreter has been deprecated. The `interpreters` config should be used instead. ([#1082](https://github.com/pyscript/pyscript/pull/1082))
- The attributes `pys-onClick` and `pys-onKeyDown` have been deprecated, but the warning was only shown in the console. An alert banner will now be shown on the page if the attributes are used. They will be removed in the next release. ([#1084](https://github.com/pyscript/pyscript/pull/1084))
- The pyscript elements `py-button`, `py-inputbox`, `py-box` and `py-title` have now completed their deprecation cycle and have been removed. ([#1084](https://github.com/pyscript/pyscript/pull/1084))
- The attributes `pys-onClick` and `pys-onKeyDown` have been removed. Use `py-click` and `py-keydown` instead ([#1361](https://github.com/pyscript/pyscript/pull/1361))

View File

@@ -59,9 +59,9 @@ If you would like to contribute to PyScript, but you aren't sure where to begin,
## Setting up your local environment and developing ## Setting up your local environment and developing
If you would like to contribute to PyScript, you will need to set up a local development environment. The [following instructions](https://docs.pyscript.net/latest/development/setting-up-environment.html) will help you get started. If you would like to contribute to PyScript, you will need to set up a local development environment. The [following instructions](https://pyscript.github.io/docs/latest/development/setting-up-environment.html) will help you get started.
You can also read about PyScript's [development process](https://docs.pyscript.net/latest/development/developing.html) to learn how to contribute code to PyScript, how to run tests and what's the PR etiquette of the community! You can also read about PyScript's [development process](https://pyscript.github.io/docs/latest/development/developing.html) to learn how to contribute code to PyScript, how to run tests and what's the PR etiquette of the community!
## License terms for contributions ## License terms for contributions

130
Makefile
View File

@@ -1,122 +1,92 @@
tag := latest MIN_NODE_VER := 20
git_hash ?= $(shell git log -1 --pretty=format:%h)
base_dir ?= $(shell git rev-parse --show-toplevel)
examples ?= ../$(base_dir)/examples
app_dir ?= $(shell git rev-parse --show-prefix)
CONDA_EXE := conda
CONDA_ENV ?= $(base_dir)/env
env := $(CONDA_ENV)
conda_run := $(CONDA_EXE) run -p $(env)
PYTEST_EXE := $(CONDA_ENV)/bin/pytest
MIN_NODE_VER := 14
MIN_NPM_VER := 6 MIN_NPM_VER := 6
MIN_PY3_VER := 8
NODE_VER := $(shell node -v | cut -d. -f1 | sed 's/^v\(.*\)/\1/') NODE_VER := $(shell node -v | cut -d. -f1 | sed 's/^v\(.*\)/\1/')
NPM_VER := $(shell npm -v | cut -d. -f1) NPM_VER := $(shell npm -v | cut -d. -f1)
PY3_VER := $(shell python3 -c "import sys;t='{v[1]}'.format(v=list(sys.version_info[:2]));print(t)")
PY_OK := $(shell python3 -c "print(int($(PY3_VER) >= $(MIN_PY3_VER)))")
ifeq ($(shell uname -s), Darwin) all:
SED_I_ARG := -i '' @echo "\nThere is no default Makefile target right now. Try:\n"
else @echo "make setup - check your environment and install the dependencies."
SED_I_ARG := -i @echo "make clean - clean up auto-generated assets."
endif @echo "make build - build PyScript."
@echo "make precommit-check - run the precommit checks (run eslint)."
@echo "make test-integration - run all integration tests sequentially."
@echo "make fmt - format the code."
@echo "make fmt-check - check the code formatting.\n"
.PHONY: check-node .PHONY: check-node
check-node: check-node:
@if [ $(NODE_VER) -lt $(MIN_NODE_VER) ]; then \ @if [ $(NODE_VER) -lt $(MIN_NODE_VER) ]; then \
echo "Build requires Node $(MIN_NODE_VER).x or higher: $(NODE_VER) detected"; \ echo "\033[0;31mBuild requires Node $(MIN_NODE_VER).x or higher: $(NODE_VER) detected.\033[0m"; \
false; \ false; \
fi fi
.PHONY: check-npm .PHONY: check-npm
check-npm: check-npm:
@if [ $(NPM_VER) -lt $(MIN_NPM_VER) ]; then \ @if [ $(NPM_VER) -lt $(MIN_NPM_VER) ]; then \
echo "Build requires Node $(MIN_NPM_VER).x or higher: $(NPM_VER) detected"; \ echo "\033[0;31mBuild requires Node $(MIN_NPM_VER).x or higher: $(NPM_VER) detected.\033[0m"; \
false; \ false; \
fi fi
setup: check-node check-npm .PHONY: check-python
cd pyscript.core && npm install && cd .. check-python:
$(CONDA_EXE) env $(shell [ -d $(env) ] && echo update || echo create) -p $(env) --file environment.yml @if [ $(PY_OK) -eq 0 ]; then \
$(conda_run) playwright install echo "\033[0;31mRequires Python 3.$(MIN_PY3_VER).x or higher: 3.$(PY3_VER) detected.\033[0m"; \
$(CONDA_EXE) install -c anaconda pytest -y false; \
fi
# Check the environment, install the dependencies.
setup: check-node check-npm check-python
cd pyscript.core && npm install && cd ..
ifeq ($(VIRTUAL_ENV),)
echo "\n\n\033[0;31mCannot install Python dependencies. Your virtualenv is not activated.\033[0m"
false
else
python -m pip install -r requirements.txt
playwright install
endif
# Clean up generated assets.
clean: clean:
find . -name \*.py[cod] -delete find . -name \*.py[cod] -delete
rm -rf $(env) *.egg-info
rm -rf .pytest_cache .coverage coverage.xml rm -rf .pytest_cache .coverage coverage.xml
clean-all: clean # Build PyScript.
rm -rf $(env) *.egg-info
shell:
@export CONDA_ENV_PROMPT='<{name}>'
@echo 'conda activate $(env)'
dev:
cd pyscript.core && npm run dev
build: build:
cd pyscript.core && npm run build cd pyscript.core && npx playwright install && npm run build
# use the following rule to do all the checks done by precommit: in # Run the precommit checks (run eslint).
# particular, use this if you want to run eslint.
precommit-check: precommit-check:
pre-commit run --all-files pre-commit run --all-files
examples: # Run all integration tests sequentially.
mkdir -p ./examples
cp -r ../examples/* ./examples
chmod -R 755 examples
find ./examples/toga -type f -name '*.html' -exec sed $(SED_I_ARG) s+https://pyscript.net/latest/+../../build/+g {} \;
find ./examples/webgl -type f -name '*.html' -exec sed $(SED_I_ARG) s+https://pyscript.net/latest/+../../../build/+g {} \;
find ./examples -type f -name '*.html' -exec sed $(SED_I_ARG) s+https://pyscript.net/latest/+../build/+g {} \;
npm run build
rm -rf ./examples/build
mkdir -p ./examples/build
cp -R ./build/* ./examples/build
@echo "To serve examples run: $(conda_run) python -m http.server 8080 --directory examples"
# run prerequisites and serve pyscript examples at http://localhost:8000/examples/
run-examples: setup build examples
make examples
npm install
make dev
# run all integration tests *including examples* sequentially
# TODO: (fpliger) The cd pyscript.core before running the tests shouldn't be needed but for
# but for some reason it seems to bother pytest tmppaths (or test cache?). Unclear.
test-integration: test-integration:
mkdir -p test_results mkdir -p test_results
$(PYTEST_EXE) -vv $(ARGS) pyscript.core/tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml pytest -vv $(ARGS) pyscript.core/tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml
# run all integration tests *except examples* in parallel (examples use too much memory) # Run all integration tests in parallel.
test-integration-parallel: test-integration-parallel:
mkdir -p test_results mkdir -p test_results
$(PYTEST_EXE) --numprocesses auto -vv $(ARGS) pyscript.core/tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml pytest --numprocesses auto -vv $(ARGS) pyscript.core/tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml
# run integration tests on only examples sequentially (to avoid running out of memory) # Format the code.
test-examples: fmt: fmt-py
mkdir -p test_results
$(PYTEST_EXE) -vv $(ARGS) pyscript.core/tests/integration/ --log-cli-level=warning --junitxml=test_results/integration.xml -k 'zz_examples'
fmt: fmt-py fmt-ts
@echo "Format completed" @echo "Format completed"
fmt-check: fmt-ts-check fmt-py-check # Check the code formatting.
fmt-check: fmt-py-check
@echo "Format check completed" @echo "Format check completed"
fmt-ts: # Format Python code.
npm run format
fmt-ts-check:
npm run format:check
fmt-py: fmt-py:
$(conda_run) black --skip-string-normalization . black -l 88 --skip-string-normalization .
$(conda_run) isort --profile black . isort --profile black .
# Check the format of Python code.
fmt-py-check: fmt-py-check:
$(conda_run) black -l 88 --check . black -l 88 --check .
.PHONY: $(MAKECMDGOALS) .PHONY: $(MAKECMDGOALS)

View File

@@ -4,11 +4,27 @@
### Summary ### Summary
PyScript is a framework that allows users to create rich Python applications in the browser using HTML's interface and the power of [Pyodide](https://pyodide.org/en/stable/), [WASM](https://webassembly.org/), and modern web technologies. PyScript is a framework that allows users to create rich Python applications in the browser using HTML's interface and the power of [Pyodide](https://pyodide.org/en/stable/), [MicroPython](https://micropython.org/) and [WASM](https://webassembly.org/), and modern web technologies.
To get started see the [getting started tutorial](docs/tutorials/getting-started.md). To get started see the [Beginning PyScript tutorial](https://docs.pyscript.net/latest/beginning-pyscript/).
For examples see [here](examples). For examples see [here](https://pyscript.com/@examples).
Other useful resources:
- The [official technical docs](https://docs.pyscript.net/).
- Our current [Home Page](https://pyscript.net/) on the web.
- A free-to-use [online editor](https://pyscript.com/) for trying PyScript.
- Our community [Discord Channel](https://discord.gg/BYB2kvyFwm), to keep in touch .
Every Tuesday at 15:30 UTC there is the _PyScript Community Call_ on zoom, where we can talk about PyScript development in the open. Most of the maintainers regularly participate in the call, and everybody is welcome to join.
Every other Thursday at 16:00 UTC there is the _PyScript FUN_ call: this is a call in which everybody is encouraged to show what they did with PyScript.
For more details on how to join the calls and up to date schedule, consult the official calendar:
- [Google calendar](https://calendar.google.com/calendar/u/0/embed?src=d3afdd81f9c132a8c8f3290f5cc5966adebdf61017fca784eef0f6be9fd519e0@group.calendar.google.com&ctz=UTC) in UTC time;
- [iCal format](https://calendar.google.com/calendar/ical/d3afdd81f9c132a8c8f3290f5cc5966adebdf61017fca784eef0f6be9fd519e0%40group.calendar.google.com/public/basic.ics).
### Longer Version ### Longer Version
@@ -16,40 +32,44 @@ PyScript is a meta project that aims to combine multiple open technologies into
## Try PyScript ## Try PyScript
To try PyScript, import the appropriate pyscript files into the `<head>` tag of your html page with: To try PyScript, import the appropriate pyscript files into the `<head>` tag of your html page:
```html ```html
<head> <head>
<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" /> <link
<script defer src="https://pyscript.net/latest/pyscript.js"></script> rel="stylesheet"
href="https://pyscript.net/releases/2023.11.2/core.css"
/>
<script
type="module"
src="https://pyscript.net/releases/2023.11.2/core.js"
></script>
</head> </head>
<body>
<script type="py" terminal>
from pyscript import display
display("Hello World!") # this goes to the DOM
print("Hello terminal") # this goes to the terminal
</script>
</body>
``` ```
You can then use PyScript components in your html page. PyScript currently implements the following elements: You can then use PyScript components in your html page. PyScript currently offers various ways of running Python code:
- `<py-script>`: can be used to define python code that is executable within the web page. The element itself is not rendered to the page and is only used to add logic - `<script type="py">`: can be used to define python code that is executable within the web page.
- `<py-repl>`: creates a REPL component that is rendered to the page as a code editor and allows users to write executable code - `<script type="py" src="hello.py">`: same as above, but the python source is fetched from the given URL.
- `<script type="py" terminal>`: same as above, but also creates a terminal where to display stdout and stderr (e.g., the output of `print()`); `input()` does not work.
- `<script type="py" terminal worker>`: run Python inside a web worker: the terminal is fully functional and `input()` works.
- `<py-script>`: same as `<script type="py">`, but it is not recommended because if the code contains HTML tags, they could be parsed wrongly.
- `<script type="mpy">`: same as above but use MicroPython instead of Python.
Check out the [the examples directory](examples) folder for more examples on how to use it, all you need to do is open them in Chrome. Check out the [official docs](https://docs.pyscript.net/) for more detailed documentation.
## How to Contribute ## How to Contribute
Read the [contributing guide](CONTRIBUTING.md) to learn about our development process, reporting bugs and improvements, creating issues and asking questions. Read the [contributing guide](CONTRIBUTING.md) to learn about our development process, reporting bugs and improvements, creating issues and asking questions.
Check out the [developing process](https://docs.pyscript.net/latest/development/developing.html) documentation for more information on how to setup your development environment. Check out the [developing process](https://pyscript.github.io/docs/latest/contributing) documentation for more information on how to setup your development environment.
## Resources
- [Official docs](https://docs.pyscript.net)
- [Discussion board](https://community.anaconda.cloud/c/tech-topics/pyscript)
- [Home Page](https://pyscript.net/)
- [Blog Post](https://engineering.anaconda.com/2022/04/welcome-pyscript.html)
- [Discord Channel](https://discord.gg/BYB2kvyFwm)
## Notes
- This is an extremely experimental project, so expect things to break!
- PyScript has been only tested on Chrome at the moment.
## Governance ## Governance

View File

@@ -1,26 +0,0 @@
channels:
- defaults
- conda-forge
- microsoft
dependencies:
- python=3.11.3
- pip
- pytest=7.1.2
- nodejs=16
- black
- isort
- codespell
- pre-commit
- pillow
- numpy
- markdown
- toml
- pip:
- playwright==1.33.0
- pytest-playwright==0.3.3
- pytest-xdist==3.3.0
- pexpect
# We need Pyodide and micropip so we can import them in our Python
# unit tests
- pyodide_py==0.23.2
- micropip==0.2.2

View File

@@ -1,90 +0,0 @@
<html>
<head>
<title>Altair</title>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="./favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<link rel="stylesheet" href="./assets/css/examples.css" />
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c">Altair</a>
</div>
</nav>
<section class="pyscript">
<div id="altair"></div>
<py-tutor>
<py-config>
packages = [
"altair",
"pandas",
"vega_datasets"
]
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
</py-config>
<py-script>
from pyscript import display
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)
display(alt.vconcat(
rect + circ,
bar
).resolve_legend(
color="independent",
size="independent"
), target="altair")
</py-script>
</py-tutor>
</section>
</body>
</html>

View File

@@ -1,39 +0,0 @@
<html>
<head>
<title>Antigravity</title>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="./favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<link rel="stylesheet" href="./assets/css/examples.css" />
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c">Antigravity</a>
</div>
</nav>
<py-tutor modules="antigravity.py">
<section class="pyscript">
<py-config>
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
[[fetch]]
files = ["./antigravity.py"]
</py-config>
<b>Based on xkcd: antigravity https://xkcd.com/353/.</b>
<py-script>
import antigravity
antigravity.fly()
</py-script>
</section>
</py-tutor>
</body>
</html>

View File

@@ -1,44 +0,0 @@
import random
from js import DOMParser, document, setInterval
from pyodide.ffi 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):
self.target = (
document.getElementById(target)
if isinstance(target, str)
else document.body
)
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,91 +0,0 @@
body {
margin: 0;
}
.pyscript {
margin: 0.5rem;
}
.code {
display: flex;
position: absolute;
right: 0px;
z-index: 9998;
top: 7rem;
}
@media (max-width: 1300px) {
.code:has(> .code-section-visible) {
width: 90%;
/* Absolute position is messing up the layout on small screens */
right: 70px;
}
}
.code-section-hidden {
width: 0px;
display: none;
}
.code-section-visible {
display: flex;
flex-direction: column;
width: 100%;
background-color: rgb(45 46 53 / 90%);
padding: 1rem;
border-radius: 10px 0px 0px 10px;
color: #c6c6c8;
}
.code-section-visible p {
margin: 0;
font-style: italic;
font-size: small;
}
.language-html,
.language-python {
float: left;
}
#view-code-button {
writing-mode: tb-rl;
text-orientation: sideways-right;
background-color: #1d1d22;
color: white;
padding: 0.5rem;
border-radius: 5px;
cursor: pointer;
height: 81px;
}
nav {
position: sticky;
width: 100%;
top: 0;
left: 0;
z-index: 9999;
}
.logo {
padding-right: 10px;
font-size: 28px;
height: 30px;
max-width: inherit;
}
.title {
text-decoration: none;
text-decoration-line: none;
text-decoration-style: initial;
text-decoration-color: initial;
font-weight: 400;
font-size: 1.5em;
line-height: 2em;
white-space: nowrap;
}
.app-header {
display: flex;
align-items: center;
padding: 0.5rem 1rem;
}

View File

@@ -1,74 +0,0 @@
.example {
margin-bottom: 5rem;
}
.example h2 {
/* color: #000000; */
font-family: "Inconsolata", monospace;
font-size: 2.25rem;
margin-bottom: 1rem;
}
.card {
height: 15rem;
background-color: var(--color-secondary);
padding: 1rem;
border-radius: 10px;
}
.card:hover,
.card:hover a,
.card:hover a:visited,
.card:hover h2 {
background-color: var(--color-primary);
color: #1d1d22;
}
.card a h2 {
color: var(--color-primary);
margin: 0;
font-family: "Inconsolata", monospace;
font-size: 2.25rem;
}
.card a p {
color: var(--text-color);
}
a .card {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.card-content {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.card-content a,
.card-content a:visited {
color: var(--color-primary);
}
.container-card {
max-width: 1500px;
margin: 0 auto;
display: grid;
gap: 1rem;
}
@media (min-width: 600px) {
.container-card {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 900px) {
.container-card {
grid-template-columns: repeat(4, 1fr);
}
}

View File

@@ -1,25 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap");
@import "./variables.css";
@import "./reset.css";
body {
background: #2d2e35
url("https://assets.anaconda.com/production/Content/1650828148240.png?w=3240&auto=compress%2Cformat&fit=crop&dm=1650828161&s=c558dc55e0ed1f8419a892e842a5728f")
repeat-x center bottom / 250px;
background-attachment: fixed;
overflow-x: hidden;
color: var(--text-color);
}
.container {
max-width: 1510px;
margin: auto;
padding: 0 2rem;
}
.title-main {
font-size: 4.25rem;
font-family: "Inconsolata", monospace;
text-align: center;
margin: 2rem 0;
}

View File

@@ -1,22 +0,0 @@
*,
*:after,
*:before {
margin: 0;
padding: 0;
box-sizing: border-box;
text-decoration: none;
}
body {
font-size: 100%;
list-style-type: none;
}
p {
font-family: "Inconsolata", monospace;
font-weight: 400;
}
a {
text-decoration: none;
}

View File

@@ -1,6 +0,0 @@
:root {
--color-primary: #fda703;
--color-secondary: #1d1d22;
--text-color: white;
--card-shadow: 0px 5px 11px 0px rgb(0 0 0 / 15%);
}

View File

@@ -1,3 +0,0 @@
/* PrismJS 1.29.0
https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+clike+javascript+python */
code[class*=language-],pre[class*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#272822}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8292a2}.token.punctuation{color:#f8f8f2}.token.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#f92672}.token.boolean,.token.number{color:#ae81ff}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#a6e22e}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#e6db74}.token.keyword{color:#66d9ef}.token.important,.token.regex{color:#fd971f}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}

File diff suppressed because one or more lines are too long

View File

@@ -1,29 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<py-script>
import js
import asyncio
for i in range(3):
js.console.log('A', i)
await asyncio.sleep(0.1)
</py-script>
<py-script>
import js
import asyncio
for i in range(3):
js.console.log('B', i)
await asyncio.sleep(0.1)
</py-script>
</body>
</html>

View File

@@ -1,45 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<div>
Pyscript - FIRST ASYNC WITH INVOKED LOOP BLOCKING AWAIT AT SAME
LEVEL AS LOOP Pyscript writing to console.log:
<py-script>
import js
import asyncio
async def asyncCallLoop1():
for i in range(3):
js.console.log('A', i)
await asyncio.sleep(2)
asyncCallLoop1()
</py-script>
</div>
<div>
Pyscript - SECOND ASYNC WITH INVOKED LOOP BLOCKING AWAIT AT SAME
LEVEL AS LOOP Pyscript writing to console.log:
<py-script>
import js
import asyncio
async def asyncCallLoop2():
for i in range(3):
js.console.log('B', i)
await asyncio.sleep(2)
asyncCallLoop2()
</py-script>
</div>
</body>
</html>

View File

@@ -1,42 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<div>
Pyscript - FIRST ASYNC WITH INVOKED LOOP BLOCKING AWAIT AT SAME
LEVEL AS LOOP Pyscript writing to console.log:
<py-script>
import js
import asyncio
async def asyncCallLoop1():
for i in range(3):
js.console.log('A', i)
await asyncio.sleep(2)
asyncCallLoop1()
</py-script>
</div>
<div>
Pyscript - SECOND ASYNC WITH TOP-LEVEL LOOP BLOCKING AWAIT AT SAME
LEVEL AS LOOP Pyscript writing to console.log:
<py-script>
import js
import asyncio
for i in range(3):
js.console.log('B', i)
await asyncio.sleep(2)
</py-script>
</div>
</body>
</html>

View File

@@ -1,45 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Async Await NON-BLOCKING Pyscript Twice</title>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<div>
Pyscript - FIRST ASYNC WITH NON-BLOCKING AWAIT AT ONE LEVEL LOWER
THAN LOOP Pyscript writing to console.log:
<py-script>
import js
import asyncio
async def asyncCall1():
await asyncio.sleep(2)
for i in range(3):
js.console.log('A', i)
asyncCall1()
</py-script>
</div>
<div>
Pyscript - SECOND ASYNC WITH NON-BLOCKING AWAIT AT ONE LEVEL LOWER
THAN LOOP Pyscript writing to console.log:
<py-script>
import js
import asyncio
async def asyncCall2():
await asyncio.sleep(2)
for i in range(3):
js.console.log('B', i)
asyncCall2()
</py-script>
</div>
</body>
</html>

View File

@@ -1,39 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<div>
Pyscript - FIRST ASYNC WITH TOP-LEVEL LOOP BLOCKING AWAIT AT SAME
LEVEL AS LOOP Pyscript writing to console.log:
<py-script>
import js
import asyncio
for i in range(3):
js.console.log('A', i)
await asyncio.sleep(2)
</py-script>
</div>
<div>
Pyscript - SECOND ASYNC WITH TOP-LEVEL LOOP BLOCKING AWAIT AT SAME
LEVEL AS LOOP Pyscript writing to console.log:
<py-script>
import js
import asyncio
for i in range(3):
js.console.log('B', i)
await asyncio.sleep(2)
</py-script>
</div>
</body>
</html>

View File

@@ -1,22 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Async Await BLOCKING LOOP Pyscript Twice</title>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<py-script>
import asyncio
from itertools import count
for i in count():
print(f"Count: {i}")
await asyncio.sleep(1)
</py-script>
</body>
</html>

View File

@@ -1,94 +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-3.0.3.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.0.3.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.0.3.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.0.3.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.0.3.min.js"
></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<link rel="stylesheet" href="./assets/css/examples.css" />
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c"
>Bokeh Example</a
>
</div>
</nav>
<py-tutor>
<section class="pyscript">
<div id="myplot"></div>
<py-config>
packages = [
"pandas",
"bokeh",
"xyzservices"
]
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
</py-config>
<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(width=400, 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>
</section>
</py-tutor>
</body>
</html>

View File

@@ -1,136 +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.3.js"
></script>
<script
type="text/javascript"
src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.3.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.3.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.3.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.3.min.js"
></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c"
>Bokeh Example</a
>
</div>
</nav>
<py-tutor>
<section class="pyscript">
<h1>Bokeh Example</h1>
<div id="myplot"></div>
<py-config>
packages = [
"https://cdn.holoviz.org/panel/0.14.3/dist/wheels/bokeh-2.4.3-py3-none-any.whl",
"numpy",
]
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
</py-config>
<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.ffi.to_js(events))
pydoc.apply_json_patch(json.loads(json_patch))
jsdoc.on_change(pyodide.ffi.create_proxy(jssync), pyodide.ffi.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.ffi.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)
asyncio.ensure_future(show(row, 'myplot'))
</py-script>
</section>
</py-tutor>
</body>
</html>

View File

@@ -1,138 +0,0 @@
<html>
<head>
<title>d3: JavaScript & PyScript visualizations side-by-side</title>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="./favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
<script src="https://d3js.org/d3.v7.min.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>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c"
>Simple d3 visualization</a
>
</div>
</nav>
<section class="pyscript">
<py-tutor modules="d3.py">
<py-config>
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
[[fetch]]
files = ["./d3.py"]
</py-config>
</py-tutor>
<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>
<py-script src="d3.py"></py-script>
</section>
<script type="module">
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>
</body>
</html>

View File

@@ -1,64 +0,0 @@
import js
from pyodide.ffi import create_proxy, to_js
d3 = js.d3
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},
]
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"])
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,81 +0,0 @@
<html>
<head>
<title>Folium</title>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="./favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<link rel="stylesheet" href="./assets/css/examples.css" />
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c">Folium</a>
</div>
</nav>
<section class="pyscript">
<div id="folium"></div>
<py-tutor>
<py-config>
packages = [
"folium",
"pandas"
]
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
</py-config>
<py-script>
from pyscript import display
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)
display(m, target="folium")
</py-script>
</py-tutor>
</section>
</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,137 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Say Hello</title>
<link rel="icon" type="image/png" href="../favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<py-script>
from js import handTrack, requestAnimationFrame, console
from pyodide import create_once_callable
import asyncio
update_note = Element("update-note")
canvas = Element("canvas")
video = Element("myvideo")
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():
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"
py-click="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" class="updatenote mt10">loading model ..</div>
</div>
<div>
<video autoplay="autoplay" id="myvideo" py-mount="video"></video>
<canvas id="canvas" class="border canvasbox"></canvas>
</div>
<script src="lib/handtrack.min.js"></script>
</body>
</html>

View File

@@ -1,55 +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/latest/pyscript.css"
/>
<link rel="stylesheet" href="./assets/css/examples.css" />
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c">Hello World</a>
</div>
</nav>
<py-tutor>
<py-config>
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
</py-config>
<section class="pyscript">
Hello world! <br />
This is the current date and time, as computed by Python:
<py-script>
from pyscript import display
from datetime import datetime
now = datetime.now()
display(now.strftime("%m/%d/%Y, %H:%M:%S"))
</py-script>
</section>
</py-tutor>
</body>
</html>

View File

@@ -1,315 +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="./assets/css/main.css" />
<link rel="stylesheet" href="./assets/css/index.css" />
</head>
<body class="container">
<h1 class="title-main">PyScript demos</h1>
<section class="example">
<h2>Basic examples</h2>
<div class="container-card">
<div class="card">
<div class="card-content">
<a href="./hello_world.html" target="_blank">
<h2>Hello world</h2>
</a>
<p>
A static demo of the
<code>&lt;py-script&gt;</code> tag
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./simple_clock.html" target="_blank">
<h2>Simple clock</h2>
</a>
<p>
A dynamic demo of the
<code>&lt;py-script&gt;</code> tag
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./repl.html" target="_blank">
<h2>REPL</h2>
</a>
<p>A Python REPL (Read Eval Print Loop)</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./repl2.html" target="_blank">
<h2>REPL2</h2>
</a>
<p>
A Python REPL (Read Eval Print Loop) with slightly
better formatting
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./todo.html" target="_blank">
<h2>TODO App</h2>
</a>
<p>Simple TODO App</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./todo-pylist.html" target="_blank">
<h2>PyScript Native TODO App</h2>
</a>
<p>
Simple TODO App using <code>&lt;py-list&gt;</code>
</p>
</div>
</div>
</div>
</section>
<section class="example">
<h2>MIME Rendering</h2>
<div class="container-card">
<div class="card">
<div class="card-content">
<a href="./matplotlib.html" target="_blank">
<h2>Matplotlib</h2>
</a>
<p>
Demonstrates rendering a
<a href="https://matplotlib.org/" target="_blank"
>Matplotlib</a
>
figure as output of the py-script tag
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./altair.html" target="_blank">
<h2>Altair</h2>
</a>
<p>
Demonstrates rendering a
<a
href="https://altair-viz.github.io/"
target="_blank"
>Altair</a
>
plot as output of the py-script tag
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./folium.html" target="_blank">
<h2>Folium</h2>
</a>
<p>
Demonstrates rendering a
<a
href="https://python-visualization.github.io/folium/"
target="_blank"
>Folium</a
>
map as output of the py-script tag
</p>
</div>
</div>
</div>
</section>
<section class="example">
<h2>JS Interaction</h2>
<div class="container-card">
<div class="card">
<div class="card-content">
<a href="./d3.html" target="_blank">
<h2>Simple d3 visualization</h2>
</a>
<p>
Minimal
<a href="https://d3js.org/" target="_blank">D3</a>
demo demonstrating how to create a visualization
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./webgl/raycaster/index.html" target="_blank">
<h2>Webgl Icosahedron Example</h2>
</a>
<p>
Demo showing how a Simple
<a
href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API"
target="_blank"
>WebGL</a
>
scene would work in the
<code>&lt;py-script&gt;</code> tag
</p>
</div>
</div>
</div>
</section>
<section class="example">
<h2>Visualizations & Dashboards</h2>
<div class="container-card">
<div class="card">
<div class="card-content">
<a href="./bokeh.html" target="_blank">
<h2>Simple Static Bokeh Plot</h2>
</a>
<p>
Minimal Bokeh demo demonstrating how to create a
simple
<a href="https://bokeh.org/" target="_blank"
>Bokeh</a
>
plot from code
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./bokeh_interactive.html" target="_blank">
<h2 class="text-2xl font-bold text-blue-600">
Bokeh Interactive
</h2>
</a>
<p>
Interactive demo using a
<a href="https://bokeh.org/" 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>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./panel_kmeans.html" target="_blank">
<h2 class="text-2xl font-bold text-blue-600">
KMeans Demo in Panel
</h2>
</a>
<p>
Interactive KMeans Chart using
<a href="https://panel.holoviz.org/" target="_blank"
>Panel</a
>
WARNING: This examples takes a little longer to
load. So be patient :)
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./panel_stream.html" target="_blank">
<h2 class="text-2xl font-bold text-blue-600">
Streaming Demo in Panel
</h2>
</a>
<p>
Interactive Streaming Table and Bokeh plot using
<a href="https://panel.holoviz.org/" target="_blank"
>Panel</a
>
WARNING: This examples takes a little longer to
load. So be patient :)
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./panel.html" target="_blank">
<h2 class="text-3xl font-bold text-blue-600">
Simple Panel Demo
</h2>
</a>
<p>
Simple demo showing
<a href="https://panel.holoviz.org/" target="_blank"
>Panel</a
>
widgets interacting with parts of the page WARNING:
This examples takes a little longer to load. So be
patient :)
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./panel_deckgl.html" target="_blank">
<h2 class="text-2xl font-bold text-blue-600">
NYC Taxi Data Panel DeckGL Demo
</h2>
</a>
<p>
Interactive application exploring the NYC Taxi
dataset using
<a href="https://panel.holoviz.org/" target="_blank"
>Panel</a
>
and
<a href="https://deck.gl/" target="_blank"
>DeckGL</a
>
WARNING: This examples takes a little longer to
load. So be patient :)
</p>
</div>
</div>
<div class="card">
<div class="card-content">
<a href="./numpy_canvas_fractals.html" target="_blank">
<h2 class="text-2xl font-bold text-blue-600">
Fractals with NumPy and canvas
</h2>
</a>
<p>
Visualization of Mandelbrot and Julia sets with
<a href="https://numpy.org/" target="_blank"
>Numpy</a
>
and
<a
href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API"
target="_blank"
>
HTML5 canvas
</a>
</p>
</div>
</div>
</div>
</section>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -1,35 +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,43 +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,84 +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,62 +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,145 +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,90 +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] = -0.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,51 +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,83 +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,253 +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,147 +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,61 +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,228 +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,341 +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,138 +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,348 +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,132 +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] = -0.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,228 +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,505 +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,14 +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,61 +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,64 +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] += 0.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,63 +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,130 +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] = -0.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,160 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Mario</title>
<link rel="icon" type="image/png" href="../favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/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
update_note = Element("update-note")
canvas = Element("canvas")
video = Element("myvideo")
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():
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 start 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"
py-click="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>
</body>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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,33 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>PyMarkdown</title>
<link rel="icon" type="image/png" href="favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<link rel="stylesheet" href="./assets/css/examples.css" />
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
<py-tutor>
<py-config>
packages = [
"markdown"
]
plugins = [
"https://pyscript.net/latest/plugins/python/py_markdown.py",
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
</py-config>
<py-md>#Hello world!</py-md>
</py-tutor>
</body>
</html>

View File

@@ -1,76 +0,0 @@
<html>
<head>
<title>Matplotlib</title>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="./favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
</head>
<body>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c">Matplotlib</a>
</div>
</nav>
<section class="pyscript">
<div id="mpl"></div>
<py-tutor>
<py-config>
packages = [
"matplotlib"
]
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
</py-config>
<script type="py">
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')
display(fig1, target="mpl")
</script>
</py-tutor>
</section>
</body>
</html>

View File

@@ -1,69 +0,0 @@
<html>
<head>
<link rel="icon" type="image/x-icon" href="./favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
</head>
<body>
<py-tutor>
<py-config>
packages = [
"numpy",
"networkx",
"matplotlib"
]
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
</py-config>
<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>
</py-tutor>
</body>
</html>

View File

@@ -1,196 +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/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<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 />
<py-config>
packages = [
"micrograd",
"numpy",
"matplotlib"
]
</py-config>
<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>&amp;gt;</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>
<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>
<button
id="run-all-button"
class="btn btn-primary"
type="submit"
py-click="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 />
</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,161 +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, strict=True)
]
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, strict=True)
]
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,411 +0,0 @@
<html>
<head>
<title>
Visualization of Mandelbrot, Julia and Newton sets with NumPy and
HTML5 canvas
</title>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="./favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
<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>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a class="title" href="" style="color: #f0ab3c"
>Fractals with NumPy and canvas</a
>
</div>
</nav>
<section class="pyscript">
<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-tutor>
<py-config type="json">
{
"packages": [
"numpy",
"sympy"
],
"fetch": [
{
"files": [
"./palettes.py",
"./fractals.py"
]
}
],
"plugins": [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
}
</py-config>
<py-script>
from pyodide.ffi 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
async def main():
_ = await asyncio.gather(
draw_mandelbrot(),
draw_julia(),
draw_newton(),
)
asyncio.ensure_future(main())
</py-script>
</py-tutor>
</section>
</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,132 +0,0 @@
<!doctype html>
<html>
<head>
<title id="header-title"></title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" type="image/png" href="./favicon.png" />
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
</head>
<body>
<nav class="navbar" style="background-color: #000000">
<div class="app-header">
<a href="/">
<img src="./logo.png" class="logo" />
</a>
<a
class="title"
id="page-title"
href=""
style="color: #f0ab3c"
></a>
</div>
</nav>
<br />
<div id="page-message"></div>
<div id="pandas-source">
<h3>Data Source</h3>
<input type="text" id="txt-url" class="py-input" size="70" />
<button
type="submit"
id="btn-load"
class="py-button"
py-click="loadFromURL()"
>
Load CSV
</button>
</div>
<div id="pandas-repl" hidden>
<h3>Python REPL</h3>
<py-repl id="pandas-repl-inner" output="pandas-output-inner">
# Hit SHIFT + ENTER to execute example code # Get all closed
airports in Great Britain df2 = df.query("type == 'closed' &
iso_country == 'GB'") df2
</py-repl>
</div>
<div id="pandas-output" hidden>
<h3>Output</h3>
<div id="pandas-output-inner"></div>
</div>
<div id="pandas-dev-console" hidden>
<h3>Dev Console</h3>
<py-terminal auto></py-terminal>
</div>
<py-tutor>
<py-config>
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py"
]
packages = ["pandas"]
</py-config>
<section class="pyscript">
<py-script>
import pandas as pd
from pyodide.http import open_url
import sys
title = "Pandas (and basic DOM manipulation)"
page_message = "This example loads a remote CSV file into a Pandas dataframe, displays it and lets you manipulate it through a Python REPL"
url = "https://raw.githubusercontent.com/datasets/airport-codes/master/data/airport-codes.csv"
Element("header-title").element.innerText = title
Element("page-title").element.innerText = title
Element("page-message").element.innerText = page_message
Element("txt-url").element.value = url
# Depending on the type of DOM element, there are several alternative methods to write to it
# Element("id-of-dom-element").write("example")
# Element("id-of-dom-element").innerText = "example"
# Element("id-of-dom-element").value = "example"
# Element("id-of-dom-element").element.innerText = "example"
# Element("id-of-dom-element").element.value = "example"
# js.document.getElementById("id-of-dom-element").innerText = "example"
# js.document.getElementById("id-of-dom-element").value = "example"
df = pd.DataFrame()
def loadFromURL(*args, **kws):
global df
# clear dataframe & output
df = pd.DataFrame()
Element("pandas-output-inner").element.innerHTML = ""
url = Element("txt-url").element.value
log ("Trying to fetch CSV from " + url)
df = pd.read_csv(open_url(url))
Element("pandas-repl").element.style.display = "block"
Element("pandas-output").element.style.display = "block"
Element("pandas-dev-console").element.style.display = "block"
display (df, target="pandas-output-inner", append="False")
def log(message):
# log to pyscript dev console
print (message)
# log to JS console
js.console.log (message)
</py-script>
</section>
</py-tutor>
</body>
</html>

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