Initial commit
This commit is contained in:
commit
8d4ce5af8a
|
@ -0,0 +1,11 @@
|
|||
install:
|
||||
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly
|
||||
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --locked
|
|
@ -0,0 +1,6 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
|
@ -0,0 +1,69 @@
|
|||
language: rust
|
||||
sudo: false
|
||||
|
||||
cache: cargo
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
||||
# Builds with wasm-pack.
|
||||
- rust: beta
|
||||
env: RUST_BACKTRACE=1
|
||||
addons:
|
||||
firefox: latest
|
||||
chrome: stable
|
||||
before_script:
|
||||
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
|
||||
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
|
||||
- cargo install-update -a
|
||||
- curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
|
||||
script:
|
||||
- cargo generate --git . --name testing
|
||||
# Having a broken Cargo.toml (in that it has curlies in fields) anywhere
|
||||
# in any of our parent dirs is problematic.
|
||||
- mv Cargo.toml Cargo.toml.tmpl
|
||||
- cd testing
|
||||
- wasm-pack build
|
||||
- wasm-pack test --chrome --firefox --headless
|
||||
|
||||
# Builds on nightly.
|
||||
- rust: nightly
|
||||
env: RUST_BACKTRACE=1
|
||||
before_script:
|
||||
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
|
||||
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
|
||||
- cargo install-update -a
|
||||
- rustup target add wasm32-unknown-unknown
|
||||
script:
|
||||
- cargo generate --git . --name testing
|
||||
- mv Cargo.toml Cargo.toml.tmpl
|
||||
- cd testing
|
||||
- cargo check
|
||||
- cargo check --target wasm32-unknown-unknown
|
||||
- cargo check --no-default-features
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features
|
||||
- cargo check --no-default-features --features console_error_panic_hook
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook
|
||||
- cargo check --no-default-features --features "console_error_panic_hook wee_alloc"
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc"
|
||||
|
||||
# Builds on beta.
|
||||
- rust: beta
|
||||
env: RUST_BACKTRACE=1
|
||||
before_script:
|
||||
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
|
||||
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
|
||||
- cargo install-update -a
|
||||
- rustup target add wasm32-unknown-unknown
|
||||
script:
|
||||
- cargo generate --git . --name testing
|
||||
- mv Cargo.toml Cargo.toml.tmpl
|
||||
- cd testing
|
||||
- cargo check
|
||||
- cargo check --target wasm32-unknown-unknown
|
||||
- cargo check --no-default-features
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features
|
||||
- cargo check --no-default-features --features console_error_panic_hook
|
||||
- cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook
|
||||
# Note: no enabling the `wee_alloc` feature here because it requires
|
||||
# nightly for now.
|
|
@ -0,0 +1,28 @@
|
|||
[package]
|
||||
name = "fluster"
|
||||
version = "0.1.0"
|
||||
authors = ["Mike Welsh <mwelsh@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook", "console_log"]
|
||||
|
||||
[dependencies]
|
||||
bacon_rajan_cc = "0.2"
|
||||
console_error_panic_hook = { version = "0.1.1", optional = true }
|
||||
console_log = { version = "0.1", optional = true }
|
||||
js-sys = "0.3.19"
|
||||
log = "0.4"
|
||||
svg = "0.5.12"
|
||||
swf = { path = "../swf-rs", version = "*" }
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.19"
|
||||
features = ["CanvasRenderingContext2d", "HtmlCanvasElement", "Performance", "HtmlImageElement", "Window"]
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.2"
|
|
@ -0,0 +1,176 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2018 Mike Welsh <mwelsh@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,53 @@
|
|||
# 🦀🕸️ `wasm-pack-template`
|
||||
|
||||
A template for kick starting a Rust and WebAssembly project using
|
||||
[`wasm-pack`](https://github.com/rustwasm/wasm-pack).
|
||||
|
||||
[**📚 Read this template tutorial! 📚**][template-docs]
|
||||
|
||||
This template is designed for compiling Rust libraries into WebAssembly and
|
||||
publishing the resulting package to NPM.
|
||||
|
||||
Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other
|
||||
templates and usages of `wasm-pack`.
|
||||
|
||||
[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html
|
||||
[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html
|
||||
|
||||
## 🚴 Usage
|
||||
|
||||
### 🐑 Use `cargo generate` to Clone this Template
|
||||
|
||||
[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate)
|
||||
|
||||
```
|
||||
cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project
|
||||
cd my-project
|
||||
```
|
||||
|
||||
### 🛠️ Build with `wasm-pack build`
|
||||
|
||||
```
|
||||
wasm-pack build
|
||||
```
|
||||
|
||||
### 🔬 Test in Headless Browsers with `wasm-pack test`
|
||||
|
||||
```
|
||||
wasm-pack test --headless --firefox
|
||||
```
|
||||
|
||||
### 🎁 Publish to NPM with `wasm-pack publish`
|
||||
|
||||
```
|
||||
wasm-pack publish
|
||||
```
|
||||
|
||||
## 🔋 Batteries Included
|
||||
|
||||
* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating
|
||||
between WebAssembly and JavaScript.
|
||||
* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook)
|
||||
for logging panic messages to the developer console.
|
||||
* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized
|
||||
for small code size.
|
|
@ -0,0 +1,12 @@
|
|||
use web_sys::HtmlImageElement;
|
||||
|
||||
pub enum Character {
|
||||
Graphic {
|
||||
image: HtmlImageElement,
|
||||
},
|
||||
MovieClip {
|
||||
num_frames: u16,
|
||||
tag_stream: swf::read::Reader<std::io::Cursor<Vec<u8>>>,
|
||||
},
|
||||
Sound,
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
use crate::Library;
|
||||
use crate::RenderContext;
|
||||
use crate::{graphic::Graphic, MovieClip};
|
||||
use bacon_rajan_cc::{Trace, Tracer};
|
||||
|
||||
pub trait DisplayObject {
|
||||
//fn children_gc_mut(&self) -> std::slice::Iter<&mut DisplayObjectNode>;
|
||||
fn run_frame(&mut self, library: &Library);
|
||||
fn render(&self, context: &mut RenderContext);
|
||||
}
|
||||
|
||||
pub enum DisplayObjectNode {
|
||||
Graphic(Graphic),
|
||||
MovieClip(MovieClip),
|
||||
}
|
||||
|
||||
impl DisplayObject for DisplayObjectNode {
|
||||
fn run_frame(&mut self, library: &Library) {
|
||||
match self {
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.run_frame(library),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.run_frame(library),
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, context: &mut RenderContext) {
|
||||
match self {
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.render(context),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.render(context),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Trace for DisplayObjectNode {
|
||||
fn trace(&mut self, tracer: &mut Tracer) {
|
||||
match self {
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.trace(tracer),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.trace(tracer),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
use crate::display_object::DisplayObject;
|
||||
use crate::library::Library;
|
||||
use crate::Matrix;
|
||||
use crate::RenderContext;
|
||||
use bacon_rajan_cc::{Trace, Tracer};
|
||||
use web_sys::HtmlImageElement;
|
||||
|
||||
pub struct Graphic {
|
||||
matrix: Matrix,
|
||||
image: HtmlImageElement,
|
||||
}
|
||||
|
||||
impl Graphic {
|
||||
pub fn new(image: HtmlImageElement) -> Graphic {
|
||||
Graphic {
|
||||
image,
|
||||
matrix: std::default::Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayObject for Graphic {
|
||||
fn run_frame(&mut self, _library: &Library) {
|
||||
// Noop
|
||||
}
|
||||
|
||||
fn render(&self, context: &mut RenderContext) {
|
||||
context.matrix_stack.push(&self.matrix);
|
||||
let world_matrix = context.matrix_stack.matrix();
|
||||
context
|
||||
.context_2d
|
||||
.transform(
|
||||
world_matrix.a.into(),
|
||||
world_matrix.b.into(),
|
||||
world_matrix.c.into(),
|
||||
world_matrix.d.into(),
|
||||
world_matrix.tx.into(),
|
||||
world_matrix.ty.into(),
|
||||
)
|
||||
.unwrap();
|
||||
context
|
||||
.context_2d
|
||||
.draw_image_with_html_image_element(&self.image, 0.0, 0.0)
|
||||
.unwrap();
|
||||
context.matrix_stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl Trace for Graphic {
|
||||
fn trace(&mut self, _tracer: &mut Tracer) {
|
||||
// Noop
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
mod character;
|
||||
mod display_object;
|
||||
mod graphic;
|
||||
mod library;
|
||||
mod matrix;
|
||||
mod movie_clip;
|
||||
mod shape_utils;
|
||||
|
||||
use self::character::Character;
|
||||
use self::display_object::DisplayObject;
|
||||
use self::library::Library;
|
||||
use self::matrix::{Matrix, MatrixStack};
|
||||
use self::movie_clip::MovieClip;
|
||||
use bacon_rajan_cc::Cc;
|
||||
use js_sys::{ArrayBuffer, Uint8Array};
|
||||
use log::{info, trace, warn};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::rc::Rc;
|
||||
use swf::Color;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlImageElement};
|
||||
|
||||
type CharacterId = swf::CharacterId;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Player {
|
||||
tag_stream: swf::read::Reader<Cursor<Vec<u8>>>,
|
||||
canvas: HtmlCanvasElement,
|
||||
render_context: RenderContext,
|
||||
|
||||
library: Library,
|
||||
root: Cc<RefCell<MovieClip>>,
|
||||
|
||||
frame_rate: f64,
|
||||
frame_accumulator: f64,
|
||||
cur_timestamp: f64,
|
||||
|
||||
background_color: Color,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Player {
|
||||
pub fn new(data: ArrayBuffer, canvas: HtmlCanvasElement) -> Player {
|
||||
console_error_panic_hook::set_once();
|
||||
console_log::init_with_level(log::Level::Trace).expect("error initializing log");
|
||||
|
||||
let data = Uint8Array::new(data.as_ref());
|
||||
let mut swf_data = vec![0; data.byte_length() as usize];
|
||||
data.copy_to(&mut swf_data[..]);
|
||||
|
||||
let (swf, tag_stream) = swf::read::read_swf_header_decompressed(&swf_data[..]).unwrap();
|
||||
info!("{}x{}", swf.stage_size.x_max, swf.stage_size.y_max);
|
||||
|
||||
canvas.set_width(swf.stage_size.x_max as u32);
|
||||
canvas.set_height(swf.stage_size.y_max as u32);
|
||||
|
||||
let context: CanvasRenderingContext2d = canvas
|
||||
.get_context("2d")
|
||||
.expect("Expected canvas")
|
||||
.expect("Expected canvas")
|
||||
.dyn_into()
|
||||
.expect("Expected CanvasRenderingContext2d");
|
||||
|
||||
Player {
|
||||
tag_stream,
|
||||
|
||||
canvas,
|
||||
render_context: RenderContext {
|
||||
context_2d: context,
|
||||
matrix_stack: MatrixStack::new(),
|
||||
},
|
||||
|
||||
library: Library::new(),
|
||||
root: MovieClip::new(),
|
||||
|
||||
frame_rate: swf.frame_rate.into(),
|
||||
frame_accumulator: 0.0,
|
||||
cur_timestamp: web_sys::window()
|
||||
.expect("Expected window")
|
||||
.performance()
|
||||
.expect("Expected performance")
|
||||
.now(),
|
||||
background_color: Color {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255,
|
||||
a: 255,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, timestamp: f64) {
|
||||
let dt = timestamp - self.cur_timestamp;
|
||||
self.cur_timestamp = timestamp;
|
||||
|
||||
self.frame_accumulator += dt;
|
||||
let frame_time = 1000.0 / self.frame_rate;
|
||||
let needs_render = self.frame_accumulator >= frame_time;
|
||||
while self.frame_accumulator >= frame_time {
|
||||
self.frame_accumulator -= frame_time;
|
||||
self.run_frame();
|
||||
}
|
||||
|
||||
if needs_render {
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Player {
|
||||
fn run_frame(&mut self) {
|
||||
use swf::Tag;
|
||||
|
||||
while let Ok(Some(tag)) = self.tag_stream.read_tag() {
|
||||
trace!("{:?}", tag);
|
||||
match tag {
|
||||
Tag::FileAttributes(file_attributes) => {}
|
||||
|
||||
Tag::SetBackgroundColor(color) => self.background_color = color,
|
||||
|
||||
Tag::ShowFrame => break,
|
||||
|
||||
Tag::DefineSceneAndFrameLabelData {
|
||||
scenes,
|
||||
frame_labels,
|
||||
} => (), // TODO(Herschel)
|
||||
|
||||
Tag::DefineShape(shape) => {
|
||||
if !self.library.contains_character(shape.id) {
|
||||
let svg = shape_utils::swf_shape_to_svg(&shape);
|
||||
info!("{}", svg);
|
||||
let mut image = HtmlImageElement::new().unwrap();
|
||||
image.set_src(&format!("data:image/svg+xml;utf8;{}", svg));
|
||||
self.library
|
||||
.register_character(shape.id, Character::Graphic { image });
|
||||
}
|
||||
}
|
||||
|
||||
//tag => self.root.borrow_mut().run_tag(tag)
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self) {
|
||||
let background_color = format!(
|
||||
"rgb({}, {}, {})",
|
||||
self.background_color.r, self.background_color.g, self.background_color.b
|
||||
);
|
||||
info!("{:?}", background_color);
|
||||
self.render_context
|
||||
.context_2d
|
||||
.set_fill_style(&background_color.into());
|
||||
|
||||
let width: f64 = self.canvas.width().into();
|
||||
let height: f64 = self.canvas.height().into();
|
||||
|
||||
self.render_context
|
||||
.context_2d
|
||||
.fill_rect(0.0, 0.0, width, height);
|
||||
|
||||
self.root.borrow_mut().render(&mut self.render_context);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RenderContext {
|
||||
context_2d: CanvasRenderingContext2d,
|
||||
matrix_stack: MatrixStack,
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
use crate::character::Character;
|
||||
use crate::display_object::DisplayObjectNode;
|
||||
use crate::graphic::Graphic;
|
||||
use crate::movie_clip::MovieClip;
|
||||
use std::collections::HashMap;
|
||||
use swf::CharacterId;
|
||||
|
||||
pub struct Library {
|
||||
characters: HashMap<CharacterId, Character>,
|
||||
}
|
||||
|
||||
impl Library {
|
||||
pub fn new() -> Library {
|
||||
Library {
|
||||
characters: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_character(&mut self, id: CharacterId, character: Character) {
|
||||
// TODO(Herschel): What is the behavior if id already exists?
|
||||
self.characters.insert(id, character);
|
||||
}
|
||||
|
||||
pub fn contains_character(&self, id: CharacterId) -> bool {
|
||||
self.characters.contains_key(&id)
|
||||
}
|
||||
|
||||
pub fn instantiate_display_object(
|
||||
&self,
|
||||
id: CharacterId,
|
||||
) -> Result<DisplayObjectNode, Box<std::error::Error>> {
|
||||
match self.characters.get(&id) {
|
||||
Some(Character::Graphic { image }) => {
|
||||
Ok(DisplayObjectNode::Graphic(Graphic::new(image.clone())))
|
||||
}
|
||||
Some(Character::MovieClip {
|
||||
tag_stream,
|
||||
num_frames,
|
||||
}) => Ok(DisplayObjectNode::MovieClip(MovieClip::new_with_data(
|
||||
tag_stream.clone(),
|
||||
*num_frames,
|
||||
))),
|
||||
Some(_) => Err("Not a DisplayObject".into()),
|
||||
None => Err("Character id doesn't exist".into()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Matrix {
|
||||
pub a: f32,
|
||||
pub b: f32,
|
||||
pub c: f32,
|
||||
pub d: f32,
|
||||
pub tx: f32,
|
||||
pub ty: f32,
|
||||
}
|
||||
|
||||
impl From<swf::Matrix> for Matrix {
|
||||
fn from(matrix: swf::Matrix) -> Matrix {
|
||||
Matrix {
|
||||
a: matrix.scale_x,
|
||||
b: matrix.rotate_skew_1,
|
||||
c: matrix.rotate_skew_0,
|
||||
d: matrix.scale_y,
|
||||
tx: matrix.translate_x,
|
||||
ty: matrix.translate_y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul for Matrix {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
Matrix {
|
||||
a: self.a * rhs.a + self.c * rhs.b,
|
||||
b: self.b * rhs.a + self.d * rhs.b,
|
||||
c: self.a * rhs.c + self.c * rhs.d,
|
||||
d: self.b * rhs.c + self.d * rhs.d,
|
||||
tx: self.a * rhs.tx + self.c * rhs.ty + self.tx,
|
||||
ty: self.b * rhs.tx + self.d * rhs.ty + self.ty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for Matrix {
|
||||
fn default() -> Matrix {
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 0.0,
|
||||
tx: 0.0,
|
||||
b: 0.0,
|
||||
d: 1.0,
|
||||
ty: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::MulAssign for Matrix {
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = Matrix {
|
||||
a: self.a * rhs.a + self.c * rhs.b,
|
||||
b: self.b * rhs.a + self.d * rhs.b,
|
||||
c: self.a * rhs.c + self.c * rhs.d,
|
||||
d: self.b * rhs.c + self.d * rhs.d,
|
||||
tx: self.a * rhs.tx + self.c * rhs.ty + self.tx,
|
||||
ty: self.b * rhs.tx + self.d * rhs.ty + self.ty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MatrixStack(Vec<Matrix>);
|
||||
|
||||
impl MatrixStack {
|
||||
pub fn new() -> MatrixStack {
|
||||
MatrixStack(vec![Matrix::default()])
|
||||
}
|
||||
|
||||
pub fn push(&mut self, matrix: &Matrix) {
|
||||
let new_matrix = *matrix * *self.matrix();
|
||||
self.0.push(new_matrix);
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) {
|
||||
if self.0.len() <= 1 {
|
||||
panic!("Matrix stack underflow");
|
||||
}
|
||||
self.0.pop();
|
||||
}
|
||||
|
||||
pub fn matrix(&self) -> &Matrix {
|
||||
&self.0[self.0.len() - 1]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
use crate::display_object::{DisplayObject, DisplayObjectNode};
|
||||
use crate::matrix::Matrix;
|
||||
use crate::Library;
|
||||
use crate::RenderContext;
|
||||
use bacon_rajan_cc::{Cc, Trace, Tracer};
|
||||
use log::{info, trace, warn};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
|
||||
type Depth = i16;
|
||||
type FrameNumber = u16;
|
||||
|
||||
pub struct MovieClip {
|
||||
tag_stream: Option<swf::read::Reader<Cursor<Vec<u8>>>>,
|
||||
is_playing: bool,
|
||||
matrix: Matrix,
|
||||
current_frame: FrameNumber,
|
||||
total_frames: FrameNumber,
|
||||
children: HashMap<Depth, Cc<RefCell<DisplayObjectNode>>>,
|
||||
}
|
||||
|
||||
impl MovieClip {
|
||||
pub fn new() -> Cc<RefCell<MovieClip>> {
|
||||
let clip = MovieClip {
|
||||
tag_stream: None,
|
||||
is_playing: true,
|
||||
matrix: Matrix::default(),
|
||||
current_frame: 1,
|
||||
total_frames: 1,
|
||||
children: HashMap::new(),
|
||||
};
|
||||
Cc::new(RefCell::new(clip))
|
||||
}
|
||||
|
||||
pub fn new_with_data(
|
||||
tag_stream: swf::read::Reader<Cursor<Vec<u8>>>,
|
||||
num_frames: u16,
|
||||
) -> MovieClip {
|
||||
MovieClip {
|
||||
tag_stream: Some(tag_stream),
|
||||
is_playing: true,
|
||||
matrix: Matrix::default(),
|
||||
current_frame: 1,
|
||||
total_frames: num_frames,
|
||||
children: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_place_object(&mut self, place_object: &swf::PlaceObject, library: &Library) {
|
||||
use swf::PlaceObjectAction;
|
||||
match place_object.action {
|
||||
PlaceObjectAction::Place(id) => {
|
||||
// TODO(Herschel): Behavior when character doesn't exist/isn't a DisplayObject?
|
||||
let mut character = library.instantiate_display_object(id).unwrap();
|
||||
|
||||
// TODO(Herschel): Behavior when depth is occupied? (I think it replaces)
|
||||
self.children
|
||||
.insert(place_object.depth, Cc::new(RefCell::new(character)));
|
||||
}
|
||||
PlaceObjectAction::Modify => (),
|
||||
PlaceObjectAction::Replace(id) => {
|
||||
let mut character = library.instantiate_display_object(id).unwrap();
|
||||
|
||||
// TODO(Herschel): Behavior when depth is occupied? (I think it replaces)
|
||||
self.children
|
||||
.insert(place_object.depth, Cc::new(RefCell::new(character)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayObject for MovieClip {
|
||||
fn run_frame(&mut self, library: &Library) {
|
||||
use swf::Tag;
|
||||
if self.tag_stream.is_some() {
|
||||
while let Ok(Some(tag)) = self.tag_stream.as_mut().unwrap().read_tag() {
|
||||
trace!("{:?}", tag);
|
||||
match tag {
|
||||
Tag::ShowFrame => break,
|
||||
Tag::PlaceObject(place_object) => {
|
||||
self.run_place_object(&*place_object, library)
|
||||
}
|
||||
_ => unimplemented!("Umimplemented tag: {:?}", tag),
|
||||
}
|
||||
}
|
||||
|
||||
// Advance frame number.
|
||||
self.current_frame += 1;
|
||||
if self.current_frame < self.total_frames {
|
||||
self.current_frame += 1;
|
||||
} else {
|
||||
self.current_frame = 1;
|
||||
//tag_stream.to_inner().set_position(0);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(Herschel): Verify order of execution for parent/children.
|
||||
// Parent first? Children first? Sorted by depth?
|
||||
for child in self.children.values() {
|
||||
child.borrow_mut().run_frame(library);
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, context: &mut RenderContext) {
|
||||
context.matrix_stack.push(&self.matrix);
|
||||
let world_matrix = context.matrix_stack.matrix();
|
||||
context
|
||||
.context_2d
|
||||
.transform(
|
||||
world_matrix.a.into(),
|
||||
world_matrix.b.into(),
|
||||
world_matrix.c.into(),
|
||||
world_matrix.d.into(),
|
||||
world_matrix.tx.into(),
|
||||
world_matrix.ty.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for child in self.children.values() {
|
||||
child.borrow_mut().render(context);
|
||||
}
|
||||
|
||||
context.matrix_stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl Trace for MovieClip {
|
||||
fn trace(&mut self, tracer: &mut Tracer) {
|
||||
for child in self.children.values_mut() {
|
||||
child.trace(tracer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
use svg::node::element::{path::Data, Definitions, LinearGradient, Path as SvgPath, Stop};
|
||||
use svg::Document;
|
||||
use swf::{Color, FillStyle, LineStyle, Shape};
|
||||
|
||||
pub fn swf_shape_to_svg(shape: &Shape) -> String {
|
||||
//let mut svg = String::new();
|
||||
let mut document = Document::new()
|
||||
.set("width", shape.shape_bounds.x_max - shape.shape_bounds.x_min)
|
||||
.set(
|
||||
"height",
|
||||
shape.shape_bounds.y_max - shape.shape_bounds.y_min,
|
||||
)
|
||||
.set(
|
||||
"viewBox",
|
||||
(
|
||||
shape.shape_bounds.x_min,
|
||||
shape.shape_bounds.y_min,
|
||||
shape.shape_bounds.x_max - shape.shape_bounds.x_min,
|
||||
shape.shape_bounds.y_max - shape.shape_bounds.y_min,
|
||||
),
|
||||
);
|
||||
|
||||
let mut defs = Definitions::new();
|
||||
let mut num_defs = 0;
|
||||
|
||||
let mut svg_paths = vec![];
|
||||
for path in swf_shape_to_paths(shape) {
|
||||
let mut svg_path = SvgPath::new();
|
||||
|
||||
svg_path = svg_path.set(
|
||||
"fill",
|
||||
match path.fill_style {
|
||||
FillStyle::Color(Color { r, g, b, a }) => format!("rgba({},{},{},{})", r, g, b, a),
|
||||
FillStyle::LinearGradient(gradient) => {
|
||||
let x1 = 0.0;
|
||||
let y1 = 0.0;
|
||||
let x2 = 1.0;
|
||||
let y2 = 1.0;
|
||||
let mut svg_gradient = LinearGradient::new()
|
||||
.set("id", format!("f{}", num_defs))
|
||||
.set("x1", x1)
|
||||
.set("y1", y1)
|
||||
.set("x2", x2)
|
||||
.set("y2", y2);
|
||||
for record in &gradient.records {
|
||||
let stop = Stop::new()
|
||||
.set("offset", format!("{}%", f32::from(record.ratio) / 255.0))
|
||||
.set(
|
||||
"stop-color",
|
||||
format!(
|
||||
"rgba({},{},{},{})",
|
||||
record.color.r, record.color.g, record.color.b, record.color.a
|
||||
),
|
||||
);
|
||||
svg_gradient = svg_gradient.add(stop);
|
||||
}
|
||||
defs = defs.add(svg_gradient);
|
||||
|
||||
let fill_id = format!("url(#f{})", num_defs);
|
||||
num_defs += 1;
|
||||
fill_id
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
);
|
||||
|
||||
let mut data = Data::new();
|
||||
for subpath in &path.subpaths {
|
||||
//svg_paths.push_str(&format!("M{} {}", subpath.start.0, subpath.start.1));
|
||||
data = data.move_to(subpath.start);
|
||||
|
||||
for edge in &subpath.edges {
|
||||
match edge {
|
||||
SubpathEdge::Straight(x, y) => {
|
||||
data = data.line_to((*x, *y));
|
||||
}
|
||||
SubpathEdge::Bezier(cx, cy, ax, ay) => {
|
||||
data = data.quadratic_curve_to((*cx, *cy, *ax, *ay));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg_path = svg_path.set("d", data);
|
||||
svg_paths.push(svg_path);
|
||||
}
|
||||
|
||||
if num_defs > 0 {
|
||||
document = document.add(defs);
|
||||
}
|
||||
|
||||
for svg_path in svg_paths {
|
||||
document = document.add(svg_path);
|
||||
}
|
||||
|
||||
document.to_string()
|
||||
}
|
||||
|
||||
fn swf_shape_to_paths(shape: &Shape) -> Vec<Path> {
|
||||
let mut layers = vec![];
|
||||
let mut paths = HashMap::<u32, Path>::new();
|
||||
|
||||
let mut x = 0f32;
|
||||
let mut y = 0f32;
|
||||
|
||||
let mut fill_style_0 = 0;
|
||||
let mut fill_style_1 = 0;
|
||||
let mut i = 0;
|
||||
let mut fill_styles = &shape.styles.fill_styles;
|
||||
for record in &shape.shape {
|
||||
use swf::ShapeRecord::*;
|
||||
match record {
|
||||
StyleChange(style_change) => {
|
||||
if let Some((move_x, move_y)) = style_change.move_to {
|
||||
x = move_x;
|
||||
y = move_y;
|
||||
}
|
||||
|
||||
if let Some(i) = style_change.fill_style_0 {
|
||||
fill_style_0 = i;
|
||||
}
|
||||
|
||||
if let Some(i) = style_change.fill_style_1 {
|
||||
fill_style_1 = i;
|
||||
}
|
||||
|
||||
if let Some(ref new_styles) = style_change.new_styles {
|
||||
// TODO
|
||||
layers.push(paths);
|
||||
paths = HashMap::new();
|
||||
fill_styles = &new_styles.fill_styles;
|
||||
}
|
||||
}
|
||||
|
||||
StraightEdge { delta_x, delta_y } => {
|
||||
if fill_style_0 != 0 {
|
||||
let path = paths.entry(fill_style_0).or_insert_with(|| {
|
||||
Path::new(fill_styles[fill_style_0 as usize - 1].clone())
|
||||
});
|
||||
path.add_edge((x + delta_x, y + delta_y), SubpathEdge::Straight(x, y));
|
||||
}
|
||||
|
||||
if fill_style_1 != 0 {
|
||||
let path = paths.entry(fill_style_1).or_insert_with(|| {
|
||||
Path::new(fill_styles[fill_style_1 as usize - 1].clone())
|
||||
});
|
||||
path.add_edge((x, y), SubpathEdge::Straight(x + delta_x, y + delta_y));
|
||||
}
|
||||
|
||||
x += delta_x;
|
||||
y += delta_y;
|
||||
}
|
||||
|
||||
CurvedEdge {
|
||||
control_delta_x,
|
||||
control_delta_y,
|
||||
anchor_delta_x,
|
||||
anchor_delta_y,
|
||||
} => {
|
||||
if fill_style_0 != 0 {
|
||||
let path = paths.entry(fill_style_0).or_insert_with(|| {
|
||||
Path::new(fill_styles[fill_style_0 as usize - 1].clone())
|
||||
});
|
||||
path.add_edge(
|
||||
(
|
||||
x + control_delta_x + anchor_delta_x,
|
||||
y + control_delta_y + anchor_delta_y,
|
||||
),
|
||||
SubpathEdge::Bezier(x + control_delta_x, y + control_delta_y, x, y),
|
||||
);
|
||||
}
|
||||
|
||||
if fill_style_1 != 0 {
|
||||
let path = paths.entry(fill_style_1).or_insert_with(|| {
|
||||
Path::new(fill_styles[fill_style_1 as usize - 1].clone())
|
||||
});
|
||||
path.add_edge(
|
||||
(x, y),
|
||||
SubpathEdge::Bezier(
|
||||
x + control_delta_x,
|
||||
y + control_delta_y,
|
||||
x + control_delta_x + anchor_delta_x,
|
||||
y + control_delta_y + anchor_delta_y,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
x += control_delta_x + anchor_delta_x;
|
||||
y += control_delta_y + anchor_delta_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layers.push(paths);
|
||||
layers
|
||||
.into_iter()
|
||||
.flat_map(|p| p.into_iter().map(|(_, sp)| sp))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub struct Path {
|
||||
fill_style: FillStyle,
|
||||
subpaths: Vec<Subpath>,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
fn new(fill_style: FillStyle) -> Path {
|
||||
Path {
|
||||
fill_style,
|
||||
subpaths: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn add_edge(&mut self, start: (f32, f32), edge: SubpathEdge) {
|
||||
let new_subpath = Subpath {
|
||||
start,
|
||||
end: match edge {
|
||||
SubpathEdge::Straight(x, y) => (x, y),
|
||||
SubpathEdge::Bezier(_cx, _cy, ax, ay) => (ax, ay),
|
||||
},
|
||||
|
||||
edges: {
|
||||
let mut edges = VecDeque::new();
|
||||
edges.push_back(edge);
|
||||
edges
|
||||
},
|
||||
};
|
||||
|
||||
self.merge_subpath(new_subpath);
|
||||
}
|
||||
|
||||
fn merge_subpath(&mut self, mut subpath: Subpath) {
|
||||
fn approx_eq(a: (f32, f32), b: (f32, f32)) -> bool {
|
||||
let dx = a.0 - b.0;
|
||||
let dy = a.1 - b.1;
|
||||
const EPSILON: f32 = 0.0001;
|
||||
dx.abs() < EPSILON && dy.abs() < EPSILON
|
||||
}
|
||||
|
||||
let mut subpath_index = None;
|
||||
for (i, other) in self.subpaths.iter_mut().enumerate() {
|
||||
if approx_eq(subpath.end, other.start) {
|
||||
other.start = subpath.start;
|
||||
for edge in subpath.edges.iter().rev() {
|
||||
other.edges.push_front(*edge);
|
||||
}
|
||||
subpath_index = Some(i);
|
||||
break;
|
||||
}
|
||||
|
||||
if approx_eq(other.end, subpath.start) {
|
||||
other.end = subpath.end;
|
||||
other.edges.append(&mut subpath.edges);
|
||||
|
||||
subpath_index = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(i) = subpath_index {
|
||||
let subpath = self.subpaths.swap_remove(i);
|
||||
self.merge_subpath(subpath);
|
||||
} else {
|
||||
self.subpaths.push(subpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Subpath {
|
||||
start: (f32, f32),
|
||||
end: (f32, f32),
|
||||
|
||||
edges: VecDeque<SubpathEdge>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum SubpathEdge {
|
||||
Straight(f32, f32),
|
||||
Bezier(f32, f32, f32, f32),
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//! Test suite for the Web and headless browsers.
|
||||
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
extern crate wasm_bindgen_test;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn pass() {
|
||||
assert_eq!(1 + 1, 2);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { spawn } = require("child_process");
|
||||
const fs = require("fs");
|
||||
|
||||
let folderName = '.';
|
||||
|
||||
if (process.argv.length >= 3) {
|
||||
folderName = process.argv[2];
|
||||
if (!fs.existsSync(folderName)) {
|
||||
fs.mkdirSync(folderName);
|
||||
}
|
||||
}
|
||||
|
||||
const clone = spawn("git", ["clone", "https://github.com/rustwasm/create-wasm-app.git", folderName]);
|
||||
|
||||
clone.on("close", code => {
|
||||
if (code !== 0) {
|
||||
console.error("cloning the template failed!")
|
||||
process.exit(code);
|
||||
} else {
|
||||
console.log("🦀 Rust + 🕸 Wasm = ❤");
|
||||
}
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
dist
|
|
@ -0,0 +1,5 @@
|
|||
language: node_js
|
||||
node_js: "10"
|
||||
|
||||
script:
|
||||
- ./node_modules/.bin/webpack
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) [year] [name]
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,51 @@
|
|||
# `create-wasm-app`
|
||||
|
||||
> An `npm init` template for kick starting a project that uses NPM packages
|
||||
> containing Rust-generated WebAssembly and bundles them with Webpack.
|
||||
|
||||
This template is designed for depending on NPM packages that contain
|
||||
Rust-generated WebAssembly and using them to create a Website.
|
||||
|
||||
* Want to create an NPM package with Rust and WebAssembly? [Check out
|
||||
`wasm-pack-template`.](https://github.com/rustwasm/wasm-pack-template)
|
||||
* Want to make a monorepo-style Website without publishing to NPM? Check out
|
||||
[`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template)
|
||||
and/or
|
||||
[`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template).
|
||||
|
||||
## 🚴 Usage
|
||||
|
||||
```
|
||||
npm init wasm-app
|
||||
```
|
||||
|
||||
## 🔋 Batteries Included
|
||||
|
||||
- `.gitignore`: ignores `node_modules`
|
||||
- `LICENSE-APACHE` and `LICENSE-MIT`: most Rust projects are licensed this way, so these are included for you
|
||||
- `README.md`: the file you are reading now!
|
||||
- `index.html`: a bare bones html document that includes the webpack bundle
|
||||
- `index.js`: example js file with a comment showing how to import and use a wasm pkg
|
||||
- `package.json` and `package-lock.json`:
|
||||
- pulls in devDependencies for using webpack:
|
||||
- [`webpack`](https://www.npmjs.com/package/webpack)
|
||||
- [`webpack-cli`](https://www.npmjs.com/package/webpack-cli)
|
||||
- [`webpack-dev-server`](https://www.npmjs.com/package/webpack-dev-server)
|
||||
- defines a `start` script to run `webpack-dev-server`
|
||||
- `webpack.config.js`: configuration file for bundling your js with webpack
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
|
@ -0,0 +1,5 @@
|
|||
// A dependency graph that contains any wasm must all be imported
|
||||
// asynchronously. This `bootstrap.js` file does the single async import, so
|
||||
// that no one else needs to worry about it again.
|
||||
import("./index.js")
|
||||
.catch(e => console.error("Error importing `index.js`:", e));
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hello wasm-pack!</title>
|
||||
<style>
|
||||
body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<input type="file" id="file-input" />
|
||||
<canvas id="fluster-canvas"></canvas>
|
||||
<script src="./bootstrap.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,30 @@
|
|||
import { Player } from "fluster";
|
||||
|
||||
let fileInput = document.getElementById("file-input");
|
||||
fileInput.addEventListener("change", fileSelected, false);
|
||||
|
||||
let player;
|
||||
|
||||
function fileSelected() {
|
||||
let file = fileInput.files[0];
|
||||
if (file) {
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = e => {
|
||||
playSwf(fileReader.result);
|
||||
}
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
}
|
||||
}
|
||||
|
||||
function playSwf(swfData) {
|
||||
let canvas = document.getElementById("fluster-canvas");
|
||||
if (swfData && canvas) {
|
||||
player = Player.new(swfData, canvas);
|
||||
window.requestAnimationFrame(tickPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
function tickPlayer(timestamp) {
|
||||
player.tick(timestamp);
|
||||
window.requestAnimationFrame(tickPlayer);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "create-wasm-app",
|
||||
"version": "0.1.0",
|
||||
"description": "create an app to consume rust-generated wasm packages",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"create-wasm-app": ".bin/create-wasm-app.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"start": "webpack-dev-server"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
|
||||
},
|
||||
"keywords": [
|
||||
"webassembly",
|
||||
"wasm",
|
||||
"rust",
|
||||
"webpack"
|
||||
],
|
||||
"author": "Ashley Williams <ashley666ashley@gmail.com>",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rustwasm/create-wasm-app/issues"
|
||||
},
|
||||
"homepage": "https://github.com/rustwasm/create-wasm-app#readme",
|
||||
"devDependencies": {
|
||||
"hello-wasm-pack": "^0.1.0",
|
||||
"webpack": "^4.29.3",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.5",
|
||||
"copy-webpack-plugin": "^5.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: "./bootstrap.js",
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
filename: "bootstrap.js",
|
||||
},
|
||||
mode: "development",
|
||||
plugins: [
|
||||
new CopyWebpackPlugin(['index.html'])
|
||||
],
|
||||
};
|
Loading…
Reference in New Issue