diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml
index 0e8687c9..4a807061 100644
--- a/.github/workflows/builds.yml
+++ b/.github/workflows/builds.yml
@@ -59,6 +59,8 @@ jobs:
run: wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 10
- name: Install ImGui dependencies
run: sudo apt install libxcb-shape0-dev libxcb-xfixes0-dev
+ - name: Install Native File Dialog dependencies
+ run: sudo apt-get install libgtk-3-dev
- name: Build NDCell
run: cargo build --release
- name: Upload executable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32c51b46..47ba2361 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,32 +4,47 @@ All notable changes to NDCell will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), except for minor stylistic changes to organize features and accomodate named versions. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) with respect to the Rust API for `ndcell_core`, the NDCA API for `ndcell_lang`, and the combined Lua/NDCA API for `ndcell_ui`, the main application.
-## [Unreleased]
+## [0.2.0] Blinker (2020-02-21)
+
+![Blinker](https://user-images.githubusercontent.com/6060305/108616710-f3316780-73dd-11eb-858f-1cda97cad993.png)
### Added
- **Simulation**
+ - 3D rendering and simulation
- Advance one generation (Space)
- Advance one step (Tab)
- - 3D rendering and simulation
+- **Selection**
+ - Added edge resize indicator
+ - Cancel selection drag (Esc)
- **Navigation**
- 3D orbit (right mouse drag)
- 3D pan (↑/←/↓/→, W/A/S/D, or middle mouse drag)
- 3D pan horizontally (middle mouse drag with Shift)
+ - Zoom (right mouse drag with Ctrl)
+- **GUI**
+ - Load/save file
### Changed
- **Simulation**
- Cells align better to pixel boundaries when zoomed out, appearing crisper
- Optimized 2D rendering of empty areas
+- **Selection**
+ - Selection edge resizing now clamps to the opposite corner
- **GUI**
- - Rename "UPS" (updates per second) to "step/sec" (steps per second)
+ - Disabled rounded window borders
- Display "RUNNING" or "STEPPING" accordingly instead of "SIMULATING"
+ - Relabeled "Trigger garbage collection" button to "Clear cache"
+ - Replaced inaccurate maximum simulation speed with average simulation time.
+- Tweaked colors
### Fixed
- Changing the step size while the simulation is running now takes effect immediately ([#6][i6])
- Selected cells no longer appear to be tiled infinitely
+- Touchpad scrolling now zooms in/out at a reasonable pace
+- Crash when pressing an exotic mouse button
[i6]: https://github.com/HactarCE/NDCell/issues/6
diff --git a/Cargo.lock b/Cargo.lock
index e029a7be..896b0188 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -55,6 +55,15 @@ version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86"
+[[package]]
+name = "approx"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "approx"
version = "0.4.0"
@@ -128,7 +137,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf"
dependencies = [
- "lazy_static",
+ "lazy_static 1.4.0",
"memchr",
"regex-automata",
"serde",
@@ -198,10 +207,16 @@ version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
dependencies = [
- "approx",
+ "approx 0.4.0",
"num-traits",
]
+[[package]]
+name = "chlorine"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd650552110e39b7c5058986cf177decd3365841836578ac50a286094eac0be6"
+
[[package]]
name = "chrono"
version = "0.4.19"
@@ -320,7 +335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59"
dependencies = [
"atty",
- "lazy_static",
+ "lazy_static 1.4.0",
"winapi 0.3.9",
]
@@ -430,7 +445,7 @@ dependencies = [
"criterion-plot",
"csv",
"itertools 0.9.0",
- "lazy_static",
+ "lazy_static 1.4.0",
"num-traits",
"oorandom",
"plotters",
@@ -484,7 +499,7 @@ dependencies = [
"cfg-if 1.0.0",
"const_fn",
"crossbeam-utils",
- "lazy_static",
+ "lazy_static 1.4.0",
"memoffset",
"scopeguard",
]
@@ -497,7 +512,16 @@ checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
- "lazy_static",
+ "lazy_static 1.4.0",
+]
+
+[[package]]
+name = "css-color-parser"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ccb6ce7ef97e6dc6e575e51b596c9889a5cc88a307b5ef177d215c61fd7581d"
+dependencies = [
+ "lazy_static 0.1.16",
]
[[package]]
@@ -693,7 +717,7 @@ dependencies = [
"fnv",
"gl_generator",
"glutin",
- "lazy_static",
+ "lazy_static 1.4.0",
"memoffset",
"smallvec",
"takeable-option",
@@ -714,7 +738,7 @@ dependencies = [
"glutin_gles2_sys",
"glutin_glx_sys",
"glutin_wgl_sys",
- "lazy_static",
+ "lazy_static 1.4.0",
"libloading",
"log",
"objc",
@@ -794,21 +818,20 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "imgui"
-version = "0.6.1"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92a0077d3bb882960467aed0bc6eaf5d4033cb9b61bfdbb99c32d1288380032f"
+checksum = "24cfcf6e3326886321c5d637caf1ce217006651059015fae372b1c49c0e722b2"
dependencies = [
"bitflags",
- "glium",
"imgui-sys",
"parking_lot 0.11.1",
]
[[package]]
name = "imgui-glium-renderer"
-version = "0.6.1"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e0755707343b50c7710ea5ededb7c955d2bceff7d38b515f7e6e0c7eeca04ca"
+checksum = "8c295c510c0d7209cf2304741808425bcd8e421142679d2448a38d4aa51762f3"
dependencies = [
"glium",
"imgui",
@@ -816,18 +839,19 @@ dependencies = [
[[package]]
name = "imgui-sys"
-version = "0.6.0"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0522b693da8a64322afbb32c63c04f39d9b9435cc75199d630207eee48886fc1"
+checksum = "85ca00be6b78bf02b57e91468cf19d08dfcc11d0fb3c2f3dc491c29404d8d330"
dependencies = [
"cc",
+ "chlorine",
]
[[package]]
name = "imgui-winit-support"
-version = "0.6.1"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0669ee7f52b80aa33d5f45507790223380c253a8fd981ddf67d1427ab8ebc8bc"
+checksum = "d632440e05c964e8a7f00f2659c4f71c97897d8c38a77a0c2dc1f3fe8d632208"
dependencies = [
"imgui",
"winit",
@@ -930,6 +954,12 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+[[package]]
+name = "lazy_static"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
+
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -974,7 +1004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9109e19fbfac3458f2970189719fa19f1007c6fd4e08c44fdebf4be0ddbe261d"
dependencies = [
"cc",
- "lazy_static",
+ "lazy_static 1.4.0",
"libc",
"regex",
"semver",
@@ -1110,36 +1140,40 @@ dependencies = [
[[package]]
name = "ndcell"
-version = "0.2.0-dev"
+version = "0.2.0"
dependencies = [
"anyhow",
"cgmath",
"clipboard",
"colorous",
+ "css-color-parser",
"enum_dispatch",
"glium",
"imgui",
"imgui-glium-renderer",
"imgui-winit-support",
"itertools 0.10.0",
- "lazy_static",
+ "lazy_static 1.4.0",
"log",
"mimalloc",
"ndcell_core",
"ndcell_lang",
+ "nfd2",
+ "palette",
"parking_lot 0.11.1",
"proptest",
"send_wrapper",
"simple_logger",
+ "sloth",
]
[[package]]
name = "ndcell_core"
-version = "0.2.0-dev"
+version = "0.2.0"
dependencies = [
"criterion",
"itertools 0.10.0",
- "lazy_static",
+ "lazy_static 1.4.0",
"noisy_float",
"num",
"parking_lot 0.11.1",
@@ -1155,7 +1189,7 @@ version = "0.1.0"
dependencies = [
"inkwell",
"itertools 0.10.0",
- "lazy_static",
+ "lazy_static 1.4.0",
"ndcell_core",
"proptest",
"regex",
@@ -1182,7 +1216,7 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241"
dependencies = [
- "lazy_static",
+ "lazy_static 1.4.0",
"libc",
"log",
"ndk",
@@ -1220,6 +1254,15 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "nfd2"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cedbdc1ecacd67f890b05bf38d268cf42dd070a4b44e82b89c4757998e7250d5"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "nix"
version = "0.18.0"
@@ -1424,6 +1467,30 @@ dependencies = [
"ttf-parser",
]
+[[package]]
+name = "palette"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a05c0334468e62a4dfbda34b29110aa7d70d58c7fdb2c9857b5874dd9827cc59"
+dependencies = [
+ "approx 0.3.2",
+ "num-traits",
+ "palette_derive",
+ "phf",
+ "phf_codegen",
+]
+
+[[package]]
+name = "palette_derive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b4b5f600e60dd3a147fb57b4547033d382d1979eb087af310e91cb45a63b1f4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "parking_lot"
version = "0.10.2"
@@ -1479,6 +1546,44 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+[[package]]
+name = "phf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+dependencies = [
+ "phf_shared",
+ "rand 0.7.3",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+dependencies = [
+ "siphasher",
+]
+
[[package]]
name = "pkg-config"
version = "0.3.19"
@@ -1530,7 +1635,7 @@ dependencies = [
"bit-set",
"bitflags",
"byteorder",
- "lazy_static",
+ "lazy_static 1.4.0",
"num-traits",
"quick-error",
"rand 0.7.3",
@@ -1567,6 +1672,7 @@ dependencies = [
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc 0.2.0",
+ "rand_pcg",
]
[[package]]
@@ -1637,6 +1743,15 @@ dependencies = [
"rand_core 0.6.1",
]
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
[[package]]
name = "rand_xorshift"
version = "0.2.0"
@@ -1676,7 +1791,7 @@ dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
- "lazy_static",
+ "lazy_static 1.4.0",
"num_cpus",
]
@@ -1857,7 +1972,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
dependencies = [
- "lazy_static",
+ "lazy_static 1.4.0",
"libc",
]
@@ -1874,12 +1989,24 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "siphasher"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7"
+
[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
+[[package]]
+name = "sloth"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7c30017141f49ee0d60d30c378052d8cccee245f0ea2bb37492a26ac61a60f8"
+
[[package]]
name = "smallvec"
version = "1.6.1"
@@ -1896,7 +2023,7 @@ dependencies = [
"bitflags",
"calloop",
"dlib",
- "lazy_static",
+ "lazy_static 1.4.0",
"log",
"memmap2",
"nix",
@@ -1977,7 +2104,7 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447"
dependencies = [
- "lazy_static",
+ "lazy_static 1.4.0",
]
[[package]]
@@ -2082,7 +2209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
dependencies = [
"bumpalo",
- "lazy_static",
+ "lazy_static 1.4.0",
"log",
"proc-macro2",
"quote",
@@ -2198,7 +2325,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6793834e0c35d11fd96a97297abe03d37be627e1847da52e17d7e0e3b51cc099"
dependencies = [
"dlib",
- "lazy_static",
+ "lazy_static 1.4.0",
"pkg-config",
]
@@ -2268,7 +2395,7 @@ dependencies = [
"core-video-sys",
"dispatch",
"instant",
- "lazy_static",
+ "lazy_static 1.4.0",
"libc",
"log",
"mio",
@@ -2311,7 +2438,7 @@ version = "2.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8"
dependencies = [
- "lazy_static",
+ "lazy_static 1.4.0",
"libc",
"maybe-uninit",
"pkg-config",
diff --git a/TODO.md b/TODO.md
index f4b93799..830b9c5f 100644
--- a/TODO.md
+++ b/TODO.md
@@ -26,7 +26,7 @@
- [ ] Selecting
- [ ] Resize selection
- [ ] Move selection
- - [ ] Drawing
+ - [x] Drawing
- [ ] Improve UI
- [ ] Better error reporting
- [ ] RLE loading
diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md
index 211b51eb..34d476f6 100644
--- a/core/CHANGELOG.md
+++ b/core/CHANGELOG.md
@@ -4,13 +4,16 @@ All notable changes to `ndcell_core` will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [Unreleased]
+## [0.2.0] - 2020-02-21
### Added
+- `Layer` method `round_rect_with_base_pos()`
- `NdVec` methods `min_component()` and `max_component()`
- `NdRect` method `span_rects()`
- Re-export of `num` modules `cast`, `iter`, and `pow` from `crate::num`
+- Implementation of `std::error::Error` for `CaFormatError`
+- Implementation of `std::iter::{Product, Sum}` for `NdVec`
### Changed
diff --git a/core/Cargo.toml b/core/Cargo.toml
index b28c9b7c..ac9cf36c 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ndcell_core"
-version = "0.2.0-dev"
+version = "0.2.0"
authors = ["HactarCE <6060305+HactarCE@users.noreply.github.com>"]
edition = "2018"
diff --git a/core/src/io/mod.rs b/core/src/io/mod.rs
index e6efc437..4671748f 100644
--- a/core/src/io/mod.rs
+++ b/core/src/io/mod.rs
@@ -41,6 +41,7 @@ pub enum CaFormatError {
RleError(RleError),
MacrocellError(MacrocellError),
}
+impl std::error::Error for CaFormatError {}
impl From for CaFormatError {
fn from(e: RleError) -> Self {
Self::RleError(e)
diff --git a/core/src/ndrect/mod.rs b/core/src/ndrect/mod.rs
index f3bdbe45..bb3a30a0 100644
--- a/core/src/ndrect/mod.rs
+++ b/core/src/ndrect/mod.rs
@@ -132,7 +132,7 @@ where
}
/// Creates an `NdRect` spanning both of the given rectangles (inclusive).
- pub fn span_rects(a: Self, b: Self) -> Self {
+ pub fn span_rects(a: &Self, b: &Self) -> Self {
Self::span(
NdVec::min(&a.min(), &b.min()),
NdVec::max(&a.max(), &b.max()),
diff --git a/core/src/ndtree/node/layer.rs b/core/src/ndtree/node/layer.rs
index dbd6471b..3bc398d7 100644
--- a/core/src/ndtree/node/layer.rs
+++ b/core/src/ndtree/node/layer.rs
@@ -269,6 +269,16 @@ impl Layer {
let len = self.big_len();
rect.div_outward(&len) * &len
}
+ /// Rounds the given rectangle outward to the next node boundaries at this
+ /// layer, given a global ND-tree base position.
+ #[inline]
+ pub fn round_rect_with_base_pos(
+ self,
+ rect: BigRect,
+ base_pos: &BigVec,
+ ) -> BigRect {
+ self.round_rect(&(rect - base_pos)) + base_pos
+ }
/// Returns the layer of the largest layer boundary a vector points to, or
/// `None` if it is the origin vector (largest aligned layer would be
diff --git a/core/src/ndtree/node/raw.rs b/core/src/ndtree/node/raw.rs
index 43253542..a7fa049d 100644
--- a/core/src/ndtree/node/raw.rs
+++ b/core/src/ndtree/node/raw.rs
@@ -458,8 +458,14 @@ impl RawNode {
/// Automatically converts the argument to a `BigUint` if necessary.
fn set_pop_small(&self, pop: usize) {
let value_to_store = (pop << 1) | 1;
+ // Does `pop` fit inside 63 bits? If not, we'll need to convert to
+ // `BigUint`.
if value_to_store >> 1 == pop {
- self.population.compare_and_swap(0, (pop << 1) | 1, Relaxed);
+ // If this fails, we don't care. That just means some other thread
+ // computed the population before we did.
+ let _ = self
+ .population
+ .compare_exchange(0, (pop << 1) | 1, Relaxed, Relaxed);
} else {
self.set_pop_big(pop.into());
}
@@ -468,10 +474,11 @@ impl RawNode {
fn set_pop_big(&self, pop: BigUint) {
// Put it on the heap and leak it.
let new_pop_ptr = Box::into_raw(Box::new(pop));
- let old = self
+ if self
.population
- .compare_and_swap(0, new_pop_ptr as usize, Relaxed);
- if old != 0 {
+ .compare_exchange(0, new_pop_ptr as usize, Relaxed, Relaxed)
+ .is_err()
+ {
// The swap was not successful, so drop `pop_ptr` because the
// it's not in `self.population`.
unsafe { std::ptr::drop_in_place(new_pop_ptr) };
diff --git a/core/src/ndvec/ops/iter.rs b/core/src/ndvec/ops/iter.rs
new file mode 100644
index 00000000..1e4500db
--- /dev/null
+++ b/core/src/ndvec/ops/iter.rs
@@ -0,0 +1,31 @@
+//! Operations on iterators over `NdVec`s.
+
+use std::iter::{Product, Sum};
+
+use super::*;
+
+impl, N: NdVecNum, X> Product for NdVec
+where
+ NdVec: MulAssign,
+{
+ fn product>(iter: I) -> Self {
+ let mut ret = Self::origin();
+ for x in iter {
+ ret *= x;
+ }
+ ret
+ }
+}
+
+impl, N: NdVecNum, X> Sum for NdVec
+where
+ NdVec: AddAssign,
+{
+ fn sum>(iter: I) -> Self {
+ let mut ret = Self::origin();
+ for x in iter {
+ ret += x;
+ }
+ ret
+ }
+}
diff --git a/core/src/ndvec/ops/mod.rs b/core/src/ndvec/ops/mod.rs
index 0b6a946c..9be8dbea 100644
--- a/core/src/ndvec/ops/mod.rs
+++ b/core/src/ndvec/ops/mod.rs
@@ -9,4 +9,5 @@ mod cmp;
mod divmod;
mod float;
mod int;
+mod iter;
mod signed;
diff --git a/docs/conf.py b/docs/conf.py
index bfa17146..960dfdcc 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -20,7 +20,7 @@
# -- Project information -----------------------------------------------------
project = 'NDCell'
-copyright = '2020, HactarCE'
+copyright = '2021, HactarCE'
author = 'HactarCE'
diff --git a/docs/custom-rules/rationale.rst b/docs/custom-rules/rationale.rst
index dd20ea1b..e4303b89 100644
--- a/docs/custom-rules/rationale.rst
+++ b/docs/custom-rules/rationale.rst
@@ -1,10 +1,11 @@
-.. _rationale:
-
*********
Rationale
*********
-Most existing cellular automaton simulation programs that support custom rules either require them defined in verbose languages like C or Java or as a list of transitions, which is limiting and hard to read. A custom programming language provides the power of math, logic, and variables with utilities unique to cellular automata, like first-class support for symmetries and masked N-dimensional arrays.
+Why make a new programming language?
+====================================
+
+Most existing cellular automaton simulation programs that support custom rules either require them defined in existing programming languages like C or Java, which tend to be verbose, or as a list of transitions, which is limiting and hard to read. A custom programming language provides the power of math, logic, and variables with utilities unique to cellular automata, like first-class support for spatial symmetry and masked N-dimensional arrays.
The original drafts for NDCell used `Lua`__ for defining custom rules, but I decided to create a custom programming language for several reasons:
@@ -13,11 +14,23 @@ __ https://www.lua.org/
- **Speed** — Lua can be JIT-compiled, but it still has dynamic typing which costs speed compared to a statically typed JIT-compiled language.
- **Less bloat** — Though Lua is very small, it has many features that are not helpful in most cellular automata, such as tables, metatables, double-precision floating point numbers, etc.
- **Guarantees** — NDCA offers useful guarantees by disallowing dynamic memory allocation and nondeterministic behavior, which improve safety and reliability.
-- **Custom features** — Types like :ref:`vectors ` and :ref:`cell patterns ` are first-class in NDCA, and there is custom syntax for handling symmetries, groups of cell states, and numerous other features that would be awkward to emulate in a general-purpose programming language.
+- **Custom features** — Types like vectors and cell configurations are first-class in NDCA, and there is custom syntax for handling symmetries, sets, and numerous other features that would be awkward to emulate in a general-purpose programming language.
NDCA takes inspiration from the following sources:
- **Rust** — basic syntax and static typing with inference
- **GLSL** — first-class vectors and user code designed to run in parallel, processing a vast amount of data in small chunks
- **Golly rule tables** — symmetry, ``@directive`` syntax, and pattern matching
-- **Java** — `>>>` logical right-shift operator
+- **Java** — ``>>>`` logical right-shift operator
+
+.. _set-contents-rationale:
+
+Why are some collections with different contents considered different types?
+============================================================================
+
+Values of :data:`IntSet`, :data:`VecSet`, :data:`PatternFilter`, and :data:`String` with different contents are considered different types for the purpose of variable assignment. This enables greater performance and reliability for the following reasons:
+
+- Operations on these types could produce arbitrarily complex values, which would require `dynamic memory allocation`__ inside the transition function, which is complex and slow and introduces the possibility of memory leaks
+- Operations on these types such as iteration and pattern maching are computed at compile-time, which enables much better optimization
+
+__ https://en.wikipedia.org/wiki/Memory_management#HEAP
diff --git a/docs/custom-rules/spec.rst b/docs/custom-rules/spec.rst
index 6b9075a8..a80c7e23 100644
--- a/docs/custom-rules/spec.rst
+++ b/docs/custom-rules/spec.rst
@@ -12,13 +12,14 @@ __ https://en.wikipedia.org/wiki/Domain-specific_language
:titlesonly:
spec/types
+ spec/conversions
spec/syntax
spec/directives
spec/statements
spec/expressions
spec/operators
- spec/conversions
spec/variables
spec/constants
spec/functions
spec/methods
+ spec/errors
diff --git a/docs/custom-rules/spec/constants.rst b/docs/custom-rules/spec/constants.rst
index 006526df..69aa3f9c 100644
--- a/docs/custom-rules/spec/constants.rst
+++ b/docs/custom-rules/spec/constants.rst
@@ -4,26 +4,28 @@
Constants
*********
+Built-in constants
+==================
+
+These constants are automatically available in every program.
+
.. data:: NDIM
The number of dimensions in the simulation.
- NOTE: not yet implemented
-
+ :status: Not yet implemented
:type: integer
.. data:: FALSE
The value ``0``.
- NOTE: not yet implemented
-
+ :status: Not yet implemented
:type: integer
.. data:: TRUE
The value ``1``.
- NOTE: not yet implemented
-
+ :status: Not yet implemented
:type: integer
diff --git a/docs/custom-rules/spec/conversions.rst b/docs/custom-rules/spec/conversions.rst
index cafe17f4..b537feb6 100644
--- a/docs/custom-rules/spec/conversions.rst
+++ b/docs/custom-rules/spec/conversions.rst
@@ -1,19 +1,46 @@
+.. include::
+
.. _conversions:
***********
Conversions
***********
-.. include::
+Some types in NDCell are implicitly converted (coerced) to other types when used with various operators or passed to functions.
+
+.. _subtype-coercion:
+
+Subtype coercion
+================
+
+Some types are `subtypes`__ of other types; this is implemented by coercing the subtype to the supertype when necessary. For example, a :data:`Cell` is implicitly converted to a :data:`CellSet` when used where a :data:`CellSet` is required. Here are the rules for subtype coercion:
+
+__ https://en.wikipedia.org/wiki/Subtyping
+
+- The :data:`CellSet` coerced from a :data:`Cell` contains only that cell state
+- The :data:`CellSet` coerced from a :data:`Tag` contains all cell states with a :ref:`truthy ` value for that tag
+- The :data:`PatternFilter` coerced from a :data:`Pattern` accepts only that pattern
+
+.. _boolean-conversion:
+
+Boolean conversion
+==================
+
+Values of some types can be converted to a boolean, which is represented using an :data:`Int`. This can happen implicitly (when used in a place where a boolean is required) or explicitly (using :func:`bool`).
-Some types in NDCell are implicitly converted to other types when used with various operators or passed to functions.
+- An :data:`Int` is truthy if it is not equal to ``0``.
+- A :data:`Cell` is truthy if it is not equal to ``#0``.
+- A :data:`Vec` is truthy if any of its components is not equal to ``0``.
+- A :data:`Pattern` is truthy if any of its cells is not equal to ``#0``.
+
+"Truthy" values become :data:`TRUE` (``1``) and "falsey" values (anything not truthy) become :data:`FALSE` (``0``).
.. _vector-vector-conversion:
Vector to vector conversion
===========================
-A vectors of one length can be converted to a vector of a different length. This can happen implicitly (when used in a place where a vector of a different length is required) or explicitly (using :func:`vec`).
+A :data:`Vec` of one length can be converted to a :data:`Vec` of a different length. This can happen implicitly (when used in a place where a :data:`Vec` of a different length is required) or explicitly (using :func:`vec`).
- If the new length is shorter than the original length, the vector is truncated and extra components are removed.
@@ -28,27 +55,8 @@ A vectors of one length can be converted to a vector of a different length. This
Integer to vector conversion
============================
-An integer can be converted to a vector of any length. This can happen implicitly (when used in a place where a vector is required) or explicitly (using :func:`vec`).
+An :data:`Int` can be converted to a :data:`Vec` of any length. This can happen implicitly (when used in a place where a :data:`Vec` is required) or explicitly (using :func:`vec`).
A new vector is constructed with the value of the original integer for each component.
- Example: ``vec3(-5)`` |rarr| ``[-5, -5, -5]``
-
-.. _cell-to-cell-filter-conversion:
-
-Cell to cell filter conversion
-===============================
-
-.. _boolean-conversion:
-
-Boolean conversion
-==================
-
-Any :ref:`basic type ` can be converted to a boolean, which is represented using an :ref:`integer `. This can happen implicitly (when used in a place where a boolean is required) or explicitly (using :func:`bool`).
-
-- An integer is truthy if it is not equal to ``0``.
-- A vector is truthy if any of its components is not equal to ``0``.
-- A cell is truthy if it is not equal to ``#0``.
-- A pattern is truthy if any of its cells is not equal to ``#0``.
-
-"Truthy" values become :data:`TRUE` (``1``) and "falsey" values (anything not truthy) become :data:`FALSE` (``0``).
diff --git a/docs/custom-rules/spec/directives.rst b/docs/custom-rules/spec/directives.rst
index 6570e846..f5110ffd 100644
--- a/docs/custom-rules/spec/directives.rst
+++ b/docs/custom-rules/spec/directives.rst
@@ -13,9 +13,8 @@ Automaton directives
The number of states in the automaton.
- NOTE: ``@states`` will be made much more powerful in the future.
-
- :type: :data:`Integer`
+ :status: Partially implemented; more functionality planned
+ :type: :data:`Int`
:default value: ``2``
:examples:
@@ -28,7 +27,8 @@ Automaton directives
__ https://en.wikipedia.org/wiki/Chebyshev_distance
- :type: :data:`Integer` (nonnegative)
+ :status: Fully implemented
+ :type: :data:`Int` (nonnegative)
:default value: ``1``
:examples:
@@ -40,6 +40,7 @@ Automaton directives
TODO: describe special variables ``this``, ``neighborhood``, and ``nbhd``.
+ :status: Fully implemented
:type: :ref:`Code block `
:default value: ``{ remain }``
:examples: See :ref:`examples`
@@ -50,9 +51,8 @@ Automaton directives
__ https://www.conwaylife.com/wiki/Run_Length_Encoded
- NOTE: not yet implemented, and may be removed
-
- :type: :ref:`String literal `
+ :status: Not yet implemented; may be removed
+ :type: :data:`String`
:default value: None
:examples:
@@ -65,9 +65,8 @@ Metadata directives
The name of the automaton.
- NOTE: not yet implemented
-
- :type: :ref:`String literal `
+ :status: Not yet implemented
+ :type: :data:`String`
:default value: Name of the file, excluding the extension.
:examples:
@@ -78,9 +77,8 @@ Metadata directives
The author(s) of the NDCA file.
- NOTE: not yet implemented
-
- :type: :ref:`String literal `
+ :status: Not yet implemented
+ :type: :data:`String`
:default value: ``"Unknown"``
:examples:
@@ -92,9 +90,8 @@ Metadata directives
The designer(s)/discoverer(s) of the automaton.
- NOTE: not yet implemented
-
- :type: :ref:`String literal `
+ :status: Not yet implemented
+ :type: :data:`String`
:default value: Same as :data:`@author`
:examples:
@@ -106,9 +103,8 @@ Metadata directives
The year that the automaton was designed/discovered.
- NOTE: not yet implemented
-
- :type: :ref:`String literal `
+ :status: Not yet implemented
+ :type: :data:`String`
:default value: ``"Unknown"``
:examples:
@@ -121,9 +117,8 @@ Metadata directives
__ https://www.conwaylife.com/wiki/Main_Page
- NOTE: not yet implemented
-
- :type: :ref:`String literal `
+ :status: Not yet implemented
+ :type: :data:`String`
:default value: ``"None"``
:examples:
diff --git a/docs/custom-rules/spec/errors.rst b/docs/custom-rules/spec/errors.rst
new file mode 100644
index 00000000..818413c5
--- /dev/null
+++ b/docs/custom-rules/spec/errors.rst
@@ -0,0 +1,9 @@
+.. _errors:
+
+******
+Errors
+******
+
+If an error occurs, the current operation is aborted and the program displays a message describing the error and where it occurred.
+
+To produce an error with a custom message, see :ref:`error-statement`.
diff --git a/docs/custom-rules/spec/expressions.rst b/docs/custom-rules/spec/expressions.rst
index fa2d3739..4456a10e 100644
--- a/docs/custom-rules/spec/expressions.rst
+++ b/docs/custom-rules/spec/expressions.rst
@@ -4,6 +4,10 @@
Expressions
***********
+.. note::
+
+ This page is under construction.
+
TODO: talk about assignable expressions and reword all this stuff
.. _assignable-expressions:
diff --git a/docs/custom-rules/spec/functions.rst b/docs/custom-rules/spec/functions.rst
index 8445c304..1287f89d 100644
--- a/docs/custom-rules/spec/functions.rst
+++ b/docs/custom-rules/spec/functions.rst
@@ -1,5 +1,3 @@
-.. _functions:
-
*********
Functions
*********
diff --git a/docs/custom-rules/spec/methods.rst b/docs/custom-rules/spec/methods.rst
index 0e49abde..51c888b4 100644
--- a/docs/custom-rules/spec/methods.rst
+++ b/docs/custom-rules/spec/methods.rst
@@ -1,26 +1,28 @@
-.. _methods:
-
*******
Methods
*******
-.. _integer-methods:
+.. note::
+
+ This page is under construction.
-Integer methods
+.. _int-methods:
+
+``Int`` methods
===============
TODO
-.. _vector-methods:
+.. _vec-methods:
-Vector methods
-==============
+``Vec`` methods
+===============
TODO
.. _cell-methods:
-Cell methods
-============
+``Cell`` methods
+================
TODO
diff --git a/docs/custom-rules/spec/operators.rst b/docs/custom-rules/spec/operators.rst
index 4a7a4b33..e995783e 100644
--- a/docs/custom-rules/spec/operators.rst
+++ b/docs/custom-rules/spec/operators.rst
@@ -1,9 +1,13 @@
-.. _operators:
+.. include::
*********
Operators
*********
+.. note::
+
+ This page is under construction.
+
Besides the ``is`` operator, both `arguments`__ to any binary (two-input) operator must be of the same type, or one type must be able to implicitly convert to the other. See :ref:`conversions` for more details. Some additional rules apply for operators:
__ https://en.wikipedia.org/wiki/Argument_of_a_function
@@ -26,31 +30,39 @@ Arithmetic operators
Given two values ``a`` and ``b``:
-- ``a + b`` — addition
-- ``a - b`` — subtraction
-- ``a * b`` — multiplication
-- ``a / b`` — division (rounds toward zero)
-- ``a % b`` — remainder
-- ``a ** b`` — exponentiation
-- ``-a`` — negation
-- ``+a`` — no operation
+- ``a + b`` — Addition
+- ``a - b`` — Subtraction
+- ``a * b`` — Multiplication
+- ``a / b`` — Division (rounds toward zero)
+- ``a % b`` — Remainder
+- ``a ** b`` — Exponentiation
+- ``-a`` — Negation
+- ``+a`` — No operation
NOTE: In the future, the behavior of ``/`` and ``%`` with negative numbers may be changed to emulate `floored or euclidean division/modulo`__.
__ https://en.wikipedia.org/wiki/Modulo_operation
-All of these operations can be applied to integers and vectors.
+All of these operations can be applied to integers and vectors. Vector operations are applied componentwise.
-If an operation is applied to two vectors of different lengths or applied to a vector and an integer, then both arguments are converted to the a vector of the same length:
+If an operation is applied to two vectors of different lengths, one vector is cast to the length of the other before the operation is applied. (See :ref:`vector-vector-conversion`.)
-- For ``+`` and ``-``, the length of the shorter vector is used.
--
+- For ``+`` and ``-``, the shorter vector is cast to the length of the **longer** one
+- For ``*``, ``/``, ``%``, and ``**``, the longer vector is cast to the length of the **shorter** one
-. (See [Conversions] for more details.) For ``+`` and ``-``, the shorter vector is extended; for ``*``, ``/``, ``%``, and ``**``, the longer vector is truncated.
+If an operation is applied to a vector and an integer, in either order, then the integer is converted to a vector of the same length before the operation is applied. (See :ref:`integer-vector-conversion`.)
-Overflow, underflow, or division by zero abort the simulation with an error.
+Overflow, underflow, division by zero, or exponentiation with a negative power cause an error. (See :ref:`errors`.)
-[vector lengths]: Types#vector-lengths
+Examples
+--------
+
+- ``2 ** 8`` |rarr| ``256``
+- ``[1, 2] + [10, 20, 30]`` |rarr| ``[11, 22, 30]``
+- ``[1, 2, 3] * [1, 2]`` |rarr| ``[1, 4]``
+- ``12 / [2, 3, 4, 5]`` |rarr| ``[6, 4, 3, 2]``
+- ``[4] % [2, 0, 1]`` |rarr| ``[0]``
+- ``4 % [2, 0, 1]`` causes a division-by-zero error
.. _bitwise-operators:
@@ -59,24 +71,24 @@ Bitwise operators
Given two values ``a`` and ``b``:
-- ``a & b`` — [bitwise AND]
-- ``a | b`` — [bitwise OR]
-- ``a ^ b`` — [bitwise XOR]
-- ``a >> b`` — [bitshift right (arithmetic/signed)][arithmetic shift]
-- ``a >>> b`` — [bitshift right (logical/unsigned)][logical shift]
-- ``a << b`` — [bitshift left][logical shift]
-- ``~a`` — [bitwise NOT]
+- ``a & b`` — `Bitwise AND `_
+- ``a | b`` — `Bitwise OR `_
+- ``a ^ b`` — `Bitwise XOR `_
+- ``a >> b`` — `Bitshift right (arithmetic/signed) `_
+- ``a >>> b`` — `Bitshift right (logical/unsigned) `_
+- ``a << b`` — `Bitshift left `_
+- ``~a`` — `Bitwise NOT `_
+
+All of these operations can be applied to integers and vectors. Vector operations are applied componentwise.
-[bitwise AND]: https://en.wikipedia.org/wiki/Bitwise_operation#AND
-[bitwise OR]: https://en.wikipedia.org/wiki/Bitwise_operation#OR
-[bitwise XOR]: https://en.wikipedia.org/wiki/Bitwise_operation#XOR
-[arithmetic shift]: https://en.wikipedia.org/wiki/Arithmetic_shift
-[logical shift]: https://en.wikipedia.org/wiki/Logical_shift
-[bitwise NOT]: https://en.wikipedia.org/wiki/Bitwise_operation#NOT
+If an operation is applied to two vectors of different lengths, one vector is cast to the length of the other before the operation is applied. (See :ref:`vector-vector-conversion`.)
-All of these operations are defined for integers and vectors.
+- For ``|``, ``^``, ``>>``, ``<<<``, and ``<<``, the shorter vector is cast to the length of the **longer** one
+- For ``&``, the longer vector is cast to the length of the **shorter** one
-Bitshifting by less than 0 or more than 64 aborts the simulation with an error.
+If an operation is applied to a vector and an integer (in either order), then the integer is converted to a vector of the same length. (See :ref:`integer-vector-conversion`.)
+
+Bitshifting by less than 0 or more than 64 causes an error. (See :ref:`errors`.)
.. _set-operators:
@@ -85,23 +97,20 @@ Set operators
Given two values ``a`` and ``b``:
-- ``a & b`` — [intersection]
-- ``a | b`` — [union]
-- ``a ^ b`` — [symmetric difference]
-- ``~a`` — [complement]
-
-[intersection]: https://en.wikipedia.org/wiki/Intersection_(set_theory)
-[union]: https://en.wikipedia.org/wiki/Union_(set_theory)
-[symmetric difference]: https://en.wikipedia.org/wiki/Symmetric_difference
-[complement]: https://en.wikipedia.org/wiki/Complement_(set_theory)
+- ``a & b`` — `Intersection `_
+- ``a | b`` — `Union `_
+- ``a ^ b`` — `Symmetric difference `_
+- ``a &~ b`` — `Relative complement of B in A `_
-Set operations are defined for cell filters. If one of these operators is applied to a cell, it is automatically converted to a cell filter that matches only that cell.
+All of these operations can be applied to any :ref:`set/filter type ` or its subtypes.
.. _comparison-operators:
Comparison operators
====================
+Given two values ``a`` and ``b``:
+
- ``a == b`` — Does ``a`` equal ``b``?
- ``a != b`` — Does ``a`` not equal ``b``?
- ``a < b`` — Is ``a`` less than ``b``?
@@ -109,22 +118,29 @@ Comparison operators
- ``a <= b`` — Is ``a`` less than or equal to ``b``?
- ``a >= b`` — Is ``a`` greater than or equal to ``b``?
-All of these operations are defined for integers and vectors. ``==`` and ``!=`` are defined for cell states and patterns.
+All of these comparisons can be applied to integers and vectors. ``==`` and ``!=`` can be applied to cell states and patterns. Vector comparisons are applied componentwise.
+
+If a comparison is applied to two vectors of different lengths, the shorter vector is to the length of the longer one before the operation is applied. (See :ref:`vector-vector-conversion`.)
+
+If a comparison is applied to a vector and an integer (in either order), then the integer is converted to a vector of the same length. (See :ref:`integer-vector-conversion`.)
+
+A ``!=`` comparison between two vectors results in :data:`TRUE` if **any** corresponding components of the two vectors are unequal. All other comparisons between two vectors result in :data:`TRUE` if only if the comparison is :data:`TRUE` for **all** corresponding components of the two vectors. Examples:
+
+- ``[1, 2] == [1, 2, 0]`` |rarr| :data:`TRUE`
+- ``[1, 2] == [1, 2, 3]`` |rarr| :data:`FALSE` because ``0 == 3`` |rarr| :data:`FALSE`
+- ``[1, 2] != [1, 2, 3]`` |rarr| :data:`TRUE` because ``0 != 3`` |rarr| :data:`TRUE`
+- ``[-1, 2] < [0, 4]`` |rarr| :data:`TRUE` because ``-1 < 0`` |rarr| :data:`TRUE` and ``2 < 4`` |rarr| :data:`TRUE`
+- ``[-1, 2] < [0, 1]`` |rarr| :data:`FALSE` because ``2 < 1`` |rarr| :data:`FALSE`
.. _boolean-operators:
Boolean operators
=================
-- ``a and b`` — [logical AND]
-- ``a or b`` — [logical OR]
-- ``a xor b`` — [logical XOR]
-- ``not a`` — [logical NOT]
-
-[logical AND]: https://en.wikipedia.org/wiki/AND_gate
-[logical OR]: https://en.wikipedia.org/wiki/OR_gate
-[logical XOR]: https://en.wikipedia.org/wiki/XOR_gate
-[logical NOT]: https://en.wikipedia.org/wiki/Inverter_(logic_gate)
+- ``a and b`` — `Logical AND `_
+- ``a or b`` — `Logical OR `_
+- ``a xor b`` — `Logical XOR `_
+- ``not a`` — `Logical NOT `_
.. _range-operator:
@@ -133,18 +149,20 @@ Range operator
- ``a..b``
-.. _indexing:
+.. _vector-indexing:
+
+Vector indexing
+===============
-Indexing
-========
+- ``v[n]``
-- ``a[b]``
+Indexing a vector ``v`` by an integer ``n`` results in the ``n``-th component of ``v``. The X component has index ``0``, the Y component has index ``1``, etc. Indexing with a value ``n`` that is not between ``0`` and ``v.len - 1`` (inclusive) causes an error. (See :ref:`errors`.)
.. _is-operator:
-Membership test
-===============
+Filter test
+===========
- ``a is b``
-This operator takes a basic type for ``a`` and the corresponding filter type for ``b``. (Note that implicit type conversion rules apply)
+This operator takes a basic type for ``a`` and the corresponding filter type for ``b``.
diff --git a/docs/custom-rules/spec/statements.rst b/docs/custom-rules/spec/statements.rst
index 23a38aa3..35dfb529 100644
--- a/docs/custom-rules/spec/statements.rst
+++ b/docs/custom-rules/spec/statements.rst
@@ -4,6 +4,10 @@
Statements
**********
+.. note::
+
+ This page is under construction.
+
Several statements involve boolean conversion; for more details, see :ref:`boolean-conversion`.
.. contents::
@@ -177,7 +181,7 @@ Debugging
Error statement
---------------
-An error statement consists of the keyword ``error``, optionally followed by a :ref:`string literal ` specifying a custom error message. An error statement causes an error, which aborts the simulation.
+An error statement consists of the keyword ``error``, optionally followed by a :data:`String` specifying a custom error message. An error statement causes an error, which aborts the simulation.
Examples:
@@ -191,7 +195,7 @@ Examples:
Assert statement
----------------
-An assert statement consists of the keyword ``assert``, followed by an :ref:`expression `, and then an optional comma and :ref:`string literal ` specifying a custom error message. The expression must be able to be converted to a boolean. An assert statement evaluates the expression and if the result is falsey it causes an error, which aborts the simulation.
+An assert statement consists of the keyword ``assert``, followed by an :ref:`expression `, and then an optional comma and :data:`String` specifying a custom error message. The expression must be able to be converted to a boolean. An assert statement evaluates the expression and if the result is falsey it causes an error, which aborts the simulation.
Examples:
diff --git a/docs/custom-rules/spec/syntax.rst b/docs/custom-rules/spec/syntax.rst
index faca12f1..74864f09 100644
--- a/docs/custom-rules/spec/syntax.rst
+++ b/docs/custom-rules/spec/syntax.rst
@@ -1,9 +1,11 @@
-.. _syntax:
-
******
Syntax
******
+.. note::
+
+ This page is under construction.
+
Like Lua and most C-family programming languages:
- :ref:`Code blocks ` begin with ``{`` and end with ``}``
@@ -29,6 +31,7 @@ The file is split into tokens, where each token is one of the following:
- String beginning and ending with ``'`` (may contain any character except ``'``)
- Number with a decimal point, matching the `regex`_ ``-?\d?\.\d+`` (currently unused)
- Number without a decimal point, matching the `regex`_ ``-?\d+``
+- Keyword (see :ref:`keywords`)
- Identifier, matching the `regex`_ ``[A-Za-z_][A-Za-z_\d]*``
- Tag name, consisting of ``#`` followed immediately by an identifier (no space)
- Directive name, consisting of ``@`` followed immediately by an identifier (no space)
@@ -50,6 +53,46 @@ NOTE: string syntax may change in the future
NOTE: either document and use string prefix characters, or remove support for them from the lexer
+.. _keywords:
+
+Keywords
+========
+
+The following keywords are reserved, and cannot be used for identifiers:
+
+- ``and``
+- ``assert``
+- ``become``
+- ``bind``
+- ``bound``
+- ``break``
+- ``case``
+- ``colors``
+- ``continue``
+- ``else``
+- ``error``
+- ``for``
+- ``icons``
+- ``if``
+- ``in``
+- ``is``
+- ``match``
+- ``models``
+- ``not``
+- ``or``
+- ``remain``
+- ``return``
+- ``same``
+- ``static``
+- ``transition``
+- ``unless``
+- ``where``
+- ``while``
+- ``with``
+- ``xor``
+
+Some of these are currently used, and some are reserved for future use.
+
.. _file-syntax:
File structure
diff --git a/docs/custom-rules/spec/types.rst b/docs/custom-rules/spec/types.rst
index 0f93a6b2..ab757c2e 100644
--- a/docs/custom-rules/spec/types.rst
+++ b/docs/custom-rules/spec/types.rst
@@ -4,128 +4,162 @@
Types
*****
-Note that all types except :ref:`patterns ` have `value semantics`__, which means that modifying a value in one variable does not have an effect on any other variables.
+Overview
+========
-__ https://en.wikipedia.org/wiki/Value_semantics
+- There are two important primitive types: :data:`Int` and :data:`Cell`
+- Each of these two primitive types has a collection type: :data:`Vec` (collection of :data:`Int`) and :data:`Pattern` (collection of :data:`Cell`)
+- Each of these four types has a corresponding set/filter type: :data:`IntSet`, :data:`CellSet`, :data:`VecSet`, and :data:`PatternFilter`
-.. _basic-types:
+ - :data:`Cell` is a subtype [#f1]_ of :data:`CellSet`
+ - :data:`Pattern` is a subtype of :data:`PatternFilter`
-Basic types
-===========
+- There are two other primitive types: :data:`Tag` and :data:`String`
-.. _integer:
+ - :data:`Tag` is also a subtype of :data:`CellSet`
-.. data:: Integer
+.. [#f1] I.e. anywhere that a :data:`CellSet` is required, a :data:`Cell` is accepted as well. All operations on a :data:`CellSet` are also allowed on a :data:`Cell`. See https://en.wikipedia.org/wiki/Subtyping.
- :aliases: ``Int``
- :methods: :ref:`integer-methods`
+See :ref:`subtype-coercion` for more about subtypes.
- Integers are represented using 64-bit signed two's complement. This means the minimum value is ``-9223372036854775808`` and the maximum value is ``9223372036854775807``.
+See :ref:`variable-types` regarding how variables use the type system.
- Integers are also used for boolean values; ``0`` is "falsey" and any other number (generally ``1``) is "truthy."
+.. _primitive-types:
- Integers are written as a sequence of digits without a leading zero but with an optional ``+`` or ``-`` at the beginning. Examples: ``0``, ``-1``, ``42``, ``+6``, ``-32768``.
+Primitive types
+===============
- NOTE: In the future, hexadecimal and/or binary literals may be supported.
+.. data:: Int
-.. _vector:
+ :status: Fully implemented
+ :methods: :ref:`int-methods`
+ :operators: :ref:`arithmetic-operators`, :ref:`bitwise-operators`, :ref:`comparison-operators`
-.. data:: Vector
+ An integer, represented using a 64-bit signed two's complement integer. This means the minimum value is ``-9223372036854775808`` and the maximum value is ``9223372036854775807``.
- :ref:`vector-methods`
+ Boolean values are represented using integers. (See :ref:`boolean-conversion`.)
- A vector is a sequence of :ref:`integers ` of a fixed length. Each integer is a component of that vector, and the number of components is the length of that vector. Vectors of different lengths are different types. The length of a vector a must be between 1 and 256 (inclusive).
+ An integer literal consists of a sequence of digits without a leading zero but with an optional ``+`` or ``-`` at the beginning. Examples:
- The first component of a vector is the X component at index 0; the second is the Y component at index 1; etc.
+ - ``0``
+ - ``-1``
+ - ``42``
+ - ``+6``
+ - ``-32768``
- Vectors are written as a list of integers separated by commas surrounded by square brackets. For example, ``[3, -1, 0]`` is a vector of length ``3`` with X component ``3``, Y component ``-1``, Z component ``0``. Vectors can also be written using :func:`vec()` and its variants.
+.. data:: Cell
- Example vector types:
+ :status: Fully implemented
+ :methods: :ref:`cell-methods`
+ :operators: :ref:`set-operators`, :ref:`comparison-operators` (``==`` and ``!=`` only)
+ :subtype of: :data:`CellSet`
- - ``Vector1``
- - ``Vector3``
- - ``Vector256``
+ A cell state, represented using an 8-bit unsigned integer. This means the minimum value is ``0`` and the maximum value is ``255``, so an automaton cannot have more than 256 states. :data:`Cell` values are always within the range of valid cell states in a cellular automaton. For example, an automaton with 10 states has a maximum cell state ID of ``9``.
-### Vector arithmetic
+ :data:`Cell` is a subtype of :data:`CellSet`. When used in place of a :data:`CellSet`, a :data:`Cell` represents a set containing only the one cell state.
-Vectors support all the same arithmetic and bitwise operations as [integers][integer] by applying them componentwise. For example, `[1, 2, 3] + [10, 20, 30]` results in `[11, 22, 33]`.
+ A :data:`Cell` literal consists of the ``#`` operator followed by the cell state ID. Examples:
-For most operations, when an operation is applied between vectors of different lengths, the shorter vector is first extended using `0`. For example, `[1, 2] + [10, 20, 30]` results in `[11, 22, 30]`. For multiplication (`*`) and bitwise AND (`&`), however, the longer vector is truncated to the length of the shorter one, since the extra components would be zero anyway. So `[1, 2, 3] * [1, 2]` results in `[1, 4]`, **not** `[1, 4, 0]`.
+ - ``#0``
+ - ``#1``
+ - ``#42``
-### Vector comparisons
+ A :data:`Cell` literal may use an arbitrary integer expression for the cell state ID by surrounding the expression in parentheses. Examples:
-Vectors support all the same comparisons as [integers][integer], by applying them componentwise. When comparing vectors, the shorter vector is first extended using `0`. A comparison between vectors compares all components, and is true only if that comparison is true for all components. For example `[-1, 2] < [0, 4]` is true because `-1 < 0` and `2 < 4` are both true. `[-1, 2] < [0, 1]`, however, is false because `2 < 1` is false.
+ - ``#(my_variable)``
+ - ``#(x + 5)``
-.. _cell:
+.. data:: Tag
-.. data:: Cell
+ :status: Not yet implemented
- Cells are represented using unsigned 8-bit integers holding ID of the cell's state. This means the minimum value is ``0`` and the maximum value is ``255``, so an automaton cannot have more than 256 states. Cells values are always within the range of valid cell states in a cellular automaton. For example, an automaton with ``10`` states has a maximum cell state ID of ``9``.
+ This type's design is still a work in progress.
- Single cell states are written using the ``#`` operator followed by a number. Examples: ``#0``, ``#1``, ``#42``. To use the value of a variable or expression instead of a literal integer, surround the expression in parentheses: ``#(my_variable)`` or ``#(10 + 5)``.
+.. data:: String
-### Cell operations
+ :status: Partially implemented
-Cells are automatically converted to [cell filters][cell filter] when used with any set operator
+ Different :data:`String` values are different types, and therefore cannot be stored in the same variable. (See :ref:`set-contents-rationale`)
-### Cell comparisons
+ This type's design is still a work in progress.
-Cells support the comparison operators ``==`` and ``!=``, which compare the IDs.
+.. _collection-types:
-.. _pattern:
+Collection types
+================
-.. data:: Pattern
+.. data:: Vec
- TODO
+ :status: Fully implemented
+ :methods: :ref:`vec-methods`
+ :operators: :ref:`arithmetic-operators`, :ref:`bitwise-operators`, :ref:`comparison-operators`, :ref:`vector-indexing`
-.. _filter-types:
+ A vector, represented using a fixed-length array of :data:`Int` values. Each :data:`Int` value is a component of the :data:`Vec`, and the number of components is the length of the :data:`Vec`. The length of a :data:`Vec` must be between 1 and 256 (inclusive). :data:`Vec` values of different lengths are different types, and therefore cannot be stored in the same variable.
-Filter types
-============
+ The first component of a :data:`Vec` is the X component at index 0; the second is the Y component at index 1; etc.
-.. _range:
+ A :data:`Vec` literal consists of a list of integer expressions separated by commas surrounded by square brackets. Examples:
-.. data:: Range
+ - ``[3, -1, 0]`` is a :data:`Vec` of length ``3`` with X component ``3``, Y component ``-1``, Z component ``0``
+ - ``[6]`` is a :data:`Vec` of length ``1`` with X component ``6``
+ - ``[a, b]`` is a :data:`Vec` of length ``2`` with X compoment ``a`` and Y component ``b``, given ``a`` and ``b`` are integers
- TODO
+ A :data:`Vec` literal may contain other vectors, which are concatenated to produce the result. Examples:
-.. _rectangle:
+ - ``[v1, -3, v2]`` is a :data:`Vec` constructed by concatenating ``v1``, ``[-3]``, and ``v2``
-.. data:: Rectangle
+ A :data:`Vec` can also be constructed using :func:`vec()` and its variants.
- TODO
+.. data:: Pattern
-.. _cell-filter:
+ :status: Partially implemented
-.. data:: Cell filter
+ A configuration of cells. Patterns with different shapes are different types.
- TODO
+.. _filter-types:
-.. _pattern-filter:
+Set/filter types
+================
-.. data:: Pattern filter
+.. data:: IntSet
- TODO
+ :status: Implementation in progress
+ :operators: :ref:`set-operators`
-.. _other-types:
+ A finite set of :data:`Int`. Different :data:`IntSet` values are different types, and therefore cannot be stored in the same variable. (See :ref:`set-contents-rationale`)
-Other types
-===========
+ An :data:`IntSet` literal consists of a comma-separated list of :data:`Int` or :data:`IntSet` surrounded by curly braces. Examples:
-.. _tag:
+ - ``{}`` constructs the empty set, containing no integers
+ - ``{42}`` constructs a set containing only the integer 42
+ - ``{1, 2, 3, 4}`` constructs a set containing the integers 1, 2, 3, and 4
+ - ``{1, 2, 3, 4,}`` is also allowed (but discouraged unless spanning multiple lines)
-.. data:: Tag
+ An :data:`IntSet` can also be constructed using a range literal consisting of two integers separated by ``..``. Examples:
- TODO
+ - ``1..5`` is equivalent to ``{1, 2, 3, 4, 5}``
+ - ``-3..+3`` contains all integers from -3 to 3 (inclusive)
+ - ``{-4..-1, 1..99}`` contains all integers from -4 to 99 (inclusive) *except* 0
-.. _string:
+.. data:: VecSet
-.. data:: String
+ :status: Implementation in progress
+ :operators: :ref:`set-operators`
+
+ A finite set of :data:`Vec`, all with the same length. Different :data:`VecSet` values are different types, and therefore cannot be stored in the same variable. (See :ref:`set-contents-rationale`)
+
+.. data:: CellSet
+
+ :status: Partially implemented
+
+ A set of cell states. Unlike :data:`IntSet` and :data:`VecSet`, all :data:`CellSet` values are the same type.
+
+ This type's design is still a work in progress.
- Strings cannot be stored in variables.
+.. data:: PatternFilter
-.. _void:
+ :status: Not yet implemented
-.. data:: Void
+ Different :data:`PatternFilter` values are different types, and therefore cannot be stored in the same variable. (See :ref:`set-contents-rationale`)
- The void type is an implementation detail that will probably be removed in a future version. Ignore it for now.
+ This type's design is still a work in progress.
diff --git a/docs/custom-rules/spec/variables.rst b/docs/custom-rules/spec/variables.rst
index 64a6abe2..76c21cec 100644
--- a/docs/custom-rules/spec/variables.rst
+++ b/docs/custom-rules/spec/variables.rst
@@ -4,7 +4,18 @@
Variables
*********
-NDCA is statically typed, meaning that a given variable can only store one type of value, however all variable types are inferred.
+.. note::
+
+ This page is under construction.
+
+.. _variable-types:
+
+Variable typing
+===============
+
+NDCA is `statically typed`__, which means that each variable has one :ref:`type ` of value it can store. Variable types are automatically inferred based on the values assigned to them.
+
+__ https://en.wikipedia.org/wiki/Type_system#Static_type_checking
.. _variable-names:
@@ -17,3 +28,33 @@ TODO: list reserved words here
Variable assignment
===================
+
+Semantics
+=========
+
+Variables use `value semantics`__, which means that modifying a value in one variable does not have an effect on any other variables.
+
+__ https://en.wikipedia.org/wiki/Value_semantics
+
+Built-in variables
+==================
+
+These variables are automatically available in every program.
+
+.. data:: this
+
+ The state of the current cell being updated. This variable is immutable.
+
+ :type: :data:`Cell`
+
+.. data:: nbhd
+
+ Alias for :data:`neighborhood`.
+
+ :type: :data:`Pattern`
+
+.. data:: neighborhood
+
+ The pattern of cells surrounding the current cell. This variable is immutable.
+
+ :type: :data:`Pattern`
diff --git a/docs/formats.rst b/docs/formats.rst
index 9ce00f68..930f06ae 100644
--- a/docs/formats.rst
+++ b/docs/formats.rst
@@ -29,7 +29,7 @@ Conventionally Y coordinates increase downwards in an RLE; in NDCell, however, Y
N-dimensional Macrocell format (NDMC)
=====================================
-For interchanging large patterns, NDCell uses a Macrocell format that is mostly backwards-compatible with Golly's `Macrocell format`__. While Golly has a variant for some two-state algorithms, NDCell always uses the generic format that supports any number of states (though it is able to parse the two-state variant). It introduces the following new features:
+For interchanging large patterns, NDCell uses a Macrocell format that is mostly backwards-compatible with Golly's `Macrocell format`__. While Golly has a variant for some two-state algorithms, NDCell always exports using the generic format that supports any number of states (though it is able to import the two-state variant). It introduces the following new features:
__ http://golly.sourceforge.net/Help/formats.html#mc
diff --git a/docs/index.rst b/docs/index.rst
index a6c946cd..f604b4ef 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -7,12 +7,23 @@ NDCell is an N-dimensional `cellular automaton`__ simulator written in Rust. You
__ https://en.wikipedia.org/wiki/Cellular_automaton
__ https://www.github.com/HactarCE/NDCell
+..
+ TODO add these to root toctree once they are ready:
+ Getting started
+ custom-rules/examples
+ also figure out why examples are not rendering?
+
.. toctree::
:maxdepth: 2
:caption: Custom rules
:hidden:
- Getting started
- custom-rules/examples
Specification
custom-rules/rationale
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Other
+ :hidden:
+
+ formats
diff --git a/examples/ed-rep.ndca b/examples/ed-rep.ndca
new file mode 100644
index 00000000..8858d829
--- /dev/null
+++ b/examples/ed-rep.ndca
@@ -0,0 +1,5 @@
+@transition {
+ ret = (this == #1)
+ for cell in nbhd { ret += (cell == #1) }
+ become #(ret & 7)
+}
diff --git a/examples/ed-rep.rle b/examples/ed-rep.rle
new file mode 100644
index 00000000..232b44ea
--- /dev/null
+++ b/examples/ed-rep.rle
@@ -0,0 +1,24 @@
+x = 49, y = 49, rule = Ed-rep
+ABA2B2ADE17F4E11D4B4A$2A2B2AB23FE10D2B3AB2A$4B2AD24F3E7DB4AB2A$3B3AE
+24F3E7DB7A$3B2AB25F7E3DCB6A$3B2AC3FE17F6E2D2BC3DCB4ABA$3B2AC3FE19F4D
+5B2DED2B3A2B$2B3AD12FE9FD3B4A3BD2EDB4AB$2B2ABE2F3E7FE5F3EDBA2BA4B3CDE
+D3B2AB$BC2ABD3FE7F2E5F3DB2D2BC5D2C2DC3B3A$2BA2BD10FE2D3F2E6DC10D3C3B
+2A$2BA2BC7F6DEF3E5DC3D2E7DBCD3BA$2BA2BC3F2DC3BC3DEFE5DC3DE2FE4D2EDBCD
+2CBA$BCA3BEFDB3AB3D5E3D3BC3DB7AB2DCD2C2B$BCA3BDFD2B2C7D4ED4BDB6A4BC5D
+CB$BCB2ABCF3D2E7DE3FD5B3AB2C11D2B$B2C3ABF8E5D3FE4B2DCDEFE6DE4D2B$ABCB
+3A2FDF2E3FE2D2B3FD3BD2E3D2E2DB2D3E2DEDB$A3B3AEF3E2FDBA3BD3FEC8BC5D4E
+4DB$2ABCB2AEF2EFC6ABE3FD2B10D3E7DEC$CABC2BAEF2E4A2B2CE4FE4D3E2FE4FEDE
+3D3ED$DABC2BADFEDAD3FEB2D4FE2DBD3EFEF7E3D4E$BA2BCBAD2FBD3FD2B2D4F2E5D
+EF6E6DE2DE$6BAC2FD2E2DBC2E5F2DED3BD3F4E5D2E2DE$D3BC2BD2F2E4DE7F3EFC2A
+BD2F4E5D3EDE$DA2BC2BD3F5E8FE2DEF2B2A13DEDE$C3B2DCB11FD5F3DF2DB2AB13DE
+$2C2BCEDC10F2D5F3D2F6B11D2E$C2BCB2E2D9FBD4F3DB2D6B9DC3D$2B3C2E2C8FDB
+2FE2FE2DB2DBD4B9DC3D$B2C2B2EDBE6FDBA2FE2F4DB3A4BC8D2C2D$3BABC2EDE4F2E
+DAB5F3DB3A7BD2BC7D$A3B2AEF3E2FE3DAB8DBA6BC2B2DBDE6D$3B3AB7E2DBA3DBABD
+2BA6BDC2B2CBCE3DB2D$B3AB2C7E2D2BFD2E2DC6BC2DCD4BCDE6D$CBC2BECBCD5EDBD
+F2D2E2D3C8DB2AB9D$ECECBC3ACDF3EDBD6EDC7D3B3AB9D$CB4C2A3CF2E2DA2D2E2D
+2F3ED4B5AB2C5DC2D$2B2CECBCECBEFEDCB5EFE2D2BABDB5ACD3C4DC2D$2B4CB2CBAE
+FE2DBDED2FE4BDB6ABC8DCB2D$10BABFE7D3BC2DB4ABCB3C6D2CB2D$2B2A8B4E4DB8A
+CD4BDBC2DC4DBC2D$B2AC5BCD2EF3E2DBDECBA3BC5D3B8DBC2D$BABC5BCE2CFEDE2DB
+D2FEDED3F2DBC2BCD2C5DBC2D$C2BC3BC2BCAB2ED2E3DE2F2E5D4CB3C3DCDCBC2D$EB
+A2B6C2BD5E2D6E2DB2CDC3BC6D3B2C$B4A4B5C6ED5E8DC9DC2B2C$2AB2A3B3C9ED3E
+10DB9DC2B2C$2B3CE2CE4C2BDF3ED4E19DC2B2C!
diff --git a/examples/ed-rep_colors.txt b/examples/ed-rep_colors.txt
new file mode 100644
index 00000000..84685007
--- /dev/null
+++ b/examples/ed-rep_colors.txt
@@ -0,0 +1,6 @@
+rgb(56, 32, 16)
+rgb(104, 80, 56)
+rgb(120, 120, 72)
+rgb(168, 120, 104)
+rgb(200, 168, 120)
+rgb(216, 176, 168)
diff --git a/examples/empty.rle b/examples/empty.rle
new file mode 100644
index 00000000..bfbf89b2
--- /dev/null
+++ b/examples/empty.rle
@@ -0,0 +1 @@
+x=0,y=0
diff --git a/examples/wireworld_2d.ndca b/examples/wireworld_2d.ndca
new file mode 100644
index 00000000..ee09c4b2
--- /dev/null
+++ b/examples/wireworld_2d.ndca
@@ -0,0 +1,11 @@
+@states 4
+
+@transition {
+ if this is #3 {
+ sum = 0
+ for cell in nbhd { sum += (cell == #1) }
+ if 1 <= sum <= 2 { become #1 }
+ }
+ if this is #1 { become #2 }
+ if this is #2 { become #3 }
+}
diff --git a/examples/wireworld_3d.ndca b/examples/wireworld_3d.ndca
new file mode 100644
index 00000000..e1b2f366
--- /dev/null
+++ b/examples/wireworld_3d.ndca
@@ -0,0 +1,12 @@
+@states 4
+@ndim 3
+
+@transition {
+ if this is #3 {
+ sum = 0
+ for cell in nbhd { sum += (cell == #1) }
+ if 1 <= sum <= 2 { become #1 }
+ }
+ if this is #1 { become #2 }
+ if this is #2 { become #3 }
+}
diff --git a/examples/wireworld_colors.txt b/examples/wireworld_colors.txt
new file mode 100644
index 00000000..2fd6924b
--- /dev/null
+++ b/examples/wireworld_colors.txt
@@ -0,0 +1,3 @@
+#0080FF
+#fff
+#FF8000
diff --git a/examples/wireworld_xor_3d.rle b/examples/wireworld_xor_3d.rle
new file mode 100644
index 00000000..8eb0fcda
--- /dev/null
+++ b/examples/wireworld_xor_3d.rle
@@ -0,0 +1,7 @@
+#CXRLE Pos=-23,-2,-15
+x = 30, y = 3, z = 53
+2$5.23C/2$28.C/$24.C$29.C/23.4C2$10CBA4CBA6C5.C/23.C2.C$27.C$28.C/23.
+4C2$4CBA10CBA6C/$24.C14/2$5.23C/2$28.C/2$13CBACBA5C6.C/$23.C$23.C5.C/$
+23.C$23.6C/$23.C$23.C/2$10CBA4CBA5C18/2$20.7C/2$5.15C7.C/2$22.2C4.C/2$
+10CBA4CBA4C2.C4.C/2$23.4C2.C/2$23.C2.3C/2$23.4C/2$4CBA10CBA4C2.C/2$22.
+2C!
diff --git a/ui/Cargo.toml b/ui/Cargo.toml
index 2e3869ae..5636ba0a 100644
--- a/ui/Cargo.toml
+++ b/ui/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ndcell"
-version = "0.2.0-dev"
+version = "0.2.0"
authors = ["HactarCE <6060305+HactarCE@users.noreply.github.com>"]
edition = "2018"
@@ -11,19 +11,23 @@ anyhow = "1.0"
cgmath = "0.18"
colorous = "1.0"
clipboard = "0.5"
+css-color-parser = "0.1.2"
enum_dispatch = "0.3"
glium = "0.29"
itertools = "0.10"
-imgui = "0.6"
-imgui-glium-renderer = "0.6"
-imgui-winit-support = "0.6"
+imgui = "0.7"
+imgui-glium-renderer = "0.7"
+imgui-winit-support = "0.7"
lazy_static = "1.4"
log = "0.4"
mimalloc = { version = "*", default-features = false }
+nfd2 = "0.2"
+palette = "0.5"
parking_lot = "0.11"
# preferences = "1.1"
send_wrapper = "0.5"
simple_logger = "1.11"
+sloth = "0.2"
ndcell_core = { path = "../core" }
ndcell_lang = { path = "../lang" }
diff --git a/ui/src/colors.rs b/ui/src/colors.rs
index e65e07c0..abe1c7ff 100644
--- a/ui/src/colors.rs
+++ b/ui/src/colors.rs
@@ -1,29 +1,90 @@
-/// Color of the gridlines. This will be configurable in the future.
-pub const GRIDLINES: [f32; 4] = [0.25, 0.25, 0.25, 1.0];
+//! Color constants. All of these will be configurable in the future.
-/// Color given to the hovered cell when drawing. This will be configurable in
-/// the future.
-pub const HOVERED_DRAW: [f32; 4] = [0.0, 0.5, 1.0, 1.0];
+use palette::{Srgb, Srgba};
-/// Color given to the hovered cell when selecting. This will be configurable in
-/// the future.
-pub const HOVERED_SELECT: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
+/// Defines an `Rgb` value at compile time.
+macro_rules! rgb {
+ ($r:expr, $g:expr, $b:expr) => {
+ palette::rgb::Rgb {
+ red: $r,
+ green: $g,
+ blue: $b,
+ standard: std::marker::PhantomData,
+ }
+ };
+}
+/// Defines an `Rgba` value at compile time.
+macro_rules! rgba {
+ ($r:expr, $g:expr, $b:expr, $a:expr) => {
+ palette::Alpha {
+ color: rgb!($r, $g, $b),
+ alpha: $a,
+ }
+ };
+}
-/// Color given to the selection resize preview. This will be configurable in
-/// the future.
-pub const SELECTION_RESIZE: [f32; 4] = [0.0, 0.5, 1.0, 0.4];
+/// 2D background color.
+pub const BACKGROUND_2D: Srgb = rgb!(0.0, 0.0, 0.0);
+/// 3D background color.
+pub const BACKGROUND_3D: Srgb = rgb!(0.0, 0.0, 0.0);
-/// Color given to the selection. This will be configurable in the future.
-pub const SELECTION: [f32; 4] = [0.6, 0.6, 0.6, 0.8];
+/// Color of the gridlines.
+pub const GRIDLINES: Srgba = rgba!(0.25, 0.25, 0.25, 1.0);
-/// 2D cell background color. This will be configurable in the future.
-pub const BACKGROUND_2D: (f32, f32, f32, f32) = (0.0, 0.0, 0.0, 1.0);
+/// Crosshair opacity change.
+pub const CROSSHAIR_OPACITY: f32 = 0.2;
-/// 3D cell background color. This will be configurable in the future.
-pub const BACKGROUND_3D: (f32, f32, f32, f32) = (0.0, 0.0, 0.0, 1.0);
+pub mod hover {
+ use super::*;
-/// Color for dead cells. This will be configurable in the future.
-pub const DEAD: [u8; 4] = [0, 0, 0, 0];
+ /// Color of the hovered cell when drawing in `PLACE` mode.
+ pub const PLACE_FILL: Srgba = rgba!(0.2, 0.5, 0.2, 0.75);
+ /// Color of the outline around the hovered cell when drawing in `PLACE`
+ /// mode.
+ pub const PLACE_OUTLINE: Srgb = rgb!(0.4, 1.0, 0.4);
-/// Color for live cells. This will be configurable in the future.
-pub const LIVE: [u8; 4] = [255, 255, 255, 255];
+ /// Color of the hovered cell when drawing in `REPLACE` mode.
+ pub const REPLACE_FILL: Srgba = rgba!(0.0, 0.2, 0.5, 0.75);
+ /// Color of the outline around the hovered cell when drawing in `REPLACE`
+ /// mode.
+ pub const REPLACE_OUTLINE: Srgb = rgb!(0.0, 0.4, 1.0);
+
+ /// Color of the hovered cell when drawing in `ERASE` mode.
+ pub const ERASE_FILL: Srgba = rgba!(0.4, 0.1, 0.1, 0.75);
+ /// Color of the outline around the hovered cell when drawing in `ERASE`
+ /// mode.
+ pub const ERASE_OUTLINE: Srgb = rgb!(0.8, 0.2, 0.2);
+
+ /// Color of the hovered cell when selecting.
+ pub const SELECT_FILL: Srgba = rgba!(0.6, 0.6, 0.6, 0.75);
+ /// Color of the outline around the hovered cell when selecting.
+ pub const SELECT_OUTLINE: Srgb = rgb!(0.8, 0.8, 0.8);
+}
+
+pub mod selection {
+ use super::*;
+
+ /// Color given to the region selection.
+ pub const REGION_FILL: Srgba = rgba!(0.4, 0.6, 0.8, 0.25);
+ /// Color of the outline around the region selection.
+ pub const REGION_OUTLINE: Srgb = rgb!(0.4, 0.6, 0.8);
+
+ /// Color of the cell selection.
+ pub const CELLS_FILL: Srgba = rgba!(0.4, 0.6, 0.8, 0.0);
+ /// Color of the outline around the cell selection.
+ pub const CELLS_OUTLINE: Srgb = rgb!(0.4, 0.6, 0.8);
+
+ /// Color of the selection resize preview.
+ pub const RESIZE_FILL: Srgba = rgba!(0.8, 0.4, 0.0, 0.125);
+ /// Color of the outline around the selection resize preview.
+ pub const RESIZE_OUTLINE: Srgb = rgb!(0.8, 0.4, 0.0);
+}
+
+pub mod cells {
+ use super::*;
+
+ /// Color for dead cells.
+ pub const DEAD: Srgba = rgba!(0.0, 0.0, 0.0, 0.0);
+ /// Color for live cells.
+ pub const LIVE: Srgba = rgba!(1.0, 1.0, 1.0, 1.0);
+}
diff --git a/ui/src/commands.rs b/ui/src/commands.rs
index ff0889b8..8cadedf6 100644
--- a/ui/src/commands.rs
+++ b/ui/src/commands.rs
@@ -1,134 +1,399 @@
use cgmath::Deg;
+use log::error;
+use palette::{Srgb, Srgba};
use ndcell_core::prelude::*;
-use crate::Scale;
+use crate::mouse::MouseDisplayMode;
+use crate::{Direction, Face};
-macro_rules! impl_command_from {
- ( Command::$command_variant:ident($inner:ty) ) => {
- impl From<$inner> for Command {
- fn from(c: $inner) -> Self {
- Self::$command_variant(c)
- }
- }
- };
+/// Message sent to a `GridView` to enqueue a command.
+#[derive(Debug, Clone)]
+pub struct CmdMsg {
+ pub command: Cmd,
+ pub cursor_pos: Option,
+}
+impl> From for CmdMsg {
+ fn from(command: T) -> Self {
+ command.into().to_msg()
+ }
}
+/// Command issued to a `GridView`.
#[derive(Debug, Clone)]
-pub enum Command {
- Sim(SimCommand),
- History(HistoryCommand),
- View(ViewCommand),
- Draw(DrawCommand),
- Select(SelectCommand),
- GarbageCollect,
-
- ContinueDrag(FVec2D),
- StopDrag,
+pub enum Cmd {
+ /// Starts a drag command.
+ BeginDrag(DragCmd),
+ /// Continues a drag command with a new mousue cursor position. (hidden)
+ ContinueDrag,
+ /// Ends a drag command. (hidden)
+ EndDrag,
+ /// Cancels a drag command.
+ CancelDrag,
+ /// Cancels any operation.
Cancel,
-}
-#[derive(Debug, Clone)]
-pub enum SimCommand {
- Step(BigInt),
- StepStepSize,
+ /// Undoes one action.
+ Undo,
+ /// Redoes one action.
+ Redo,
+ /// Undoes to generation 0.
+ Reset,
+
+ /// Moves the viewpoint in 2D using relative coordinates.
+ Move2D(Move2D),
+ /// Moves the viewpoint in 3D using relative coordinates.
+ Move3D(Move3D),
+ /// Zooms in or out by a power of 2. (positive = in, negative = out)
+ Scale(f64),
+ /// Zooms in or out by a power of 2, keeping the position at the mouse
+ /// cursor invariant.
+ ScaleToCursor(f64),
+
+ /// Snaps the viewpoint to the nearest cell boundary.
+ SnapPos,
+ /// Snaps the viewpoint to the nearest power-of-2 scale factor.
+ SnapScale,
+ /// Snaps the viewpoint to the nearest power-of-2 scale factor, keeping the
+ /// position at the mouse cursor invariant.
+ SnapScaleToCursor,
+
+ /// Moves the viewpoint to the origin.
+ ResetView,
+ /// Moves the viewpoint to fit the pattern.
+ FitView,
+ /// Moves the viewpoint to cell at the mouse cursor.
+ FocusCursor,
+ /// Sets the cell state for drawing.
+ SetDrawState(u8),
+ /// Selects the next cell state for drawing.
+ NextDrawState { wrap: bool },
+ /// Selects the previous cell state for drawing.
+ PrevDrawState { wrap: bool },
+ /// Completes a drawing operation.
+ ConfirmDraw,
+ /// Cancels a drawing operation.
+ CancelDraw,
+
+ /// Selects the entire pattern.
+ SelectAll,
+ /// Deselects all cells.
+ Deselect,
+ /// Copies the selection to the clipboard.
+ CopySelection(CaFormat),
+ /// Pastes the clipboard contents as a selection.
+ PasteSelection,
+ /// Deletes the selection contents.
+ DeleteSelection,
+ /// Drops selected cells onto the pattern or deselects all cells.
+ CancelSelection,
+
+ /// Advances the simulation by a specific number of generations.
+ Step(usize),
+ /// Advances the simulation by the configured step size.
+ StepStepSize,
+ /// Starts running the simulation continuously.
StartRunning,
+ /// Stops running the simulation continuously.
StopRunning,
+ /// Toggles running the simulation continuously.
ToggleRunning,
-
+ /// Restarts the continuously-running simulation using the newly configured
+ /// step size value, if it was already running. (hidden)
UpdateStepSize,
+ /// Cancels any pending simulation requests.
+ CancelSim,
- Cancel,
+ /// Clears the HashLife cache to reduce memory usage.
+ ClearCache,
}
-impl_command_from!(Command::Sim(SimCommand));
+impl Cmd {
+ /// Returns the way to display the mouse cursor when this is the main
+ /// command for the selected tool.
+ pub fn mouse_display_mode(&self) -> MouseDisplayMode {
+ match self {
+ Self::BeginDrag(cmd) => cmd.mouse_display_mode(),
+
+ _ => MouseDisplayMode::Normal,
+ }
+ }
+
+ /// Creates a `CmdMsg` for this command including a mouse cursor position.
+ pub fn at(self, cursor_pos: FVec2D) -> CmdMsg {
+ CmdMsg {
+ command: self,
+ cursor_pos: Some(cursor_pos),
+ }
+ }
+ /// Creates a `CmdMsg` for this command with no mouse cursor position. This
+ /// is invalid for some commands, and will log a warning!
+ pub fn to_msg(self) -> CmdMsg {
+ match &self {
+ Cmd::BeginDrag(_)
+ | Cmd::ContinueDrag
+ | Cmd::ScaleToCursor(_)
+ | Cmd::SnapScaleToCursor
+ | Cmd::FocusCursor => {
+ error!(
+ ".to_msg() called on {:?}, which requires cursor position; use .at() instead",
+ self,
+ );
+ }
+ _ => (),
+ }
+ CmdMsg {
+ command: self,
+ cursor_pos: None,
+ }
+ }
+}
+
+/// Command issued to a `GridView` that starts a mouse drag.
#[derive(Debug, Clone)]
-pub enum HistoryCommand {
- Undo,
- Redo,
- UndoTo(BigInt),
+pub enum DragCmd {
+ /// Drag the viewpoint/camera.
+ View(DragViewCmd),
+
+ /// Draws freeform.
+ DrawFreeform(DrawMode),
+
+ /// Selects a new rectangle.
+ SelectNewRect,
+ /// Resizes the selection to the mouse cursor.
+ ResizeSelectionToCursor,
+ /// Resizes the selection in 2D along a cardinal or intercardinal direction.
+ /// (mouse target)
+ ResizeSelection2D(Direction),
+ /// Resizes the selection in 3D along a cardinal direction. (mouse target)
+ ResizeSelection3D(Face),
+ /// Moves the selection. (mouse target)
+ MoveSelection(Option),
+ /// Moves the selected cells. (mouse target)
+ MoveSelectedCells(Option),
+ /// Moves a copy of the selected cells. (mouse target)
+ CopySelectedCells(Option),
}
-impl From for Command {
- fn from(c: HistoryCommand) -> Self {
- Self::History(c)
+impl From for Cmd {
+ fn from(cmd: DragCmd) -> Self {
+ Cmd::BeginDrag(cmd)
}
}
+impl DragCmd {
+ /// Returns the way to display the mouse cursor when this is the main
+ /// command for the selected tool.
+ pub fn mouse_display_mode(&self) -> MouseDisplayMode {
+ match self {
+ Self::View(cmd) => cmd.mouse_display_mode(),
-#[derive(Debug, Clone)]
-pub enum ViewCommand {
- Drag(ViewDragCommand, FVec2D),
-
- GoTo2D {
- x: Option,
- y: Option,
- relative: bool,
- scaled: bool,
- },
- GoTo3D {
- x: Option,
- y: Option,
- z: Option,
- yaw: Option>,
- pitch: Option>,
- relative: bool,
- scaled: bool,
- },
- GoToScale(Scale),
-
- Scale {
- /// Base-2 logarithm of the relative scale factor.
- log2_factor: f64,
- /// Optional invariant position.
- invariant_pos: Option,
- },
-
- /// Snap viewpoint to the nearest integer cell position.
- SnapPos,
- /// Snap viewpoint to the nearest power-of-2 scale factor.
- SnapScale {
- /// Optional invariant position.
- invariant_pos: Option,
- },
+ Self::DrawFreeform(mode) => MouseDisplayMode::Draw(*mode),
- FitView,
+ Self::SelectNewRect => MouseDisplayMode::Select,
+ Self::ResizeSelectionToCursor => MouseDisplayMode::ResizeSelectionToCursor,
+ Self::ResizeSelection2D(direction) => MouseDisplayMode::ResizeSelectionEdge(*direction),
+ Self::ResizeSelection3D(face) => MouseDisplayMode::ResizeSelectionFace(*face),
+ Self::MoveSelection(_) | Self::MoveSelectedCells(_) | Self::CopySelectedCells(_) => {
+ MouseDisplayMode::Move
+ }
+ }
+ }
+ /// Returns `true` if this drag command always waits for the cursor to move
+ /// a certain threshold distance from the initial click before acting.
+ /// Returns `false` if the drag command only waits for that treshold when
+ /// another action is bound on click.
+ pub fn always_uses_movement_threshold(&self) -> bool {
+ match self {
+ Self::View(cmd) => cmd.always_uses_movement_threshold(),
+
+ Self::DrawFreeform(_) => false,
+
+ Self::SelectNewRect => true,
+ Self::ResizeSelectionToCursor => false,
+ Self::ResizeSelection2D(_) => true,
+ Self::ResizeSelection3D(_) => true,
+ Self::MoveSelection(_) => true,
+ Self::MoveSelectedCells(_) => true,
+ Self::CopySelectedCells(_) => true,
+ }
+ }
+
+ pub fn is_view_cmd(&self) -> bool {
+ match self {
+ DragCmd::View(_) => true,
+
+ DragCmd::DrawFreeform(_) => false,
+
+ DragCmd::SelectNewRect
+ | DragCmd::ResizeSelectionToCursor
+ | DragCmd::ResizeSelection2D(_)
+ | DragCmd::ResizeSelection3D(_)
+ | DragCmd::MoveSelection(_)
+ | DragCmd::MoveSelectedCells(_)
+ | DragCmd::CopySelectedCells(_) => false,
+ }
+ }
+ pub fn is_draw_cmd(&self) -> bool {
+ match self {
+ DragCmd::View(_) => false,
+
+ DragCmd::DrawFreeform(_) => true,
+
+ DragCmd::SelectNewRect
+ | DragCmd::ResizeSelectionToCursor
+ | DragCmd::ResizeSelection2D(_)
+ | DragCmd::ResizeSelection3D(_)
+ | DragCmd::MoveSelection(_)
+ | DragCmd::MoveSelectedCells(_)
+ | DragCmd::CopySelectedCells(_) => false,
+ }
+ }
+ pub fn is_select_cmd(&self) -> bool {
+ match self {
+ DragCmd::View(_) => false,
+
+ DragCmd::DrawFreeform(_) => false,
+
+ DragCmd::SelectNewRect
+ | DragCmd::ResizeSelectionToCursor
+ | DragCmd::ResizeSelection2D(_)
+ | DragCmd::ResizeSelection3D(_)
+ | DragCmd::MoveSelection(_)
+ | DragCmd::MoveSelectedCells(_)
+ | DragCmd::CopySelectedCells(_) => true,
+ }
+ }
}
-impl_command_from!(Command::View(ViewCommand));
#[derive(Debug, Copy, Clone)]
-pub enum ViewDragCommand {
- /// Rotates the 3D view around the viewpoint pivot.
- Orbit,
+pub enum DragViewCmd {
+ /// Rotates the 3D camera around the viewpoint pivot.
+ Orbit3D,
/// Pans in the plane of the camera.
Pan,
- /// Pans in the nearest axis-aligned plane.
- PanAligned,
- /// Pans in the nearest axis-aligned plane parallel to the Y axis.
- PanAlignedVertical,
/// Pans in the XZ plane.
- PanHorizontal,
- /// Adjusts scale.
+ PanHorizontal3D,
+ /// Zooms in or out.
Scale,
}
+impl From for DragCmd {
+ fn from(cmd: DragViewCmd) -> Self {
+ DragCmd::View(cmd)
+ }
+}
+impl From for Cmd {
+ fn from(cmd: DragViewCmd) -> Self {
+ Cmd::BeginDrag(DragCmd::View(cmd))
+ }
+}
+impl DragViewCmd {
+ /// Returns the way to display the mouse cursor when this is the main
+ /// command for the selected tool.
+ pub fn mouse_display_mode(&self) -> MouseDisplayMode {
+ match self {
+ Self::Orbit3D => MouseDisplayMode::Orbit,
+ Self::Pan | Self::PanHorizontal3D => MouseDisplayMode::Pan,
+ Self::Scale => MouseDisplayMode::Scale,
+ }
+ }
+ pub fn always_uses_movement_threshold(&self) -> bool {
+ false
+ }
+}
-#[derive(Debug, Clone)]
-pub enum DrawCommand {
- SetState(u8),
- Drag(DrawDragCommand, FVec2D),
-
- Confirm,
- Cancel,
+/// 2D movement relative to the camera.
+#[derive(Debug, Default, Copy, Clone)]
+pub struct Move2D {
+ pub dx: f64,
+ pub dy: f64,
+}
+impl Move2D {
+ pub fn right(n: f64) -> Self {
+ Self { dx: n, dy: 0.0 }
+ }
+ pub fn left(n: f64) -> Self {
+ Self { dx: -n, dy: 0.0 }
+ }
+ pub fn up(n: f64) -> Self {
+ Self { dx: 0.0, dy: n }
+ }
+ pub fn down(n: f64) -> Self {
+ Self { dx: 0.0, dy: -n }
+ }
+}
+impl From for Cmd {
+ fn from(move2d: Move2D) -> Self {
+ Cmd::Move2D(move2d)
+ }
}
-impl_command_from!(Command::Draw(DrawCommand));
+/// 3D movement relative to the camera.
#[derive(Debug, Copy, Clone)]
-pub struct DrawDragCommand {
- pub mode: DrawMode,
- pub shape: DrawShape,
+pub struct Move3D {
+ pub dx: f64,
+ pub dy: f64,
+ pub dz: f64,
+ pub dyaw: Deg,
+ pub dpitch: Deg,
+}
+impl Default for Move3D {
+ fn default() -> Self {
+ Self {
+ dx: 0.0,
+ dy: 0.0,
+ dz: 0.0,
+ dyaw: Deg(0.0),
+ dpitch: Deg(0.0),
+ }
+ }
+}
+impl From for Cmd {
+ fn from(move3d: Move3D) -> Self {
+ Cmd::Move3D(move3d)
+ }
+}
+impl Move3D {
+ pub fn east(n: f64) -> Self {
+ Self {
+ dx: n,
+ ..Default::default()
+ }
+ }
+ pub fn west(n: f64) -> Self {
+ Self {
+ dx: -n,
+ ..Default::default()
+ }
+ }
+ pub fn up(n: f64) -> Self {
+ Self {
+ dy: n,
+ ..Default::default()
+ }
+ }
+ pub fn down(n: f64) -> Self {
+ Self {
+ dy: -n,
+ ..Default::default()
+ }
+ }
+ pub fn north(n: f64) -> Self {
+ Self {
+ dz: n,
+ ..Default::default()
+ }
+ }
+ pub fn south(n: f64) -> Self {
+ Self {
+ dz: -n,
+ ..Default::default()
+ }
+ }
}
-#[derive(Debug, Copy, Clone)]
+/// How drawing should affect individual cells, depending on their prior state.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DrawMode {
/// Sets the state of a #0 cell to nonzero.
Place,
@@ -156,36 +421,19 @@ impl DrawMode {
DrawMode::Erase => 0_u8,
}
}
-}
-
-#[derive(Debug, Copy, Clone)]
-pub enum DrawShape {
- /// Modifies cells in a freeform path.
- Freeform,
- /// Modifies cells in a straight line.
- Line,
-}
-#[derive(Debug, Clone)]
-pub enum SelectCommand {
- Drag(SelectDragCommand, FVec2D),
- SelectAll,
- Deselect,
-
- Copy(CaFormat),
- Paste,
- Delete,
-
- Cancel,
-}
-impl_command_from!(Command::Select(SelectCommand));
-
-#[derive(Debug, Copy, Clone)]
-pub enum SelectDragCommand {
- NewRect,
- Resize { axes: AxisSet, plane: Option },
- ResizeToCell,
- MoveSelection,
- MoveCells,
- CopyCells,
+ pub fn fill_color(self) -> Srgba {
+ match self {
+ DrawMode::Place => crate::colors::hover::PLACE_FILL,
+ DrawMode::Replace => crate::colors::hover::REPLACE_FILL,
+ DrawMode::Erase => crate::colors::hover::ERASE_FILL,
+ }
+ }
+ pub fn outline_color(self) -> Srgb {
+ match self {
+ DrawMode::Place => crate::colors::hover::PLACE_OUTLINE,
+ DrawMode::Replace => crate::colors::hover::REPLACE_OUTLINE,
+ DrawMode::Erase => crate::colors::hover::ERASE_OUTLINE,
+ }
+ }
}
diff --git a/ui/src/config/ctrl.rs b/ui/src/config/ctrl.rs
index 3f25bb85..7e163bb4 100644
--- a/ui/src/config/ctrl.rs
+++ b/ui/src/config/ctrl.rs
@@ -6,8 +6,8 @@ pub struct CtrlConfig {
pub keybd_scale_speed_3d: f64,
pub discrete_scale_speed_2d: f64,
pub discrete_scale_speed_3d: f64,
- pub smooth_scroll_speed_2d: f64,
- pub smooth_scroll_speed_3d: f64,
+ pub pixels_per_2x_scale_2d: f64,
+ pub pixels_per_2x_scale_3d: f64,
pub mouse_orbit_speed: f64,
// TODO: make speed_modifier an attribute of the keybind
pub speed_modifier: f64,
@@ -34,8 +34,8 @@ impl Default for CtrlConfig {
keybd_scale_speed_3d: 2.0,
discrete_scale_speed_2d: 1.0,
discrete_scale_speed_3d: 0.5,
- smooth_scroll_speed_2d: 1.0,
- smooth_scroll_speed_3d: 0.5,
+ pixels_per_2x_scale_2d: 100.0,
+ pixels_per_2x_scale_3d: 200.0,
mouse_orbit_speed: 0.75,
speed_modifier: 3.0,
@@ -50,7 +50,7 @@ impl Default for CtrlConfig {
interpolation: Interpolation::default(),
- selection_resize_drag_target_width: 8.0,
+ selection_resize_drag_target_width: 12.0,
}
}
}
diff --git a/ui/src/config/gfx.rs b/ui/src/config/gfx.rs
index dcf49787..21aacefa 100644
--- a/ui/src/config/gfx.rs
+++ b/ui/src/config/gfx.rs
@@ -1,10 +1,16 @@
+use palette::Srgba;
+
#[derive(Debug)]
pub struct GfxConfig {
pub dpi: f64,
pub fps: f64,
pub font_size: f32,
+ pub msaa: Msaa,
+
pub octree_perf_view: bool,
+
+ pub cell_colors: [Srgba; 256],
}
impl Default for GfxConfig {
fn default() -> Self {
@@ -13,7 +19,11 @@ impl Default for GfxConfig {
fps: 60.0,
font_size: 16.0,
+ msaa: Msaa::_8,
+
octree_perf_view: false,
+
+ cell_colors: crate::default_colors(),
}
}
}
@@ -23,3 +33,11 @@ impl GfxConfig {
std::time::Duration::from_secs_f64(1.0 / self.fps)
}
}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Msaa {
+ Off = 0,
+ _2 = 2,
+ _4 = 4,
+ _8 = 8,
+}
diff --git a/ui/src/config/hist.rs b/ui/src/config/hist.rs
index 3dfb886d..d9d68e68 100644
--- a/ui/src/config/hist.rs
+++ b/ui/src/config/hist.rs
@@ -1,3 +1,5 @@
+use crate::commands::DragCmd;
+
#[derive(Debug)]
pub struct HistoryConfig {
pub record_move_cells: bool,
@@ -10,8 +12,24 @@ impl Default for HistoryConfig {
fn default() -> Self {
Self {
record_move_cells: true,
- record_select: false, // still restored when moving
+ record_select: false,
record_view: false,
}
}
}
+impl HistoryConfig {
+ pub fn should_record_history_for_drag_command(&self, command: &DragCmd) -> bool {
+ match command {
+ DragCmd::View(_) => false,
+
+ DragCmd::DrawFreeform(_) => true,
+
+ DragCmd::SelectNewRect
+ | DragCmd::ResizeSelectionToCursor
+ | DragCmd::ResizeSelection2D(_)
+ | DragCmd::ResizeSelection3D(_)
+ | DragCmd::MoveSelection(_) => self.record_select,
+ DragCmd::MoveSelectedCells(_) | DragCmd::CopySelectedCells(_) => self.record_move_cells,
+ }
+ }
+}
diff --git a/ui/src/config/mouse/mod.rs b/ui/src/config/mouse.rs
similarity index 58%
rename from ui/src/config/mouse/mod.rs
rename to ui/src/config/mouse.rs
index 28d299ce..0f794b47 100644
--- a/ui/src/config/mouse/mod.rs
+++ b/ui/src/config/mouse.rs
@@ -1,20 +1,42 @@
-use glium::glutin::event::{ModifiersState, MouseButton};
+use glium::glutin::event::{ModifiersState, MouseButton as GlutinMouseButton};
+use std::convert::{TryFrom, TryInto};
use std::ops::{Index, IndexMut};
-mod click;
-mod drag;
+use crate::commands::{Cmd, DragCmd, DragViewCmd, DrawMode};
-pub use click::*;
-pub use drag::*;
+/// Left, right, or middle mouse button.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum MouseButton {
+ Left,
+ Right,
+ Middle,
+}
+impl Default for MouseButton {
+ fn default() -> Self {
+ Self::Left
+ }
+}
+impl TryFrom for MouseButton {
+ type Error = OtherMouseButton;
-use crate::commands::{DrawDragCommand, DrawMode, DrawShape, SelectDragCommand, ViewDragCommand};
+ fn try_from(button: GlutinMouseButton) -> Result {
+ match button {
+ GlutinMouseButton::Left => Ok(Self::Left),
+ GlutinMouseButton::Right => Ok(Self::Right),
+ GlutinMouseButton::Middle => Ok(Self::Middle),
+ GlutinMouseButton::Other(n) => Err(OtherMouseButton(n)),
+ }
+ }
+}
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+struct OtherMouseButton(u16);
#[derive(Debug)]
pub struct MouseConfig {
- pub click_bindings_2d: MouseBindings