init
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
398
Cargo.lock
generated
Normal file
|
@ -0,0 +1,398 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.71.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fishbowl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gif",
|
||||
"image",
|
||||
"tiny-skia",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strict-num"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-skia"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6a067b809476893fce6a254cf285850ff69c847e6cfbade6a20b655b6c7e80d"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"png",
|
||||
"tiny-skia-path",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-skia-path"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de35e8a90052baaaf61f171680ac2f8e925a1e43ea9d2e3a00514772250e541"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"bytemuck",
|
||||
"strict-num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "fishbowl"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
image = "0.24"
|
||||
gif = "0.12"
|
||||
tiny-skia = "0.11"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
BIN
resources/als.jpg
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
resources/face.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
resources/gay.jpg
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
resources/geo.jpg
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
resources/joe.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
resources/man.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
resources/mona lisa.png
Normal file
After Width: | Height: | Size: 372 KiB |
BIN
resources/person.png
Normal file
After Width: | Height: | Size: 831 KiB |
BIN
resources/rust-logo.png
Normal file
After Width: | Height: | Size: 21 KiB |
1
resources/rust-logo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="144" width="144" xmlns="http://www.w3.org/2000/svg"><path d="m71.05 23.68c-26.06 0-47.27 21.22-47.27 47.27s21.22 47.27 47.27 47.27 47.27-21.22 47.27-47.27-21.22-47.27-47.27-47.27zm-.07 4.2a3.1 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11zm7.12 5.12a38.27 38.27 0 0 1 26.2 18.66l-3.67 8.28c-.63 1.43.02 3.11 1.44 3.75l7.06 3.13a38.27 38.27 0 0 1 .08 6.64h-3.93c-.39 0-.55.26-.55.64v1.8c0 4.24-2.39 5.17-4.49 5.4-2 .23-4.21-.84-4.49-2.06-1.18-6.63-3.14-8.04-6.24-10.49 3.85-2.44 7.85-6.05 7.85-10.87 0-5.21-3.57-8.49-6-10.1-3.42-2.25-7.2-2.7-8.22-2.7h-40.6a38.27 38.27 0 0 1 21.41-12.08l4.79 5.02c1.08 1.13 2.87 1.18 4 .09zm-44.2 23.02a3.11 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11zm74.15.14a3.11 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11zm-68.29.5h5.42v24.44h-10.94a38.27 38.27 0 0 1 -1.24-14.61l6.7-2.98c1.43-.64 2.08-2.31 1.44-3.74zm22.62.26h12.91c.67 0 4.71.77 4.71 3.8 0 2.51-3.1 3.41-5.65 3.41h-11.98zm0 17.56h9.89c.9 0 4.83.26 6.08 5.28.39 1.54 1.26 6.56 1.85 8.17.59 1.8 2.98 5.4 5.53 5.4h16.14a38.27 38.27 0 0 1 -3.54 4.1l-6.57-1.41c-1.53-.33-3.04.65-3.37 2.18l-1.56 7.28a38.27 38.27 0 0 1 -31.91-.15l-1.56-7.28c-.33-1.53-1.83-2.51-3.36-2.18l-6.43 1.38a38.27 38.27 0 0 1 -3.32-3.92h31.27c.35 0 .59-.06.59-.39v-11.06c0-.32-.24-.39-.59-.39h-9.15zm-14.43 25.33a3.11 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11zm46.05.14a3.11 3.11 0 0 1 3.02 3.11 3.11 3.11 0 0 1 -6.22 0 3.11 3.11 0 0 1 3.2-3.11z"/><path d="m115.68 70.95a44.63 44.63 0 0 1 -44.63 44.63 44.63 44.63 0 0 1 -44.63-44.63 44.63 44.63 0 0 1 44.63-44.63 44.63 44.63 0 0 1 44.63 44.63zm-.84-4.31 6.96 4.31-6.96 4.31 5.98 5.59-7.66 2.87 4.78 6.65-8.09 1.32 3.4 7.46-8.19-.29 1.88 7.98-7.98-1.88.29 8.19-7.46-3.4-1.32 8.09-6.65-4.78-2.87 7.66-5.59-5.98-4.31 6.96-4.31-6.96-5.59 5.98-2.87-7.66-6.65 4.78-1.32-8.09-7.46 3.4.29-8.19-7.98 1.88 1.88-7.98-8.19.29 3.4-7.46-8.09-1.32 4.78-6.65-7.66-2.87 5.98-5.59-6.96-4.31 6.96-4.31-5.98-5.59 7.66-2.87-4.78-6.65 8.09-1.32-3.4-7.46 8.19.29-1.88-7.98 7.98 1.88-.29-8.19 7.46 3.4 1.32-8.09 6.65 4.78 2.87-7.66 5.59 5.98 4.31-6.96 4.31 6.96 5.59-5.98 2.87 7.66 6.65-4.78 1.32 8.09 7.46-3.4-.29 8.19 7.98-1.88-1.88 7.98 8.19-.29-3.4 7.46 8.09 1.32-4.78 6.65 7.66 2.87z" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/></svg>
|
After Width: | Height: | Size: 2.3 KiB |
BIN
resources/sam.png
Normal file
After Width: | Height: | Size: 664 KiB |
BIN
resources/troll.png
Normal file
After Width: | Height: | Size: 190 KiB |
37
src/drawing.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use tiny_skia::*;
|
||||
|
||||
use crate::sim::Circle;
|
||||
pub fn clear(pixmap: &mut Pixmap) {
|
||||
let mut black = Paint::default();
|
||||
black.set_color_rgba8(0, 0, 0, 255);
|
||||
pixmap.fill_rect(
|
||||
Rect::from_xywh(0.0, 0.0, pixmap.width() as f32, pixmap.height() as f32)
|
||||
.unwrap(),
|
||||
&black,
|
||||
Transform::identity(),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw_circle(pixmap: &mut Pixmap, circle: &Circle) {
|
||||
let x = circle.position.x;
|
||||
let y = circle.position.y;
|
||||
let radius = circle.radius;
|
||||
let r = circle.color.0;
|
||||
let g = circle.color.1;
|
||||
let b = circle.color.2;
|
||||
let mut paint = Paint::default();
|
||||
paint.set_color_rgba8(r, g, b, 255);
|
||||
let mut brush = PathBuilder::new();
|
||||
brush.push_circle(x, y, radius);
|
||||
brush.move_to(x, y);
|
||||
brush.close();
|
||||
|
||||
pixmap.fill_path(
|
||||
&brush.finish().unwrap(),
|
||||
&paint,
|
||||
FillRule::Winding,
|
||||
tiny_skia::Transform::identity(),
|
||||
None,
|
||||
)
|
||||
}
|
82
src/helper.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Color(pub u8, pub u8, pub u8);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Vector2 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl Vector2 {
|
||||
pub const fn new(x: f32, y: f32) -> Vector2 {
|
||||
Vector2 { x, y }
|
||||
}
|
||||
|
||||
pub fn length2(&self) -> f32 {
|
||||
(self.x * self.x) + (self.y * self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Vector2 {
|
||||
type Output = Vector2;
|
||||
|
||||
fn add(self, v: Vector2) -> Self {
|
||||
Vector2 {
|
||||
x: self.x + v.x,
|
||||
y: self.y + v.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<f32> for Vector2 {
|
||||
type Output = Vector2;
|
||||
|
||||
fn add(self, value: f32) -> Self {
|
||||
Vector2 {
|
||||
x: self.x + value,
|
||||
y: self.y + value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Vector2 {
|
||||
fn add_assign(&mut self, v: Vector2) {
|
||||
*self = *self + v;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Vector2 {
|
||||
type Output = Vector2;
|
||||
|
||||
fn sub(self, v: Vector2) -> Self {
|
||||
Vector2 {
|
||||
x: self.x - v.x,
|
||||
y: self.y - v.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Vector2 {
|
||||
fn sub_assign(&mut self, v: Vector2) {
|
||||
*self = *self - v;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Vector2 {
|
||||
type Output = Vector2;
|
||||
|
||||
fn mul(self, value: f32) -> Self {
|
||||
Vector2 {
|
||||
x: self.x * value,
|
||||
y: self.y * value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<f32> for Vector2 {
|
||||
fn mul_assign(&mut self, value: f32) {
|
||||
*self = *self * value;
|
||||
}
|
||||
}
|
57
src/lib.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use std::io::Write;
|
||||
|
||||
use image::{ImageBuffer, Rgb};
|
||||
|
||||
mod drawing;
|
||||
mod helper;
|
||||
mod sim;
|
||||
|
||||
fn write_frame<W: Write>(
|
||||
sim: &sim::Simulation,
|
||||
encoder: &mut gif::Encoder<&mut W>,
|
||||
) {
|
||||
let mut pixbuf = tiny_skia::Pixmap::new(500, 500).unwrap();
|
||||
drawing::clear(&mut pixbuf);
|
||||
for index in 0..sim.circles.len() {
|
||||
let circle = &sim.circles[index];
|
||||
drawing::draw_circle(&mut pixbuf, circle);
|
||||
}
|
||||
let mut frame = gif::Frame::from_rgba(500, 500, pixbuf.data_mut());
|
||||
frame.delay = 1;
|
||||
encoder.write_frame(&frame).unwrap();
|
||||
}
|
||||
|
||||
async fn write_frame_async<W: Write>(
|
||||
sim: &sim::Simulation,
|
||||
encoder: &mut gif::Encoder<&mut W>,
|
||||
) {
|
||||
write_frame(sim, encoder);
|
||||
}
|
||||
|
||||
pub fn generate(image: ImageBuffer<Rgb<u8>, Vec<u8>>) -> Vec<u8> {
|
||||
let (mut sim, it) = sim::Simulation::simulate_image(500.0, 500.0, 8.0, image);
|
||||
|
||||
let mut buffer = Vec::<u8>::new();
|
||||
let mut encoder = gif::Encoder::new(&mut buffer, 500, 500, &[]).unwrap();
|
||||
// encoder.set_repeat(gif::Repeat::Infinite).unwrap();
|
||||
while sim.clock < it {
|
||||
write_frame(&sim, &mut encoder);
|
||||
(0..5).for_each(|_| sim.step());
|
||||
}
|
||||
drop(encoder);
|
||||
buffer
|
||||
}
|
||||
|
||||
pub async fn generate_async(image: ImageBuffer<Rgb<u8>, Vec<u8>>) -> Vec<u8> {
|
||||
let (mut sim, it) = sim::Simulation::simulate_image(500.0, 500.0, 8.0, image);
|
||||
|
||||
let mut buffer = Vec::<u8>::new();
|
||||
let mut encoder = gif::Encoder::new(&mut buffer, 500, 500, &[]).unwrap();
|
||||
// encoder.set_repeat(gif::Repeat::Infinite).unwrap();
|
||||
while sim.clock < it {
|
||||
write_frame_async(&sim, &mut encoder).await;
|
||||
(0..5).for_each(|_| sim.step());
|
||||
}
|
||||
drop(encoder);
|
||||
buffer
|
||||
}
|
235
src/sim.rs
Normal file
|
@ -0,0 +1,235 @@
|
|||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::helper::*;
|
||||
use image::{ImageBuffer, Rgb};
|
||||
|
||||
pub struct Circle {
|
||||
pub position: Vector2,
|
||||
last_position: Vector2,
|
||||
pub radius: f32,
|
||||
pub color: Color,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
pub struct Simulation {
|
||||
pub circles: Vec<Circle>,
|
||||
pub colors: Vec<Color>,
|
||||
pub max_circles: usize,
|
||||
pub clock: usize,
|
||||
substeps: usize,
|
||||
rand_seed: usize,
|
||||
timescale: f32,
|
||||
circle_radius: f32,
|
||||
radius_variance: f32,
|
||||
area_size: (f32, f32),
|
||||
gravity: f32,
|
||||
response_mod: f32,
|
||||
}
|
||||
|
||||
impl Simulation {
|
||||
pub fn new(
|
||||
width: f32,
|
||||
height: f32,
|
||||
circle_radius: f32,
|
||||
rand_seed: usize,
|
||||
) -> Self {
|
||||
let area = width * height;
|
||||
let circle_area = circle_radius.powi(2) * std::f32::consts::PI;
|
||||
let approx_max = ((area / circle_area).round() * 1.0) as usize;
|
||||
Self {
|
||||
circles: Vec::with_capacity(approx_max),
|
||||
max_circles: approx_max,
|
||||
timescale: 1.0 / 60.0,
|
||||
substeps: 4,
|
||||
colors: vec![Color(255, 255, 255); approx_max],
|
||||
clock: rand_seed,
|
||||
rand_seed,
|
||||
circle_radius,
|
||||
radius_variance: circle_radius * 0.1,
|
||||
area_size: (width, height),
|
||||
gravity: height,
|
||||
response_mod: 0.4,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn assign_colors_from_image(&mut self, img: ImageBuffer<Rgb<u8>, Vec<u8>>) {
|
||||
let (width, height) = (img.width() as f32 - 1.0, img.height() as f32 - 1.0);
|
||||
for (pos, index) in self.circles.iter().map(|c| (c.position, c.index)) {
|
||||
let img_x =
|
||||
((pos.x / self.area_size.0).clamp(0.0, 1.0) * width).round() as u32;
|
||||
let img_y =
|
||||
((pos.y / self.area_size.1).clamp(0.0, 1.0) * height).round() as u32;
|
||||
let pixel = img.get_pixel(img_x, img_y);
|
||||
let color = Color(pixel[0], pixel[1], pixel[2]);
|
||||
|
||||
self.colors[index] = color;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn simulate_image(
|
||||
width: f32,
|
||||
height: f32,
|
||||
circle_radius: f32,
|
||||
img: ImageBuffer<Rgb<u8>, Vec<u8>>,
|
||||
) -> (Self, usize) {
|
||||
let image_hash = ({
|
||||
let mut s = std::hash::DefaultHasher::new();
|
||||
img.hash(&mut s);
|
||||
s.finish()
|
||||
} % 1204) as usize;
|
||||
let mut sim = Simulation::new(width, height, circle_radius, image_hash);
|
||||
while sim.circles() < sim.max_circles {
|
||||
sim.step();
|
||||
}
|
||||
(0..120).for_each(|_| sim.step());
|
||||
let total_iterations = sim.clock;
|
||||
sim.assign_colors_from_image(img);
|
||||
sim.circles.clear();
|
||||
sim.clock = sim.rand_seed;
|
||||
(sim, total_iterations)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_circle(&mut self, position: Vector2, velocity: Vector2) {
|
||||
self.circles.push(Circle {
|
||||
position,
|
||||
last_position: position - velocity,
|
||||
radius: self.circle_radius
|
||||
+ (self.clock as f32).sin() * self.radius_variance,
|
||||
color: self.colors[self.circles.len()],
|
||||
index: self.circles.len(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn launch(&mut self) {
|
||||
let time = self.clock as f32 / (10.0 * std::f32::consts::PI);
|
||||
let halfwidth = self.area_size.0 / 2.0;
|
||||
let x = ((halfwidth - self.circle_radius * 2.0) * time.cos().abs())
|
||||
+ self.circle_radius;
|
||||
self.add_circle(
|
||||
Vector2::new(x, self.circle_radius),
|
||||
Vector2::new(time.cos(), time.sin().abs()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn launch2(&mut self) {
|
||||
let time = self.clock as f32 / (10.0 * std::f32::consts::PI);
|
||||
let halfwidth = self.area_size.0 / 2.0;
|
||||
let x = ((halfwidth - self.circle_radius * 2.0) * time.sin().abs())
|
||||
+ self.circle_radius;
|
||||
let x = x + halfwidth;
|
||||
self.add_circle(
|
||||
Vector2::new(x, self.circle_radius),
|
||||
Vector2::new(time.cos(), time.sin().abs()),
|
||||
)
|
||||
}
|
||||
|
||||
// Insertion sort
|
||||
#[inline]
|
||||
fn sort(&mut self) {
|
||||
if self.circles.len() == 1 {
|
||||
return;
|
||||
}
|
||||
for i in 1..self.circles.len() {
|
||||
let mut j = i;
|
||||
while j > 0 && self.circles[j - 1].position.x > self.circles[j].position.x
|
||||
{
|
||||
self.circles.swap(j - 1, j);
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn integrate(&mut self) {
|
||||
let delta = self.timescale * (1.0 / self.substeps as f32);
|
||||
let gravity = Vector2::new(0.0, self.gravity) * delta.powi(2);
|
||||
self.circles.iter_mut().for_each(|circle| {
|
||||
let velocity = circle.position - circle.last_position;
|
||||
circle.last_position = circle.position;
|
||||
circle.position = circle.position + velocity + gravity;
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn collide(&mut self) {
|
||||
for i in 0..self.circles.len() {
|
||||
// Apply gravity
|
||||
for j in i..self.circles.len() {
|
||||
let circle = &self.circles[i].position;
|
||||
let other = &self.circles[j].position;
|
||||
let this_radius = self.circles[i].radius;
|
||||
let other_radius = self.circles[i].radius;
|
||||
let diameter = this_radius + other_radius;
|
||||
if circle.x < other.x - diameter {
|
||||
break; // No further collisions possible
|
||||
}
|
||||
let dy = (circle.y - other.y).abs();
|
||||
if dy >= diameter {
|
||||
continue; // Skip over obvious noncollisions
|
||||
}
|
||||
let combined = *circle - *other;
|
||||
let distance_squared = combined.length2();
|
||||
if distance_squared >= diameter.powi(2) || distance_squared == 0.0 {
|
||||
continue;
|
||||
}
|
||||
// Finally, resort to expensive calculation
|
||||
let distance = distance_squared.sqrt();
|
||||
let normalized = combined * (1.0 / distance);
|
||||
let delta = 0.5 * self.response_mod * (distance - diameter);
|
||||
self.circles[i].position -= normalized * delta * 0.5;
|
||||
self.circles[j].position += normalized * delta * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn constrain_rect(&mut self) {
|
||||
self
|
||||
.circles
|
||||
.iter_mut()
|
||||
.map(|c| &mut c.position)
|
||||
.for_each(|pos| {
|
||||
pos.x = pos
|
||||
.x
|
||||
.clamp(self.circle_radius, self.area_size.0 - self.circle_radius);
|
||||
pos.y = pos
|
||||
.y
|
||||
.clamp(self.circle_radius, self.area_size.1 - self.circle_radius);
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn step(&mut self) {
|
||||
if self.circles.len() < self.max_circles {
|
||||
self.launch();
|
||||
}
|
||||
if self.circles.len() < self.max_circles {
|
||||
self.launch2();
|
||||
}
|
||||
|
||||
(0..self.substeps).for_each(|_| {
|
||||
self.constrain_rect();
|
||||
self.sort();
|
||||
self.collide();
|
||||
self.integrate();
|
||||
self.clock += 1;
|
||||
});
|
||||
}
|
||||
|
||||
// #[inline]
|
||||
// pub fn draw(&self, d: &mut RaylibDrawHandle) {
|
||||
// self
|
||||
// .circles
|
||||
// .iter()
|
||||
// .map(|c| (c.position, c.radius, c.color))
|
||||
// .for_each(|(pos, radius, color)| d.draw_circle_v(pos,
|
||||
// radius, color)); }
|
||||
|
||||
pub fn circles(&self) -> usize {
|
||||
self.circles.len()
|
||||
}
|
||||
}
|