diff --git a/.github/workflows/rust_ci.yml b/.github/workflows/rust_ci.yml new file mode 100644 index 0000000..ee280ee --- /dev/null +++ b/.github/workflows/rust_ci.yml @@ -0,0 +1,130 @@ +# This CI (Continuous Integration) file will automatically check your Rust code for errors. +# It runs using GitHub Actions: https://docs.github.com/en/actions +# It will check the code compiles, run tests, run lints, and check for security issues. +# CI will help you standardise your code style and to detect issues with your code easily and early. +# It makes it easier to integrate different branches once they're finished. +# adapted from https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md and https://gist.github.com/LukeMathWalker/5ae1107432ce283310c3e601fac915f3 + +name: Rust CI + +on: + push: + # branches: + # - main + release: + types: [published] + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + name: Check code compiles + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Install apt dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: build-essential gettext git libcairo2-dev libgtk-4-dev meson ninja-build sassc valac + version: latest + - name: Clone libadwaita git repo + run: git clone https://gitlab.gnome.org/GNOME/libadwaita.git -b libadwaita-1-2 --depth=1 + - name: Build libadwaita with Meson + run: | + cd libadwaita + meson . _build -Dgtk_doc=false -Dtests=false -Dexamples=false + - name: Install libadwaita with Ninja + run: | + cd libadwaita + sudo ninja -C _build install + + - uses: dtolnay/rust-toolchain@nightly + - name: Run cargo check + run: cargo check + + test: + name: Run tests + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Install apt dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: build-essential gettext git libcairo2-dev libgtk-4-dev meson ninja-build sassc valac + version: latest + - name: Clone libadwaita git repo + run: git clone https://gitlab.gnome.org/GNOME/libadwaita.git -b libadwaita-1-2 --depth=1 + - name: Build libadwaita with Meson + run: | + cd libadwaita + meson . _build -Dgtk_doc=false -Dtests=false -Dexamples=false + - name: Install libadwaita with Ninja + run: | + cd libadwaita + sudo ninja -C _build install + + - uses: dtolnay/rust-toolchain@nightly + - name: Run cargo test + run: cargo test + + fmt: + name: Lint with rustfmt + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + - name: Run rustfmt checks + run: cargo fmt --check + + clippy: + name: Lint with clippy + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Install apt dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: build-essential gettext git libcairo2-dev libgtk-4-dev meson ninja-build sassc valac + version: latest + - name: Clone libadwaita git repo + run: git clone https://gitlab.gnome.org/GNOME/libadwaita.git -b libadwaita-1-2 --depth=1 + - name: Build libadwaita with Meson + run: | + cd libadwaita + meson . _build -Dgtk_doc=false -Dtests=false -Dexamples=false + - name: Install libadwaita with Ninja + run: | + cd libadwaita + sudo ninja -C _build install + + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy + - name: Run cargo clippy checks + run: cargo clippy -- -D warnings + + cargo-deny: + name: Security scan with cargo deny + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Run "cargo deny" to check for vulnerabilities + uses: EmbarkStudios/cargo-deny-action@v1 + + cargo-audit: + name: Security scan with cargo audit + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: Install cargo audit + run: cargo install cargo-audit + - name: Run "cargo audit" to check for vulnerabilities + run: cargo audit diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0751bfb --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,85 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at [@Spydr_06](@Spydr_06). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. + + diff --git a/Cargo.toml b/Cargo.toml index 687856a..97895f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logicrs" -version = "0.1.1" +version = "0.1.2" edition = "2021" description = "An easy-to-use simulator for logical circuits" readme = "./README.md" diff --git a/DOCS.md b/DOCS.md index f529e5b..7a4ee0f 100644 --- a/DOCS.md +++ b/DOCS.md @@ -16,7 +16,9 @@ - [Basic Modules](#basic-modules) - [Input/Output Modules](#inputoutput-modules) - [Gate Modules](#gate-modules) + - [Combinational Modules](#combinational-modules) - [Latch Modules](#latch-modules) + - [Flip Flops](#flip-flops) - [3. Custom Modules](#3-custom-modules) - [Creating a new module](#creating-a-new-module) - [Exporting Modules](#exporting-modules) @@ -104,41 +106,41 @@ LogicRs features several builtin modules available for every project. Here is a - | A | B | Output | - |---|---|--------| - | 0 | 0 | 0 | - | 0 | 1 | 0 | - | 1 | 0 | 0 | - | 1 | 1 | 1 | + | A | B | Output | + | --- | --- | ------ | + | 0 | 0 | 0 | + | 0 | 1 | 0 | + | 1 | 0 | 0 | + | 1 | 1 | 1 | - **`Nand`**: inverse of `And`: - | A | B | Output | - |---|---|--------| - | 0 | 0 | 1 | - | 0 | 1 | 1 | - | 1 | 0 | 1 | - | 1 | 1 | 0 | + | A | B | Output | + | --- | --- | ------ | + | 0 | 0 | 1 | + | 0 | 1 | 1 | + | 1 | 0 | 1 | + | 1 | 1 | 0 | - **`Nor`**: inverse of `Or`: - | A | B | Output | - |---|---|--------| - | 0 | 0 | 1 | - | 0 | 1 | 0 | - | 1 | 0 | 0 | - | 1 | 1 | 0 | + | A | B | Output | + | --- | --- | ------ | + | 0 | 0 | 1 | + | 0 | 1 | 0 | + | 1 | 0 | 0 | + | 1 | 1 | 0 | - **`Not`**: outputs the inverse of the input signal: | Input | Output | - |-------|--------| + | ----- | ------ | | 0 | 1 | | 1 | 0 | @@ -146,34 +148,54 @@ LogicRs features several builtin modules available for every project. Here is a - | A | B | Output | - |---|---|--------| - | 0 | 0 | 0 | - | 0 | 1 | 1 | - | 1 | 0 | 1 | - | 1 | 1 | 1 | + | A | B | Output | + | --- | --- | ------ | + | 0 | 0 | 0 | + | 0 | 1 | 1 | + | 1 | 0 | 1 | + | 1 | 1 | 1 | - **`Xnor`**: inverse of `Xor`: - | A | B | Output | - |---|---|--------| - | 0 | 0 | 1 | - | 0 | 1 | 0 | - | 1 | 0 | 0 | - | 1 | 1 | 1 | + | A | B | Output | + | --- | --- | ------ | + | 0 | 0 | 1 | + | 0 | 1 | 0 | + | 1 | 0 | 0 | + | 1 | 1 | 1 | - **`Xor`**: outputs `1` if either the A *or* (strictly) B input is `1`: - | A | B | Output | - |---|---|--------| - | 0 | 0 | 0 | - | 0 | 1 | 1 | - | 1 | 0 | 1 | - | 1 | 1 | 0 | + | A | B | Output | + | --- | --- | ------ | + | 0 | 0 | 0 | + | 0 | 1 | 1 | + | 1 | 0 | 1 | + | 1 | 1 | 0 | + +### Combinational Modules + +- **`Mux`**: A 2:1 multiplexer, selecting either the A or B input based on the S input: + + + + | S | Output | + | --- | ------ | + | 0 | A | + | 1 | B | + +- **`Demux`**: A 1:2 demultiplexer, selecting either the O0 or O1 output based on the S input: + + + + | S | O1 | O0 | + | --- | --- | --- | + | 0 | 0 | A | + | 1 | A | 0 | ### Latch Modules @@ -181,34 +203,59 @@ LogicRs features several builtin modules available for every project. Here is a - | A | B | Action | - |---|---|-----------| - | 0 | 0 | No change | - | 0 | 1 | Reset | - | 1 | 0 | Set | - | 1 | 1 | Toggle | + | A | B | Action | + | --- | --- | --------- | + | 0 | 0 | No change | + | 0 | 1 | Reset | + | 1 | 0 | Set | + | 1 | 1 | Toggle | - **`SR Latch`**: Latch used to store a value: - | A | B | Action | - |---|---|-------------| - | 0 | 0 | No change | - | 0 | 1 | Reset | - | 1 | 0 | Set | - | 1 | 1 | Not allowed | + | A | B | Action | + | --- | --- | ----------- | + | 0 | 0 | No change | + | 0 | 1 | Reset | + | 1 | 0 | Set | + | 1 | 1 | Not allowed | - **`SR Nand Latch`**: - | A | B | Action | - |---|---|-------------| - | 0 | 0 | Not allowed | - | 0 | 1 | Store 1 | - | 1 | 0 | Store 0 | - | 1 | 1 | No change | + | A | B | Action | + | --- | --- | ----------- | + | 0 | 0 | Not allowed | + | 0 | 1 | Store 1 | + | 1 | 0 | Store 0 | + | 1 | 1 | No change | + + +### Flip Flops + +- **`D Flip Flop`**: A positive edge triggered flip flop storing D value: + + + + | D | Q(t) | Q(t+1) | + | --- | ---- | ------ | + | 0 | 0 | 0 | + | 0 | 1 | 0 | + | 1 | 0 | 1 | + | 1 | 1 | 1 | + +- **`T Flip Flop`**: A positive edge triggered flip flop toggeling its value: + + + + | T | Q(t) | Q(t+1) | + | --- | ---- | ------ | + | 0 | 0 | 0 | + | 0 | 1 | 1 | + | 1 | 0 | 1 | + | 1 | 1 | 0 | > **Note** > Feel free to submit pull-requests for more module implementations diff --git a/INSTALL.md b/INSTALL.md index 0c0e42f..8efbc77 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -15,7 +15,7 @@ Building is and installation is done via Cargo from source. **Supported Operating Systems:** - [x] Linux - [x] Windows 10/11 -- [ ] MacOS (untested) +- [x] MacOS ## Obtaining LogicRs @@ -35,6 +35,44 @@ Prebuilt Binaries can be found on the `releases` section of this repository. On UNIX, these dependencies mostly will be taken care of by Cargo and your distribution's package manager. On Windows, you will need to install an unix-like environment like MSYS64/MINGW64 and take care of the dependencies yourself. +**Linux** + +For rpm-based distributions (Fedora, Red Hat, etc.): + +```bash +$ sudo dnf install gtk4-devel cairo-devel libadwaita-devel +``` + +For Ubuntu/Debian-based distributions (requires Ubuntu 23.04 or newer): + +```bash +$ sudo apt install build-essential libadwaita-1-dev libcairo2-dev libgtk-4-dev +``` + +For Ubuntu/Debian-based distributions that do not ship with `libadwaita` version `1.2` yet (22.10 and below): + +```bash +$ sudo apt install build-essential git libcairo2-dev libgtk-4-dev meson valac sassc gettext +$ git clone https://gitlab.gnome.org/GNOME/libadwaita.git -b libadwaita-1-2 --depth=1 +$ cd libadwaita +$ meson . _build -Dgtk_doc=false -Dtests=false -Dexamples=false +``` + +then, execute with root permission: +```console +# ninja -C _build install +``` + +**MacOS** + +On MacOS, use brew to install these dependencies. + +```zsh +$ brew install cairo +$ brew install gtk4 +$ brew install libadwaita +``` + ## Building Building LogicRs is very easy by just running one command in the main directory of this repository: @@ -56,4 +94,4 @@ Or, if you don't want to clone this repository: ```console $ cargo install --git https://github.com/Spydr06/logicrs -``` \ No newline at end of file +``` diff --git a/README.md b/README.md index da87fa4..db0ec70 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ -**LogicRs** is an easy-to-use, free and open-source simulator for logical circuits/diagrams written in rust. +**LogicRs** is an easy-to-use, free and open-source simulator for logical circuits/diagrams written in Rust. -This program is meant for students, who want learn about [boolean algebra](https://en.wikipedia.org/wiki/Boolean_algebra) and visualize their equations as well as for developers simulating and debugging conditional expressions found everywhere in programming. +This program is meant for students, who want to learn about [boolean algebra](https://en.wikipedia.org/wiki/Boolean_algebra) and visualize their equations as well as for developers simulating and debugging conditional expressions found everywhere in programming. ![random screenshot](./assets/random-screenshot.png) @@ -36,7 +36,7 @@ LogicRs can be downloaded as a prebuilt package from the [releases](https://gith - `.zip` portable installation (Windows) Alternatively, you can download this repository and build LogicRs from source. -Please refer to [INSTALL.md](./INSTALL.md) for build instructions and information about compatability +Please refer to [INSTALL.md](./INSTALL.md) for build instructions and information about compatibility ## Documentation diff --git a/assets/modules/d-flip-flop.png b/assets/modules/d-flip-flop.png new file mode 100644 index 0000000..f60e0cf Binary files /dev/null and b/assets/modules/d-flip-flop.png differ diff --git a/assets/modules/demux.png b/assets/modules/demux.png new file mode 100644 index 0000000..c8aea79 Binary files /dev/null and b/assets/modules/demux.png differ diff --git a/assets/modules/mux.png b/assets/modules/mux.png new file mode 100644 index 0000000..88d341b Binary files /dev/null and b/assets/modules/mux.png differ diff --git a/assets/modules/t-flip-flop.png b/assets/modules/t-flip-flop.png index e418be3..21ab9ae 100644 Binary files a/assets/modules/t-flip-flop.png and b/assets/modules/t-flip-flop.png differ diff --git a/content/module-list.ui b/content/module-list.ui index cc5a2d2..2387629 100644 --- a/content/module-list.ui +++ b/content/module-list.ui @@ -103,6 +103,23 @@ + + + Combinational Blocks + 0.0 + + + + + + browse + + + Latch Modules diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..08af801 --- /dev/null +++ b/deny.toml @@ -0,0 +1,264 @@ +# This template contains all of the possible sections and their default values + +# Note that all fields that take a lint level have these possible values: +# * deny - An error will be produced and the check will fail +# * warn - A warning will be produced, but the check will not fail +# * allow - No warning or error will be produced, though in some cases a note +# will be + +# The values provided in this template are the default values that will be used +# when any section or field is not specified in your own configuration + +# Root options + +# If 1 or more target triples (and optionally, target_features) are specified, +# only the specified targets will be checked when running `cargo deny check`. +# This means, if a particular package is only ever used as a target specific +# dependency, such as, for example, the `nix` crate only being used via the +# `target_family = "unix"` configuration, that only having windows targets in +# this list would mean the nix crate, as well as any of its exclusive +# dependencies not shared by any other crates, would be ignored, as the target +# list here is effectively saying which targets you are building for. +targets = [ + # The triple can be any string, but only the target triples built in to + # rustc (as of 1.40) can be checked against actual config expressions + #{ triple = "x86_64-unknown-linux-musl" }, + # You can also specify which target_features you promise are enabled for a + # particular target. target_features are currently not validated against + # the actual valid features supported by the target architecture. + #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, +] +# When creating the dependency graph used as the source of truth when checks are +# executed, this field can be used to prune crates from the graph, removing them +# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate +# is pruned from the graph, all of its dependencies will also be pruned unless +# they are connected to another crate in the graph that hasn't been pruned, +# so it should be used with care. The identifiers are [Package ID Specifications] +# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html) +#exclude = [] +# If true, metadata will be collected with `--all-features`. Note that this can't +# be toggled off if true, if you want to conditionally enable `--all-features` it +# is recommended to pass `--all-features` on the cmd line instead +all-features = false +# If true, metadata will be collected with `--no-default-features`. The same +# caveat with `all-features` applies +no-default-features = false +# If set, these feature will be enabled when collecting metadata. If `--features` +# is specified on the cmd line they will take precedence over this option. +#features = [] +# When outputting inclusion graphs in diagnostics that include features, this +# option can be used to specify the depth at which feature edges will be added. +# This option is included since the graphs can be quite large and the addition +# of features from the crate(s) to all of the graph roots can be far too verbose. +# This option can be overridden via `--feature-depth` on the cmd line +feature-depth = 1 + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# The path where the advisory database is cloned/fetched into +db-path = "~/.cargo/advisory-db" +# The url(s) of the advisory databases to use +db-urls = ["https://github.com/rustsec/advisory-db"] +# The lint level for security vulnerabilities +vulnerability = "deny" +# The lint level for unmaintained crates +unmaintained = "warn" +# The lint level for crates that have been yanked from their source registry +yanked = "warn" +# The lint level for crates with security notices. Note that as of +# 2019-12-17 there are no security notice advisories in +# https://github.com/rustsec/advisory-db +notice = "warn" +# A list of advisory IDs to ignore. Note that ignored advisories will still +# output a note when they are encountered. +ignore = [ + #"RUSTSEC-0000-0000", +] +# Threshold for security vulnerabilities, any vulnerability with a CVSS score +# lower than the range specified will be ignored. Note that ignored advisories +# will still output a note when they are encountered. +# * None - CVSS Score 0.0 +# * Low - CVSS Score 0.1 - 3.9 +# * Medium - CVSS Score 4.0 - 6.9 +# * High - CVSS Score 7.0 - 8.9 +# * Critical - CVSS Score 9.0 - 10.0 +#severity-threshold = + +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. +# See Git Authentication for more information about setting up git authentication. +#git-fetch-with-cli = true + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +# The lint level for crates which do not have a detectable license +unlicensed = "deny" +# List of explicitly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +allow = [ + "MIT", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "Unicode-DFS-2016" +] +# List of explicitly disallowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +deny = [ +] +# Lint level for licenses considered copyleft +copyleft = "warn" +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will be approved if it is both OSI-approved *AND* FSF +# * either - The license will be approved if it is either OSI-approved *OR* FSF +# * osi - The license will be approved if it is OSI approved +# * fsf - The license will be approved if it is FSF Free +# * osi-only - The license will be approved if it is OSI-approved *AND NOT* FSF +# * fsf-only - The license will be approved if it is FSF *AND NOT* OSI-approved +# * neither - This predicate is ignored and the default lint level is used +allow-osi-fsf-free = "neither" +# Lint level used when no other predicates are matched +# 1. License isn't in the allow or deny lists +# 2. License isn't copyleft +# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" +default = "deny" +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.8 +# Allow 1 or more licenses on a per-crate basis, so that particular licenses +# aren't accepted for every possible crate as with the normal allow list +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +# Some crates don't have (easily) machine readable licensing information, +# adding a clarification entry for it allows you to manually specify the +# licensing information +#[[licenses.clarify]] +# The name of the crate the clarification applies to +#name = "ring" +# The optional version constraint for the crate +#version = "*" +# The SPDX expression for the license requirements of the crate +#expression = "MIT AND ISC AND OpenSSL" +# One or more files in the crate's source used as the "source of truth" for +# the license expression. If the contents match, the clarification will be used +# when running the license check, otherwise the clarification will be ignored +# and the crate will be checked normally, which may produce warnings or errors +# depending on the rest of your configuration +#license-files = [ + # Each entry is a crate relative path, and the (opaque) hash of its contents + #{ path = "LICENSE", hash = 0xbd0eed23 } +#] + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries. +# To see how to mark a crate as unpublished (to the official registry), +# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. +ignore = false +# One or more private registries that you might publish crates to, if a crate +# is only published to private registries, and ignore is true, the crate will +# not have its license(s) checked +registries = [ + #"https://sekretz.com/registry +] + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "warn" +# Lint level for when a crate version requirement is `*` +wildcards = "allow" +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "all" +# The default lint level for `default` features for crates that are members of +# the workspace that is being checked. This can be overridden by allowing/denying +# `default` on a crate-by-crate basis if desired. +workspace-default-features = "allow" +# The default lint level for `default` features for external crates that are not +# members of the workspace. This can be overridden by allowing/denying `default` +# on a crate-by-crate basis if desired. +external-default-features = "allow" +# List of crates that are allowed. Use with care! +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. + #{ name = "ansi_term", version = "=0.11.0" }, + # + # Wrapper crates can optionally be specified to allow the crate when it + # is a direct dependency of the otherwise banned crate + #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, +] + +# List of features to allow/deny +# Each entry the name of a crate and a version range. If version is +# not specified, all versions will be matched. +#[[bans.features]] +#name = "reqwest" +# Features to not allow +#deny = ["json"] +# Features to allow +#allow = [ +# "rustls", +# "__rustls", +# "__tls", +# "hyper-rustls", +# "rustls", +# "rustls-pemfile", +# "rustls-tls-webpki-roots", +# "tokio-rustls", +# "webpki-roots", +#] +# If true, the allowed features must exactly match the enabled feature set. If +# this is set there is no point setting `deny` +#exact = true + +# Certain crates/versions that will be skipped when doing duplicate detection. +skip = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# Similarly to `skip` allows you to skip certain crates during duplicate +# detection. Unlike skip, it also includes the entire tree of transitive +# dependencies starting at the specified crate, up to a certain depth, which is +# by default infinite. +skip-tree = [ + #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, +] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "warn" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "warn" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] + diff --git a/examples/1_4_demux.lrsproj b/examples/1_4_demux.lrsproj new file mode 100644 index 0000000..31a622a --- /dev/null +++ b/examples/1_4_demux.lrsproj @@ -0,0 +1 @@ +{"modules":{"3bitAnd":{"name":"3bitAnd","category":"Custom","builtin":false,"num_inputs":3,"num_outputs":1,"decoration":"None","custom_data":{"plot":{"blocks":{"6948700405194961169":{"id":6948700405194961169,"name":"And","position":[712,395],"size":[75,100],"unique":false,"passthrough":true,"inputs":[10662400792057666129,7345923671473693104],"outputs":[6429374803292863683],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"&"},"color":null},"2223862272419019123":{"id":2223862272419019123,"name":"Switch","position":[104,553],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[11570067050577817514],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"7740736621524242644":{"id":7740736621524242644,"name":"Switch","position":[100,187],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[4974037787672064312],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"17292695365849631448":{"id":17292695365849631448,"name":"Input","position":[352,253],"size":[75,125],"unique":true,"passthrough":true,"inputs":[4974037787672064312,8053859255388208832,11570067050577817514],"outputs":[17817569241488365919,17461459182539826599,7345923671473693104],"state":{"Direct":0},"output_state":7,"decoration":{"Label":"|>"},"color":null},"8953122646148321222":{"id":8953122646148321222,"name":"Output","position":[1101,229],"size":[75,75],"unique":true,"passthrough":true,"inputs":[6429374803292863683],"outputs":[null],"state":{"Direct":0},"output_state":1,"decoration":{"Label":">|"},"color":null},"8717508397863664973":{"id":8717508397863664973,"name":"Switch","position":[104,351],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[8053859255388208832],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"16997336006853255364":{"id":16997336006853255364,"name":"And","position":[526,195],"size":[75,100],"unique":false,"passthrough":true,"inputs":[17817569241488365919,17461459182539826599],"outputs":[10662400792057666129],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"&"},"color":null}},"connections":{"6429374803292863683":{"id":6429374803292863683,"active":false,"origin":{"Output":[6948700405194961169,0]},"segments":{"8572503183172864208":{"Block":[8953122646148321222,0]}}},"7345923671473693104":{"id":7345923671473693104,"active":false,"origin":{"Output":[17292695365849631448,2]},"segments":{"4382706148638993365":{"Block":[6948700405194961169,1]}}},"17461459182539826599":{"id":17461459182539826599,"active":false,"origin":{"Output":[17292695365849631448,1]},"segments":{"9031088541180828430":{"Block":[16997336006853255364,1]}}},"17817569241488365919":{"id":17817569241488365919,"active":false,"origin":{"Output":[17292695365849631448,0]},"segments":{"18417871976226709345":{"Block":[16997336006853255364,0]}}},"8053859255388208832":{"id":8053859255388208832,"active":false,"origin":{"Output":[8717508397863664973,0]},"segments":{"7143166582827326744":{"Block":[17292695365849631448,1]}}},"11570067050577817514":{"id":11570067050577817514,"active":false,"origin":{"Output":[2223862272419019123,0]},"segments":{"10379448467557914331":{"Block":[17292695365849631448,2]}}},"10662400792057666129":{"id":10662400792057666129,"active":false,"origin":{"Output":[16997336006853255364,0]},"segments":{"7611774187484034587":{"Block":[6948700405194961169,0]}}},"4974037787672064312":{"id":4974037787672064312,"active":false,"origin":{"Output":[7740736621524242644,0]},"segments":{"2878205261345184673":{"Block":[17292695365849631448,0]}}}},"states":[{"blocks":{},"connections":{}}]},"input_block":17292695365849631448,"output_block":8953122646148321222,"cache":{}}}},"main_plot":{"blocks":{"4566777863512029735":{"id":4566777863512029735,"name":"Not","position":[356,113],"size":[75,75],"unique":false,"passthrough":true,"inputs":[1029715267087324680],"outputs":[8369026761119912537],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"10963779010771048180":{"id":10963779010771048180,"name":"Lamp","position":[1227,735],"size":[75,75],"unique":false,"passthrough":true,"inputs":[6325038329420834866],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"16098422277027027568":{"id":16098422277027027568,"name":"3bitAnd","position":[973,540],"size":[75,125],"unique":false,"passthrough":true,"inputs":[1029715267087324680,7136991359026484261,13350348527737366313],"outputs":[13205058123947805439],"state":{"Inherit":{"blocks":{"8953122646148321222":{"Direct":0},"2223862272419019123":{"Direct":0},"17292695365849631448":{"Direct":0},"16997336006853255364":{"Direct":0},"6948700405194961169":{"Direct":0},"8717508397863664973":{"Direct":0},"7740736621524242644":{"Direct":0}},"connections":{"17817569241488365919":false,"8053859255388208832":false,"4974037787672064312":false,"7345923671473693104":true,"11570067050577817514":false,"6429374803292863683":false,"17461459182539826599":true,"10662400792057666129":false}}},"output_state":0,"decoration":"None","color":null},"7147216287796945260":{"id":7147216287796945260,"name":"Lamp","position":[1216,559],"size":[75,75],"unique":false,"passthrough":true,"inputs":[13205058123947805439],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"2428517483448542637":{"id":2428517483448542637,"name":"Switch","position":[357,318],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[13350348527737366313],"state":{"Direct":0},"output_state":1,"decoration":{"Switch":true},"color":null},"14215297122274218053":{"id":14215297122274218053,"name":"3bitAnd","position":[962,345],"size":[75,125],"unique":false,"passthrough":true,"inputs":[8369026761119912537,13387288784240604644,13350348527737366313],"outputs":[10884082348613417379],"state":{"Inherit":{"blocks":{"8953122646148321222":{"Direct":0},"16997336006853255364":{"Direct":0},"6948700405194961169":{"Direct":0},"17292695365849631448":{"Direct":0},"8717508397863664973":{"Direct":0},"2223862272419019123":{"Direct":0},"7740736621524242644":{"Direct":0}},"connections":{"6429374803292863683":false,"7345923671473693104":true,"8053859255388208832":false,"11570067050577817514":false,"4974037787672064312":false,"17817569241488365919":true,"17461459182539826599":false,"10662400792057666129":false}}},"output_state":0,"decoration":"None","color":null},"6506733616368019282":{"id":6506733616368019282,"name":"Not","position":[359,504],"size":[75,75],"unique":false,"passthrough":true,"inputs":[13387288784240604644],"outputs":[7136991359026484261],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"1863803427217861332":{"id":1863803427217861332,"name":"Lamp","position":[1208,350],"size":[75,75],"unique":false,"passthrough":true,"inputs":[10884082348613417379],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"16080200127264158799":{"id":16080200127264158799,"name":"Switch","position":[153,247],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[1029715267087324680],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"11209751191681582009":{"id":11209751191681582009,"name":"3bitAnd","position":[955,142],"size":[75,125],"unique":false,"passthrough":true,"inputs":[8369026761119912537,7136991359026484261,13350348527737366313],"outputs":[6286869588338832157],"state":{"Inherit":{"blocks":{"8717508397863664973":{"Direct":0},"16997336006853255364":{"Direct":0},"2223862272419019123":{"Direct":0},"6948700405194961169":{"Direct":0},"8953122646148321222":{"Direct":1},"7740736621524242644":{"Direct":0},"17292695365849631448":{"Direct":0}},"connections":{"17817569241488365919":true,"8053859255388208832":false,"17461459182539826599":true,"4974037787672064312":false,"6429374803292863683":true,"11570067050577817514":false,"10662400792057666129":true,"7345923671473693104":true}}},"output_state":1,"decoration":"None","color":null},"14268195668279466546":{"id":14268195668279466546,"name":"3bitAnd","position":[976,728],"size":[75,125],"unique":false,"passthrough":true,"inputs":[1029715267087324680,13387288784240604644,13350348527737366313],"outputs":[6325038329420834866],"state":{"Inherit":{"blocks":{"6948700405194961169":{"Direct":0},"17292695365849631448":{"Direct":0},"16997336006853255364":{"Direct":0},"8953122646148321222":{"Direct":0},"7740736621524242644":{"Direct":0},"8717508397863664973":{"Direct":0},"2223862272419019123":{"Direct":0}},"connections":{"17817569241488365919":false,"4974037787672064312":false,"8053859255388208832":false,"11570067050577817514":false,"10662400792057666129":false,"17461459182539826599":false,"6429374803292863683":false,"7345923671473693104":true}}},"output_state":0,"decoration":"None","color":null},"11414719916384349550":{"id":11414719916384349550,"name":"Lamp","position":[1197,141],"size":[75,75],"unique":false,"passthrough":true,"inputs":[6286869588338832157],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"6426630108478115960":{"id":6426630108478115960,"name":"Switch","position":[150,638],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[13387288784240604644],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null}},"connections":{"8369026761119912537":{"id":8369026761119912537,"active":true,"origin":{"Output":[4566777863512029735,0]},"segments":{"15974087682035872221":{"Block":[14215297122274218053,0]},"13554226893899518211":{"Block":[11209751191681582009,0]}}},"6286869588338832157":{"id":6286869588338832157,"active":true,"origin":{"Output":[11209751191681582009,0]},"segments":{"6294193629884173":{"Block":[11414719916384349550,0]}}},"10884082348613417379":{"id":10884082348613417379,"active":false,"origin":{"Output":[14215297122274218053,0]},"segments":{"2547705500044812549":{"Block":[1863803427217861332,0]}}},"13205058123947805439":{"id":13205058123947805439,"active":false,"origin":{"Output":[16098422277027027568,0]},"segments":{"12768261393853683572":{"Block":[7147216287796945260,0]}}},"6325038329420834866":{"id":6325038329420834866,"active":false,"origin":{"Output":[14268195668279466546,0]},"segments":{"12734991480319601238":{"Block":[10963779010771048180,0]}}},"13387288784240604644":{"id":13387288784240604644,"active":false,"origin":{"Output":[6426630108478115960,0]},"segments":{"11329492936587909499":{"Block":[6506733616368019282,0]},"5089426793474021631":{"Block":[14268195668279466546,1]},"1608126473310076310":{"Block":[14215297122274218053,1]}}},"1029715267087324680":{"id":1029715267087324680,"active":false,"origin":{"Output":[16080200127264158799,0]},"segments":{"17730874295944404830":{"Block":[4566777863512029735,0]},"6722419165924834377":{"Block":[16098422277027027568,0]},"3652411176845172241":{"Block":[14268195668279466546,0]}}},"13350348527737366313":{"id":13350348527737366313,"active":true,"origin":{"Output":[2428517483448542637,0]},"segments":{"12667323900843725082":{"Block":[14215297122274218053,2]},"10561935862568852273":{"Block":[11209751191681582009,2]},"12718523861491284102":{"Waypoint":[{},[432,368],false]},"5402514459897047508":{"Block":[14268195668279466546,2]},"5254394755049166745":{"Block":[16098422277027027568,2]}}},"7136991359026484261":{"id":7136991359026484261,"active":true,"origin":{"Output":[6506733616368019282,0]},"segments":{"15696108758887676765":{"Waypoint":[{},[434,554],false]},"4029657026824451213":{"Block":[16098422277027027568,1]},"14463221633115693166":{"Block":[11209751191681582009,1]}}}},"states":[{"blocks":{},"connections":{}}]},"tps":10} \ No newline at end of file diff --git a/examples/2_4_decoder.lrsproj b/examples/2_4_decoder.lrsproj new file mode 100644 index 0000000..caba2cb --- /dev/null +++ b/examples/2_4_decoder.lrsproj @@ -0,0 +1 @@ +{"modules":{"3bitOR":{"name":"3bitOR","category":"Custom","builtin":false,"num_inputs":3,"num_outputs":1,"decoration":"None","custom_data":{"plot":{"blocks":{"11443517787118858110":{"id":11443517787118858110,"name":"Switch","position":[90,195],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[8323659685219399651],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"15870468717204097903":{"id":15870468717204097903,"name":"Switch","position":[82,342],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[15321779133911459609],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"14079651739154616822":{"id":14079651739154616822,"name":"Or","position":[582,227],"size":[75,100],"unique":false,"passthrough":true,"inputs":[17567280730831145591,13797049658103114415],"outputs":[13850735144841480736],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"≥1"},"color":null},"10535106079101738299":{"id":10535106079101738299,"name":"Or","position":[778,405],"size":[75,100],"unique":false,"passthrough":true,"inputs":[13850735144841480736,14810257009264333944],"outputs":[15958655187596469594],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null},"13876727103587442375":{"id":13876727103587442375,"name":"Switch","position":[88,520],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[18077900244756926],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"9046654010173238627":{"id":9046654010173238627,"name":"Input","position":[352,281],"size":[75,125],"unique":true,"passthrough":true,"inputs":[8323659685219399651,15321779133911459609,18077900244756926],"outputs":[17567280730831145591,13797049658103114415,14810257009264333944],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"|>"},"color":null},"7236466785598597269":{"id":7236466785598597269,"name":"Output","position":[1062,300],"size":[75,75],"unique":true,"passthrough":true,"inputs":[15958655187596469594],"outputs":[null],"state":{"Direct":0},"output_state":0,"decoration":{"Label":">|"},"color":null}},"connections":{"13850735144841480736":{"id":13850735144841480736,"active":false,"origin":{"Output":[14079651739154616822,0]},"segments":{"12184680259188353433":{"Block":[10535106079101738299,0]}}},"17567280730831145591":{"id":17567280730831145591,"active":false,"origin":{"Output":[9046654010173238627,0]},"segments":{"15209059209405701524":{"Block":[14079651739154616822,0]}}},"14810257009264333944":{"id":14810257009264333944,"active":false,"origin":{"Output":[9046654010173238627,2]},"segments":{"13181173420222233356":{"Block":[10535106079101738299,1]}}},"8323659685219399651":{"id":8323659685219399651,"active":false,"origin":{"Output":[11443517787118858110,0]},"segments":{"9804393645902378223":{"Block":[9046654010173238627,0]}}},"18077900244756926":{"id":18077900244756926,"active":false,"origin":{"Output":[13876727103587442375,0]},"segments":{"13927142080883652095":{"Block":[9046654010173238627,2]}}},"15321779133911459609":{"id":15321779133911459609,"active":false,"origin":{"Output":[15870468717204097903,0]},"segments":{"9327599817669836497":{"Block":[9046654010173238627,1]}}},"13797049658103114415":{"id":13797049658103114415,"active":false,"origin":{"Output":[9046654010173238627,1]},"segments":{"12965976658209389373":{"Block":[14079651739154616822,1]}}},"15958655187596469594":{"id":15958655187596469594,"active":false,"origin":{"Output":[10535106079101738299,0]},"segments":{"18012138398257332260":{"Block":[7236466785598597269,0]}}}},"states":[{"blocks":{},"connections":{}}]},"input_block":9046654010173238627,"output_block":7236466785598597269,"cache":{}}}},"main_plot":{"blocks":{"618665617955604654":{"id":618665617955604654,"name":"Not","position":[277,517],"size":[75,75],"unique":false,"passthrough":true,"inputs":[2595855822306225095],"outputs":[5256713864818856951],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"12696249670015137028":{"id":12696249670015137028,"name":"3bitOR","position":[954,529],"size":[75,125],"unique":false,"passthrough":true,"inputs":[8819801213269541905,2595855822306225095,8762804374067659567],"outputs":[26233994823951067],"state":{"Inherit":{"blocks":{"11443517787118858110":{"Direct":0},"13876727103587442375":{"Direct":0},"9046654010173238627":{"Direct":0},"14079651739154616822":{"Direct":0},"15870468717204097903":{"Direct":0},"10535106079101738299":{"Direct":0},"7236466785598597269":{"Direct":1}},"connections":{"18077900244756926":false,"13850735144841480736":true,"15958655187596469594":true,"8323659685219399651":false,"14810257009264333944":false,"13797049658103114415":false,"15321779133911459609":false,"17567280730831145591":true}}},"output_state":1,"decoration":"None","color":null},"6244679217130272547":{"id":6244679217130272547,"name":"Switch","position":[130,140],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[1646924472918740782],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"17239549332934020459":{"id":17239549332934020459,"name":"3bitOR","position":[951,323],"size":[75,125],"unique":false,"passthrough":true,"inputs":[1646924472918740782,5256713864818856951,8762804374067659567],"outputs":[2704741354143972577],"state":{"Inherit":{"blocks":{"13876727103587442375":{"Direct":0},"7236466785598597269":{"Direct":1},"15870468717204097903":{"Direct":0},"14079651739154616822":{"Direct":0},"11443517787118858110":{"Direct":0},"10535106079101738299":{"Direct":0},"9046654010173238627":{"Direct":0}},"connections":{"15321779133911459609":false,"15958655187596469594":true,"18077900244756926":false,"13850735144841480736":true,"14810257009264333944":false,"8323659685219399651":false,"13797049658103114415":true,"17567280730831145591":false}}},"output_state":1,"decoration":"None","color":null},"7900299704622201815":{"id":7900299704622201815,"name":"Lamp","position":[1213,337],"size":[75,75],"unique":false,"passthrough":true,"inputs":[2704741354143972577],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"8988342801399865296":{"id":8988342801399865296,"name":"Lamp","position":[1205,543],"size":[75,75],"unique":false,"passthrough":true,"inputs":[26233994823951067],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"18178579160817102670":{"id":18178579160817102670,"name":"Lamp","position":[1212,126],"size":[75,75],"unique":false,"passthrough":true,"inputs":[9113680570867485458],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"8315868534591282482":{"id":8315868534591282482,"name":"3bitOR","position":[945,118],"size":[75,125],"unique":false,"passthrough":true,"inputs":[1646924472918740782,2595855822306225095,8762804374067659567],"outputs":[9113680570867485458],"state":{"Inherit":{"blocks":{"11443517787118858110":{"Direct":0},"9046654010173238627":{"Direct":0},"7236466785598597269":{"Direct":0},"14079651739154616822":{"Direct":0},"13876727103587442375":{"Direct":0},"10535106079101738299":{"Direct":0},"15870468717204097903":{"Direct":0}},"connections":{"13850735144841480736":false,"17567280730831145591":false,"8323659685219399651":false,"18077900244756926":false,"15321779133911459609":false,"15958655187596469594":false,"13797049658103114415":false,"14810257009264333944":false}}},"output_state":0,"decoration":"None","color":null},"7031268855510831243":{"id":7031268855510831243,"name":"Lamp","position":[1224,733],"size":[75,75],"unique":false,"passthrough":true,"inputs":[3184000563359397713],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"1865697411466048487":{"id":1865697411466048487,"name":"3bitOR","position":[956,730],"size":[75,125],"unique":false,"passthrough":true,"inputs":[8819801213269541905,5256713864818856951,8762804374067659567],"outputs":[3184000563359397713],"state":{"Inherit":{"blocks":{"13876727103587442375":{"Direct":0},"9046654010173238627":{"Direct":0},"10535106079101738299":{"Direct":0},"11443517787118858110":{"Direct":0},"14079651739154616822":{"Direct":0},"15870468717204097903":{"Direct":0},"7236466785598597269":{"Direct":1}},"connections":{"18077900244756926":false,"13797049658103114415":true,"13850735144841480736":true,"17567280730831145591":true,"8323659685219399651":false,"15321779133911459609":false,"14810257009264333944":false,"15958655187596469594":true}}},"output_state":1,"decoration":"None","color":null},"14731512801401000252":{"id":14731512801401000252,"name":"Switch","position":[133,406],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[2595855822306225095],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"16681430467851955986":{"id":16681430467851955986,"name":"Switch","position":[418,781],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[8762804374067659567],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"537587565737748227":{"id":537587565737748227,"name":"Not","position":[277,287],"size":[75,75],"unique":false,"passthrough":true,"inputs":[1646924472918740782],"outputs":[8819801213269541905],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null}},"connections":{"2704741354143972577":{"id":2704741354143972577,"active":true,"origin":{"Output":[17239549332934020459,0]},"segments":{"3723877577440933675":{"Block":[7900299704622201815,0]}}},"5256713864818856951":{"id":5256713864818856951,"active":true,"origin":{"Output":[618665617955604654,0]},"segments":{"9832098882928627546":{"Block":[17239549332934020459,1]},"16060468133683624741":{"Block":[1865697411466048487,1]}}},"2595855822306225095":{"id":2595855822306225095,"active":false,"origin":{"Output":[14731512801401000252,0]},"segments":{"5407835712998102224":{"Block":[618665617955604654,0]},"11862900896369679221":{"Block":[12696249670015137028,1]},"10038219324655551845":{"Block":[8315868534591282482,1]}}},"26233994823951067":{"id":26233994823951067,"active":true,"origin":{"Output":[12696249670015137028,0]},"segments":{"10434413248209673263":{"Block":[8988342801399865296,0]}}},"9113680570867485458":{"id":9113680570867485458,"active":false,"origin":{"Output":[8315868534591282482,0]},"segments":{"7256114119577960846":{"Block":[18178579160817102670,0]}}},"8762804374067659567":{"id":8762804374067659567,"active":false,"origin":{"Output":[16681430467851955986,0]},"segments":{"2980317265845602467":{"Block":[1865697411466048487,2]},"1526677205142637203":{"Block":[12696249670015137028,2]},"16856868293742851109":{"Block":[8315868534591282482,2]},"10888185278143869898":{"Block":[17239549332934020459,2]}}},"1646924472918740782":{"id":1646924472918740782,"active":false,"origin":{"Output":[6244679217130272547,0]},"segments":{"11668740206953008660":{"Block":[17239549332934020459,0]},"1424954051278926875":{"Block":[537587565737748227,0]},"14328699146061727823":{"Block":[8315868534591282482,0]}}},"8819801213269541905":{"id":8819801213269541905,"active":true,"origin":{"Output":[537587565737748227,0]},"segments":{"8513068359107690113":{"Block":[1865697411466048487,0]},"15744768234538700910":{"Block":[12696249670015137028,0]}}},"3184000563359397713":{"id":3184000563359397713,"active":true,"origin":{"Output":[1865697411466048487,0]},"segments":{"6095594292965914044":{"Block":[7031268855510831243,0]}}}},"states":[{"blocks":{},"connections":{}}]},"tps":10} \ No newline at end of file diff --git a/examples/3_8_decoder.lrsproj b/examples/3_8_decoder.lrsproj new file mode 100644 index 0000000..f86be7f --- /dev/null +++ b/examples/3_8_decoder.lrsproj @@ -0,0 +1 @@ +{"modules":{"3bitOR":{"name":"3bitOR","category":"Custom","builtin":false,"num_inputs":3,"num_outputs":1,"decoration":"None","custom_data":{"plot":{"blocks":{"5826163202502436433":{"id":5826163202502436433,"name":"Switch","position":[152,303],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[952252436743807739],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"5452757655614386393":{"id":5452757655614386393,"name":"Output","position":[1139,319],"size":[75,75],"unique":true,"passthrough":true,"inputs":[15392351252222348794],"outputs":[null],"state":{"Direct":0},"output_state":1,"decoration":{"Label":">|"},"color":null},"6614822015036453802":{"id":6614822015036453802,"name":"Input","position":[387,250],"size":[75,125],"unique":true,"passthrough":true,"inputs":[7458184624048906877,952252436743807739,9133928137021643107],"outputs":[17072520607245603087,15148398867051954076,7920015545758256485],"state":{"Direct":0},"output_state":6,"decoration":{"Label":"|>"},"color":null},"5271430048768925053":{"id":5271430048768925053,"name":"Switch","position":[160,156],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[7458184624048906877],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"10303482053638174318":{"id":10303482053638174318,"name":"Switch","position":[158,481],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[9133928137021643107],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"9802436522604365846":{"id":9802436522604365846,"name":"Or","position":[848,366],"size":[75,100],"unique":false,"passthrough":true,"inputs":[6967065799081486337,7920015545758256485],"outputs":[15392351252222348794],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"≥1"},"color":null},"8202913845398650572":{"id":8202913845398650572,"name":"Or","position":[652,188],"size":[75,100],"unique":false,"passthrough":true,"inputs":[17072520607245603087,15148398867051954076],"outputs":[6967065799081486337],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"≥1"},"color":null}},"connections":{"7920015545758256485":{"id":7920015545758256485,"active":false,"origin":{"Output":[6614822015036453802,2]},"segments":{"305395396602945974":{"Block":[9802436522604365846,1]}}},"7458184624048906877":{"id":7458184624048906877,"active":false,"origin":{"Output":[5271430048768925053,0]},"segments":{"220247698770864146":{"Block":[6614822015036453802,0]}}},"17072520607245603087":{"id":17072520607245603087,"active":false,"origin":{"Output":[6614822015036453802,0]},"segments":{"4006217642052995386":{"Block":[8202913845398650572,0]}}},"9133928137021643107":{"id":9133928137021643107,"active":false,"origin":{"Output":[10303482053638174318,0]},"segments":{"3940214396470768509":{"Block":[6614822015036453802,2]}}},"6967065799081486337":{"id":6967065799081486337,"active":false,"origin":{"Output":[8202913845398650572,0]},"segments":{"12184680259188353433":{"Block":[9802436522604365846,0]}}},"15148398867051954076":{"id":15148398867051954076,"active":false,"origin":{"Output":[6614822015036453802,1]},"segments":{"8224254481504947777":{"Block":[8202913845398650572,1]}}},"952252436743807739":{"id":952252436743807739,"active":false,"origin":{"Output":[5826163202502436433,0]},"segments":{"12569937672979158904":{"Block":[6614822015036453802,1]}}},"15392351252222348794":{"id":15392351252222348794,"active":false,"origin":{"Output":[9802436522604365846,0]},"segments":{"1805500893964110194":{"Block":[5452757655614386393,0]}}}},"states":[{"blocks":{},"connections":{}}]},"input_block":6614822015036453802,"output_block":5452757655614386393,"cache":{}}},"2_4_Decoder":{"name":"2_4_Decoder","category":"Custom","builtin":false,"num_inputs":3,"num_outputs":4,"decoration":"None","custom_data":{"plot":{"blocks":{"1330694236149807409":{"id":1330694236149807409,"name":"Switch","position":[73,373],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[14026651389560852168],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"8189240966998745058":{"id":8189240966998745058,"name":"Not","position":[474,523],"size":[75,75],"unique":false,"passthrough":true,"inputs":[10922923853940047626],"outputs":[12467892045607315882],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"15557370318547489418":{"id":15557370318547489418,"name":"Switch","position":[70,171],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[14195392525994340972],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"12178251541950162930":{"id":12178251541950162930,"name":"Input","position":[241,252],"size":[75,125],"unique":true,"passthrough":true,"inputs":[14195392525994340972,14026651389560852168,4812935712881981986],"outputs":[3808237062440998043,10922923853940047626,10273697210932994563],"state":{"Direct":0},"output_state":4,"decoration":{"Label":"|>"},"color":null},"2300950934748975848":{"id":2300950934748975848,"name":"Not","position":[438,422],"size":[75,75],"unique":false,"passthrough":true,"inputs":[3808237062440998043],"outputs":[3966827297602536366],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"12816718785919822261":{"id":12816718785919822261,"name":"3bitOR","position":[993,533],"size":[75,125],"unique":false,"passthrough":true,"inputs":[3966827297602536366,10922923853940047626,10273697210932994563],"outputs":[4952242122553214070],"state":{"Inherit":{"blocks":{"5271430048768925053":{"Direct":0},"5826163202502436433":{"Direct":0},"5452757655614386393":{"Direct":1},"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"6614822015036453802":{"Direct":0},"10303482053638174318":{"Direct":0}},"connections":{"7458184624048906877":false,"952252436743807739":false,"9133928137021643107":false,"17072520607245603087":true,"15392351252222348794":true,"7920015545758256485":false,"15148398867051954076":false,"6967065799081486337":true}}},"output_state":1,"decoration":"None","color":null},"7808749499628525607":{"id":7808749499628525607,"name":"3bitOR","position":[979,122],"size":[75,125],"unique":false,"passthrough":true,"inputs":[3808237062440998043,10922923853940047626,10273697210932994563],"outputs":[11381851217234279496],"state":{"Inherit":{"blocks":{"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"5452757655614386393":{"Direct":0},"5271430048768925053":{"Direct":0},"10303482053638174318":{"Direct":0},"5826163202502436433":{"Direct":0},"6614822015036453802":{"Direct":0}},"connections":{"15148398867051954076":false,"7458184624048906877":false,"17072520607245603087":false,"9133928137021643107":false,"15392351252222348794":false,"6967065799081486337":false,"7920015545758256485":false,"952252436743807739":false}}},"output_state":1,"decoration":"None","color":null},"7741497079368067120":{"id":7741497079368067120,"name":"3bitOR","position":[993,727],"size":[75,125],"unique":false,"passthrough":true,"inputs":[3966827297602536366,12467892045607315882,10273697210932994563],"outputs":[15818037284891998905],"state":{"Inherit":{"blocks":{"6614822015036453802":{"Direct":0},"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"5452757655614386393":{"Direct":0},"5826163202502436433":{"Direct":0},"5271430048768925053":{"Direct":0},"10303482053638174318":{"Direct":0}},"connections":{"952252436743807739":false,"7458184624048906877":false,"17072520607245603087":false,"6967065799081486337":false,"9133928137021643107":false,"15392351252222348794":false,"7920015545758256485":false,"15148398867051954076":false}}},"output_state":1,"decoration":"None","color":null},"16433046658246031474":{"id":16433046658246031474,"name":"Switch","position":[66,573],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[4812935712881981986],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"17482433680007540011":{"id":17482433680007540011,"name":"3bitOR","position":[982,335],"size":[75,125],"unique":false,"passthrough":true,"inputs":[3808237062440998043,12467892045607315882,10273697210932994563],"outputs":[16390771063111257896],"state":{"Inherit":{"blocks":{"6614822015036453802":{"Direct":0},"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"10303482053638174318":{"Direct":0},"5452757655614386393":{"Direct":0},"5271430048768925053":{"Direct":0},"5826163202502436433":{"Direct":0}},"connections":{"952252436743807739":false,"6967065799081486337":false,"7458184624048906877":false,"7920015545758256485":false,"9133928137021643107":false,"17072520607245603087":false,"15392351252222348794":false,"15148398867051954076":false}}},"output_state":1,"decoration":"None","color":null},"30362919423619325":{"id":30362919423619325,"name":"Output","position":[1340,302],"size":[75,150],"unique":true,"passthrough":true,"inputs":[11381851217234279496,16390771063111257896,4952242122553214070,null],"outputs":[null,null,null,null],"state":{"Direct":4},"output_state":7,"decoration":{"Label":">|"},"color":null}},"connections":{"4812935712881981986":{"id":4812935712881981986,"active":false,"origin":{"Output":[16433046658246031474,0]},"segments":{"15515307691949506622":{"Block":[12178251541950162930,2]}}},"10922923853940047626":{"id":10922923853940047626,"active":false,"origin":{"Output":[12178251541950162930,1]},"segments":{"17513128131938831222":{"Block":[8189240966998745058,0]},"118461893910285939":{"Block":[12816718785919822261,1]},"11523689731207433031":{"Block":[7808749499628525607,1]}}},"4952242122553214070":{"id":4952242122553214070,"active":true,"origin":{"Output":[12816718785919822261,0]},"segments":{"16946874495337132372":{"Block":[30362919423619325,2]}}},"16390771063111257896":{"id":16390771063111257896,"active":false,"origin":{"Output":[17482433680007540011,0]},"segments":{"1086093768689246390":{"Block":[30362919423619325,1]}}},"3808237062440998043":{"id":3808237062440998043,"active":false,"origin":{"Output":[12178251541950162930,0]},"segments":{"114412306175720629":{"Block":[2300950934748975848,0]},"10162827596300513332":{"Block":[7808749499628525607,0]},"17300235435915267330":{"Block":[17482433680007540011,0]}}},"10273697210932994563":{"id":10273697210932994563,"active":false,"origin":{"Output":[12178251541950162930,2]},"segments":{"14409140448488282550":{"Block":[7808749499628525607,2]},"10012352627614707336":{"Block":[17482433680007540011,2]},"6741642278829914348":{"Block":[12816718785919822261,2]},"4308006128139571699":{"Block":[7741497079368067120,2]}}},"15818037284891998905":{"id":15818037284891998905,"active":false,"origin":{"Output":[7741497079368067120,0]},"segments":{"15305943656140340214":{"Waypoint":[{},[1337,426],false]}}},"14026651389560852168":{"id":14026651389560852168,"active":false,"origin":{"Output":[1330694236149807409,0]},"segments":{"9265339585035423185":{"Block":[12178251541950162930,1]}}},"11381851217234279496":{"id":11381851217234279496,"active":false,"origin":{"Output":[7808749499628525607,0]},"segments":{"502220940879043029":{"Block":[30362919423619325,0]}}},"14195392525994340972":{"id":14195392525994340972,"active":false,"origin":{"Output":[15557370318547489418,0]},"segments":{"11239877027686413271":{"Block":[12178251541950162930,0]}}},"3966827297602536366":{"id":3966827297602536366,"active":true,"origin":{"Output":[2300950934748975848,0]},"segments":{"4151035799672908026":{"Block":[12816718785919822261,0]},"4896861240800832200":{"Block":[7741497079368067120,0]}}},"12467892045607315882":{"id":12467892045607315882,"active":true,"origin":{"Output":[8189240966998745058,0]},"segments":{"4747548296712710506":{"Block":[17482433680007540011,1]},"1528704419500364182":{"Waypoint":[{},[549,573],false]},"9622020762044695799":{"Block":[7741497079368067120,1]}}}},"states":[{"blocks":{},"connections":{}}]},"input_block":12178251541950162930,"output_block":30362919423619325,"cache":{}}}},"main_plot":{"blocks":{"1759316403844097879":{"id":1759316403844097879,"name":"Lamp","position":[1246,807],"size":[75,75],"unique":false,"passthrough":true,"inputs":[7957031207826508758],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"123090107300306959":{"id":123090107300306959,"name":"Lamp","position":[1354,559],"size":[75,75],"unique":false,"passthrough":true,"inputs":[7633169386538168058],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"6396916313663147024":{"id":6396916313663147024,"name":"Switch","position":[280,183],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[5065737285276919638],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"12305515693068026001":{"id":12305515693068026001,"name":"2_4_Decoder","position":[965,143],"size":[110,150],"unique":false,"passthrough":true,"inputs":[5065737285276919638,6476180121580787074,1397779537791848485],"outputs":[16636482770662429022,14813477561190340116,16328898762128503554,2347706892057985628],"state":{"Inherit":{"blocks":{"7808749499628525607":{"Inherit":{"blocks":{"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"5452757655614386393":{"Direct":0},"5271430048768925053":{"Direct":0},"10303482053638174318":{"Direct":0},"5826163202502436433":{"Direct":0},"6614822015036453802":{"Direct":0}},"connections":{"15148398867051954076":false,"7458184624048906877":false,"17072520607245603087":false,"9133928137021643107":false,"15392351252222348794":false,"6967065799081486337":false,"7920015545758256485":false,"952252436743807739":false}}},"30362919423619325":{"Direct":4},"2300950934748975848":{"Direct":0},"15557370318547489418":{"Direct":0},"8189240966998745058":{"Direct":0},"16433046658246031474":{"Direct":0},"12816718785919822261":{"Inherit":{"blocks":{"5271430048768925053":{"Direct":0},"5826163202502436433":{"Direct":0},"5452757655614386393":{"Direct":1},"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"6614822015036453802":{"Direct":0},"10303482053638174318":{"Direct":0}},"connections":{"7458184624048906877":false,"952252436743807739":false,"9133928137021643107":false,"17072520607245603087":true,"15392351252222348794":true,"7920015545758256485":false,"15148398867051954076":false,"6967065799081486337":true}}},"12178251541950162930":{"Direct":0},"17482433680007540011":{"Inherit":{"blocks":{"6614822015036453802":{"Direct":0},"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"10303482053638174318":{"Direct":0},"5452757655614386393":{"Direct":0},"5271430048768925053":{"Direct":0},"5826163202502436433":{"Direct":0}},"connections":{"952252436743807739":false,"6967065799081486337":false,"7458184624048906877":false,"7920015545758256485":false,"9133928137021643107":false,"17072520607245603087":false,"15392351252222348794":false,"15148398867051954076":false}}},"7741497079368067120":{"Inherit":{"blocks":{"6614822015036453802":{"Direct":0},"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"5452757655614386393":{"Direct":0},"5826163202502436433":{"Direct":0},"5271430048768925053":{"Direct":0},"10303482053638174318":{"Direct":0}},"connections":{"952252436743807739":false,"7458184624048906877":false,"17072520607245603087":false,"6967065799081486337":false,"9133928137021643107":false,"15392351252222348794":false,"7920015545758256485":false,"15148398867051954076":false}}},"1330694236149807409":{"Direct":0}},"connections":{"3808237062440998043":false,"4952242122553214070":true,"10922923853940047626":false,"4812935712881981986":false,"10273697210932994563":false,"16390771063111257896":false,"14026651389560852168":false,"11381851217234279496":false,"14195392525994340972":false,"12467892045607315882":true,"3966827297602536366":true,"15818037284891998905":false}}},"output_state":4,"decoration":"None","color":null},"17606163393965307161":{"id":17606163393965307161,"name":"Lamp","position":[1239,339],"size":[75,75],"unique":false,"passthrough":true,"inputs":[2347706892057985628],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"10842204851663357744":{"id":10842204851663357744,"name":"Lamp","position":[1357,673],"size":[75,75],"unique":false,"passthrough":true,"inputs":[14827350910199421236],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"1002077032964811609":{"id":1002077032964811609,"name":"Lamp","position":[1240,53],"size":[75,75],"unique":false,"passthrough":true,"inputs":[16636482770662429022],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"7199644146568884472":{"id":7199644146568884472,"name":"Switch","position":[285,401],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[6476180121580787074],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"3448675688672118821":{"id":3448675688672118821,"name":"Not","position":[642,615],"size":[75,75],"unique":false,"passthrough":true,"inputs":[1397779537791848485],"outputs":[546051171609359874],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"14410049255332017570":{"id":14410049255332017570,"name":"Switch","position":[278,600],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[1397779537791848485],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"7960602585792751475":{"id":7960602585792751475,"name":"Lamp","position":[1345,231],"size":[75,75],"unique":false,"passthrough":true,"inputs":[16328898762128503554],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"8406648493657608412":{"id":8406648493657608412,"name":"2_4_Decoder","position":[970,506],"size":[110,150],"unique":false,"passthrough":true,"inputs":[5065737285276919638,6476180121580787074,546051171609359874],"outputs":[7229444187097551701,7633169386538168058,14827350910199421236,7957031207826508758],"state":{"Inherit":{"blocks":{"12816718785919822261":{"Inherit":{"blocks":{"10303482053638174318":{"Direct":0},"6614822015036453802":{"Direct":0},"9802436522604365846":{"Direct":0},"5826163202502436433":{"Direct":0},"8202913845398650572":{"Direct":0},"5452757655614386393":{"Direct":1},"5271430048768925053":{"Direct":0}},"connections":{"7920015545758256485":true,"6967065799081486337":true,"17072520607245603087":true,"9133928137021643107":false,"15148398867051954076":false,"7458184624048906877":false,"15392351252222348794":true,"952252436743807739":false}}},"7808749499628525607":{"Inherit":{"blocks":{"9802436522604365846":{"Direct":0},"6614822015036453802":{"Direct":0},"8202913845398650572":{"Direct":0},"5452757655614386393":{"Direct":1},"5271430048768925053":{"Direct":0},"5826163202502436433":{"Direct":0},"10303482053638174318":{"Direct":0}},"connections":{"7920015545758256485":true,"15148398867051954076":false,"6967065799081486337":false,"952252436743807739":false,"9133928137021643107":false,"7458184624048906877":false,"17072520607245603087":false,"15392351252222348794":true}}},"15557370318547489418":{"Direct":0},"12178251541950162930":{"Direct":0},"7741497079368067120":{"Inherit":{"blocks":{"9802436522604365846":{"Direct":0},"8202913845398650572":{"Direct":0},"5271430048768925053":{"Direct":0},"5452757655614386393":{"Direct":1},"6614822015036453802":{"Direct":0},"5826163202502436433":{"Direct":0},"10303482053638174318":{"Direct":0}},"connections":{"7458184624048906877":false,"17072520607245603087":true,"952252436743807739":false,"15148398867051954076":true,"15392351252222348794":true,"9133928137021643107":false,"7920015545758256485":true,"6967065799081486337":true}}},"8189240966998745058":{"Direct":0},"16433046658246031474":{"Direct":0},"30362919423619325":{"Direct":7},"1330694236149807409":{"Direct":0},"17482433680007540011":{"Inherit":{"blocks":{"8202913845398650572":{"Direct":0},"6614822015036453802":{"Direct":0},"5452757655614386393":{"Direct":1},"5826163202502436433":{"Direct":0},"10303482053638174318":{"Direct":0},"9802436522604365846":{"Direct":0},"5271430048768925053":{"Direct":0}},"connections":{"7458184624048906877":false,"6967065799081486337":true,"15148398867051954076":true,"7920015545758256485":true,"17072520607245603087":false,"15392351252222348794":true,"9133928137021643107":false,"952252436743807739":false}}},"2300950934748975848":{"Direct":0}},"connections":{"14026651389560852168":false,"16390771063111257896":true,"3808237062440998043":false,"14195392525994340972":false,"4952242122553214070":true,"15818037284891998905":true,"10273697210932994563":true,"3966827297602536366":true,"12467892045607315882":true,"4812935712881981986":false,"11381851217234279496":true,"10922923853940047626":false}}},"output_state":7,"decoration":"None","color":null},"13748615919910911212":{"id":13748615919910911212,"name":"Lamp","position":[1336,107],"size":[75,75],"unique":false,"passthrough":true,"inputs":[14813477561190340116],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"12585007627354273937":{"id":12585007627354273937,"name":"Lamp","position":[1244,453],"size":[75,75],"unique":false,"passthrough":true,"inputs":[7229444187097551701],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null}},"connections":{"16636482770662429022":{"id":16636482770662429022,"active":false,"origin":{"Output":[12305515693068026001,0]},"segments":{"16197809942884808409":{"Block":[1002077032964811609,0]}}},"7229444187097551701":{"id":7229444187097551701,"active":true,"origin":{"Output":[8406648493657608412,0]},"segments":{"2298914358189928172":{"Block":[12585007627354273937,0]}}},"14827350910199421236":{"id":14827350910199421236,"active":true,"origin":{"Output":[8406648493657608412,2]},"segments":{"141777333881508207":{"Block":[10842204851663357744,0]}}},"2347706892057985628":{"id":2347706892057985628,"active":false,"origin":{"Output":[12305515693068026001,3]},"segments":{"17584291785536184651":{"Block":[17606163393965307161,0]}}},"7633169386538168058":{"id":7633169386538168058,"active":true,"origin":{"Output":[8406648493657608412,1]},"segments":{"4340435478381472025":{"Block":[123090107300306959,0]}}},"7957031207826508758":{"id":7957031207826508758,"active":false,"origin":{"Output":[8406648493657608412,3]},"segments":{"7171900031433196679":{"Block":[1759316403844097879,0]}}},"5065737285276919638":{"id":5065737285276919638,"active":false,"origin":{"Output":[6396916313663147024,0]},"segments":{"15490204593582344568":{"Block":[8406648493657608412,0]},"11788910925893895345":{"Block":[12305515693068026001,0]}}},"6476180121580787074":{"id":6476180121580787074,"active":false,"origin":{"Output":[7199644146568884472,0]},"segments":{"15844331468225758221":{"Block":[12305515693068026001,1]},"7953163649976562948":{"Waypoint":[{},[360,451],false]},"11272489831585491750":{"Block":[8406648493657608412,1]}}},"1397779537791848485":{"id":1397779537791848485,"active":false,"origin":{"Output":[14410049255332017570,0]},"segments":{"10424529627479595011":{"Block":[12305515693068026001,2]},"13767259991903200427":{"Block":[3448675688672118821,0]}}},"546051171609359874":{"id":546051171609359874,"active":true,"origin":{"Output":[3448675688672118821,0]},"segments":{"8121071211545815006":{"Block":[8406648493657608412,2]}}},"16328898762128503554":{"id":16328898762128503554,"active":true,"origin":{"Output":[12305515693068026001,2]},"segments":{"16475957394934609151":{"Block":[7960602585792751475,0]}}},"14813477561190340116":{"id":14813477561190340116,"active":false,"origin":{"Output":[12305515693068026001,1]},"segments":{"4779083572882037008":{"Block":[13748615919910911212,0]}}}},"states":[{"blocks":{},"connections":{}}]},"tps":10} \ No newline at end of file diff --git a/examples/4-bit-counter.lrsproj b/examples/4-bit-counter.lrsproj index d1e8cc3..d139673 100644 --- a/examples/4-bit-counter.lrsproj +++ b/examples/4-bit-counter.lrsproj @@ -1 +1 @@ -{"modules":{},"main_plot":{"blocks":{"5354188438588814584":{"id":5354188438588814584,"name":"Lamp","position":[375,225],"size":[75,75],"unique":false,"passthrough":true,"inputs":[10440987279344356270],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"17585780629157557337":{"id":17585780629157557337,"name":"T Flip-Flop","position":[125,25],"size":[110,75],"unique":false,"passthrough":true,"inputs":[895923654972995817],"outputs":[9271995660626457038],"state":{"Direct":2},"output_state":0,"decoration":{"Label":"T"},"color":null},"7846093778520356818":{"id":7846093778520356818,"name":"Lamp","position":[675,225],"size":[75,75],"unique":false,"passthrough":true,"inputs":[3134920317199890587],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"17325373593975782801":{"id":17325373593975782801,"name":"T Flip-Flop","position":[275,25],"size":[110,75],"unique":false,"passthrough":true,"inputs":[9271995660626457038],"outputs":[8096273090428741690],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"T"},"color":null},"7433714147185165618":{"id":7433714147185165618,"name":"T Flip-Flop","position":[425,25],"size":[110,75],"unique":false,"passthrough":true,"inputs":[8096273090428741690],"outputs":[6273669149933044936],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"T"},"color":null},"3596810306868589121":{"id":3596810306868589121,"name":"Not","position":[125,125],"size":[75,75],"unique":false,"passthrough":true,"inputs":[895923654972995817],"outputs":[14744021733325809919],"state":{"Direct":0},"output_state":340282366920938463463374607431768211454,"decoration":{"NotLabel":"1"},"color":null},"12911774693485193003":{"id":12911774693485193003,"name":"Not","position":[25,25],"size":[75,75],"unique":false,"passthrough":true,"inputs":[895923654972995817],"outputs":[895923654972995817],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"10497699952113116465":{"id":10497699952113116465,"name":"Not","position":[425,125],"size":[75,75],"unique":false,"passthrough":true,"inputs":[8096273090428741690],"outputs":[7588132761653207674],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"16190443890770760703":{"id":16190443890770760703,"name":"Lamp","position":[525,225],"size":[75,75],"unique":false,"passthrough":true,"inputs":[7588132761653207674],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"11961212448952297802":{"id":11961212448952297802,"name":"Not","position":[575,125],"size":[75,75],"unique":false,"passthrough":true,"inputs":[6273669149933044936],"outputs":[3134920317199890587],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"13998638750705030997":{"id":13998638750705030997,"name":"Lamp","position":[225,225],"size":[75,75],"unique":false,"passthrough":true,"inputs":[14744021733325809919],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"2861656550823996258":{"id":2861656550823996258,"name":"Not","position":[275,125],"size":[75,75],"unique":false,"passthrough":true,"inputs":[9271995660626457038],"outputs":[10440987279344356270],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null}},"connections":{"9271995660626457038":{"id":9271995660626457038,"active":false,"origin":{"Output":[17585780629157557337,0]},"segments":{"4695115420018433688":{"Block":[2861656550823996258,0]},"13156344502743620961":{"Block":[17325373593975782801,0]}}},"3134920317199890587":{"id":3134920317199890587,"active":true,"origin":{"Output":[11961212448952297802,0]},"segments":{"10612020911719657597":{"Block":[7846093778520356818,0]}}},"7588132761653207674":{"id":7588132761653207674,"active":true,"origin":{"Output":[10497699952113116465,0]},"segments":{"18078718082634527518":{"Block":[16190443890770760703,0]}}},"10440987279344356270":{"id":10440987279344356270,"active":true,"origin":{"Output":[2861656550823996258,0]},"segments":{"4827306972552525760":{"Block":[5354188438588814584,0]}}},"6273669149933044936":{"id":6273669149933044936,"active":false,"origin":{"Output":[7433714147185165618,0]},"segments":{"4117633801655392617":{"Block":[11961212448952297802,0]}}},"895923654972995817":{"id":895923654972995817,"active":true,"origin":{"Output":[12911774693485193003,0]},"segments":{"9702505521668695002":{"Block":[12911774693485193003,0]},"7187239749178552577":{"Block":[3596810306868589121,0]},"8965177135727669501":{"Block":[17585780629157557337,0]}}},"14744021733325809919":{"id":14744021733325809919,"active":false,"origin":{"Output":[3596810306868589121,0]},"segments":{"14019196343756079993":{"Block":[13998638750705030997,0]}}},"8096273090428741690":{"id":8096273090428741690,"active":false,"origin":{"Output":[17325373593975782801,0]},"segments":{"6814540146988114983":{"Block":[7433714147185165618,0]},"2534036242052040424":{"Block":[10497699952113116465,0]}}}},"states":[{"blocks":{},"connections":{}}]},"tps":10} \ No newline at end of file +{"modules":{},"main_plot":{"blocks":{"3310398795976558300":{"id":3310398795976558300,"name":"High","position":[271,-7],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[4672615323058003302],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"Label":"1"},"color":null},"18193944025558943703":{"id":18193944025558943703,"name":"Lamp","position":[489,252],"size":[75,75],"unique":false,"passthrough":true,"inputs":[17115819071537323242],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"13764986653674854796":{"id":13764986653674854796,"name":"T Flip-Flop","position":[522,118],"size":[110,100],"unique":false,"passthrough":true,"inputs":[4672615323058003302,13779025196202125909],"outputs":[5266926683341780562,null],"state":{"Direct":2},"output_state":340282366920938463463374607431768211450,"decoration":{"Label":"T"},"color":null},"15743551746034192670":{"id":15743551746034192670,"name":"T Flip-Flop","position":[233,118],"size":[110,100],"unique":false,"passthrough":true,"inputs":[4672615323058003302,11679388001501554237],"outputs":[7635165452054149733,3895035867946493813],"state":{"Direct":2},"output_state":340282366920938463463374607431768211450,"decoration":{"Label":"T"},"color":null},"2185657683087893488":{"id":2185657683087893488,"name":"T Flip-Flop","position":[90,118],"size":[110,100],"unique":false,"passthrough":true,"inputs":[4672615323058003302,9789689151405107917],"outputs":[9676552777124640418,11679388001501554237],"state":{"Direct":0},"output_state":340282366920938463463374607431768211454,"decoration":{"Label":"T"},"color":null},"8129519016779213416":{"id":8129519016779213416,"name":"Button","position":[11,251],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[9789689151405107917],"state":{"Direct":0},"output_state":0,"decoration":{"Button":false},"color":null},"3757203818844583619":{"id":3757203818844583619,"name":"Lamp","position":[345,251],"size":[75,75],"unique":false,"passthrough":true,"inputs":[7635165452054149733],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"3553480486059670029":{"id":3553480486059670029,"name":"T Flip-Flop","position":[379,118],"size":[110,100],"unique":false,"passthrough":true,"inputs":[4672615323058003302,3895035867946493813],"outputs":[17115819071537323242,13779025196202125909],"state":{"Direct":2},"output_state":340282366920938463463374607431768211450,"decoration":{"Label":"T"},"color":null},"16642788480495327288":{"id":16642788480495327288,"name":"Lamp","position":[633,250],"size":[75,75],"unique":false,"passthrough":true,"inputs":[5266926683341780562],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"4986905588854076751":{"id":4986905588854076751,"name":"Lamp","position":[200,250],"size":[75,75],"unique":false,"passthrough":true,"inputs":[9676552777124640418],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null}},"connections":{"11679388001501554237":{"id":11679388001501554237,"active":true,"origin":{"Output":[2185657683087893488,1]},"segments":{"1959939874093137484":{"Block":[15743551746034192670,1]}}},"7635165452054149733":{"id":7635165452054149733,"active":false,"origin":{"Output":[15743551746034192670,0]},"segments":{"2823241741726666062":{"Block":[3757203818844583619,0]}}},"5266926683341780562":{"id":5266926683341780562,"active":false,"origin":{"Output":[13764986653674854796,0]},"segments":{"16401291941952202028":{"Block":[16642788480495327288,0]}}},"4672615323058003302":{"id":4672615323058003302,"active":true,"origin":{"Output":[3310398795976558300,0]},"segments":{"11400273655492572696":{"Block":[13764986653674854796,0]},"5093895033105929308":{"Block":[3553480486059670029,0]},"4700516162004995735":{"Block":[2185657683087893488,0]},"15591164720030407252":{"Block":[15743551746034192670,0]}}},"3895035867946493813":{"id":3895035867946493813,"active":true,"origin":{"Output":[15743551746034192670,1]},"segments":{"4266020639005941764":{"Block":[3553480486059670029,1]}}},"9789689151405107917":{"id":9789689151405107917,"active":false,"origin":{"Output":[8129519016779213416,0]},"segments":{"10163194800539781641":{"Block":[2185657683087893488,1]}}},"9676552777124640418":{"id":9676552777124640418,"active":false,"origin":{"Output":[2185657683087893488,0]},"segments":{"6387114459403000980":{"Block":[4986905588854076751,0]}}},"13779025196202125909":{"id":13779025196202125909,"active":true,"origin":{"Output":[3553480486059670029,1]},"segments":{"18124619394754504424":{"Block":[13764986653674854796,1]}}},"17115819071537323242":{"id":17115819071537323242,"active":false,"origin":{"Output":[3553480486059670029,0]},"segments":{"14049412917095013675":{"Block":[18193944025558943703,0]}}}},"states":[{"blocks":{},"connections":{}}]},"tps":10} \ No newline at end of file diff --git a/examples/4_1_mux.lrsproj b/examples/4_1_mux.lrsproj new file mode 100644 index 0000000..669d6ac --- /dev/null +++ b/examples/4_1_mux.lrsproj @@ -0,0 +1 @@ +{"modules":{"4bitOr":{"name":"4bitOr","category":"Custom","builtin":false,"num_inputs":4,"num_outputs":1,"decoration":"None","custom_data":{"plot":{"blocks":{"12166928311404177694":{"id":12166928311404177694,"name":"Or","position":[638,475],"size":[75,100],"unique":false,"passthrough":true,"inputs":[7576821208456737720,482923191737322998],"outputs":[262314561853221528],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null},"5637598222391782080":{"id":5637598222391782080,"name":"Switch","position":[67,456],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[11893518677564875527],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"4064320683642352758":{"id":4064320683642352758,"name":"Output","position":[1056,325],"size":[75,75],"unique":true,"passthrough":true,"inputs":[9850323938053374053],"outputs":[null],"state":{"Direct":0},"output_state":1,"decoration":{"Label":">|"},"color":null},"11639949932439738998":{"id":11639949932439738998,"name":"Or","position":[845,329],"size":[75,100],"unique":false,"passthrough":true,"inputs":[9402177740616183468,262314561853221528],"outputs":[9850323938053374053],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"≥1"},"color":null},"9017659792229562936":{"id":9017659792229562936,"name":"Switch","position":[62,569],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[17838084143709526958],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"18278660270663525882":{"id":18278660270663525882,"name":"Switch","position":[64,142],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[1495283470360210198],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"8463179022162666903":{"id":8463179022162666903,"name":"Switch","position":[60,285],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[12920192117471586588],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"10223082420416540366":{"id":10223082420416540366,"name":"Input","position":[353,304],"size":[75,150],"unique":true,"passthrough":true,"inputs":[1495283470360210198,12920192117471586588,11893518677564875527,17838084143709526958],"outputs":[10089032244369624877,10784981009563256597,7576821208456737720,482923191737322998],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"|>"},"color":null},"15981728760744330715":{"id":15981728760744330715,"name":"Or","position":[628,250],"size":[75,100],"unique":false,"passthrough":true,"inputs":[10089032244369624877,10784981009563256597],"outputs":[9402177740616183468],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"≥1"},"color":null}},"connections":{"9402177740616183468":{"id":9402177740616183468,"active":false,"origin":{"Output":[15981728760744330715,0]},"segments":{"2089418997430454785":{"Block":[11639949932439738998,0]},"7097076896816467440":{"Waypoint":[{},[703,300],false]}}},"12920192117471586588":{"id":12920192117471586588,"active":false,"origin":{"Output":[8463179022162666903,0]},"segments":{"7593137853772377748":{"Block":[10223082420416540366,1]}}},"262314561853221528":{"id":262314561853221528,"active":false,"origin":{"Output":[12166928311404177694,0]},"segments":{"910712293872634115":{"Block":[11639949932439738998,1]}}},"10089032244369624877":{"id":10089032244369624877,"active":false,"origin":{"Output":[10223082420416540366,0]},"segments":{"12285248006521893844":{"Block":[15981728760744330715,0]}}},"10784981009563256597":{"id":10784981009563256597,"active":false,"origin":{"Output":[10223082420416540366,1]},"segments":{"8924015553444566848":{"Block":[15981728760744330715,1]}}},"482923191737322998":{"id":482923191737322998,"active":false,"origin":{"Output":[10223082420416540366,3]},"segments":{"13836573218871455513":{"Block":[12166928311404177694,1]}}},"11893518677564875527":{"id":11893518677564875527,"active":false,"origin":{"Output":[5637598222391782080,0]},"segments":{"15507382151875238424":{"Block":[10223082420416540366,2]}}},"7576821208456737720":{"id":7576821208456737720,"active":false,"origin":{"Output":[10223082420416540366,2]},"segments":{"3708168736893782404":{"Block":[12166928311404177694,0]}}},"17838084143709526958":{"id":17838084143709526958,"active":false,"origin":{"Output":[9017659792229562936,0]},"segments":{"751150477672919703":{"Block":[10223082420416540366,3]}}},"9850323938053374053":{"id":9850323938053374053,"active":false,"origin":{"Output":[11639949932439738998,0]},"segments":{"3717512230930130927":{"Block":[4064320683642352758,0]}}},"1495283470360210198":{"id":1495283470360210198,"active":false,"origin":{"Output":[18278660270663525882,0]},"segments":{"5824833421350973289":{"Block":[10223082420416540366,0]}}}},"states":[{"blocks":{},"connections":{}}]},"input_block":10223082420416540366,"output_block":4064320683642352758,"cache":{}}},"3bitAnd":{"name":"3bitAnd","category":"Custom","builtin":false,"num_inputs":3,"num_outputs":1,"decoration":"None","custom_data":{"plot":{"blocks":{"8953122646148321222":{"id":8953122646148321222,"name":"Output","position":[1101,229],"size":[75,75],"unique":true,"passthrough":true,"inputs":[6429374803292863683],"outputs":[null],"state":{"Direct":0},"output_state":1,"decoration":{"Label":">|"},"color":null},"8717508397863664973":{"id":8717508397863664973,"name":"Switch","position":[104,351],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[8053859255388208832],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"7740736621524242644":{"id":7740736621524242644,"name":"Switch","position":[100,187],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[4974037787672064312],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"17292695365849631448":{"id":17292695365849631448,"name":"Input","position":[352,253],"size":[75,125],"unique":true,"passthrough":true,"inputs":[4974037787672064312,8053859255388208832,11570067050577817514],"outputs":[17817569241488365919,17461459182539826599,7345923671473693104],"state":{"Direct":0},"output_state":7,"decoration":{"Label":"|>"},"color":null},"6948700405194961169":{"id":6948700405194961169,"name":"And","position":[712,395],"size":[75,100],"unique":false,"passthrough":true,"inputs":[10662400792057666129,7345923671473693104],"outputs":[6429374803292863683],"state":{"Direct":0},"output_state":1,"decoration":{"Label":"&"},"color":null},"16997336006853255364":{"id":16997336006853255364,"name":"And","position":[526,195],"size":[75,100],"unique":false,"passthrough":true,"inputs":[17817569241488365919,17461459182539826599],"outputs":[10662400792057666129],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"&"},"color":null},"2223862272419019123":{"id":2223862272419019123,"name":"Switch","position":[104,553],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[11570067050577817514],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null}},"connections":{"4974037787672064312":{"id":4974037787672064312,"active":false,"origin":{"Output":[7740736621524242644,0]},"segments":{"2878205261345184673":{"Block":[17292695365849631448,0]}}},"11570067050577817514":{"id":11570067050577817514,"active":false,"origin":{"Output":[2223862272419019123,0]},"segments":{"10379448467557914331":{"Block":[17292695365849631448,2]}}},"6429374803292863683":{"id":6429374803292863683,"active":false,"origin":{"Output":[6948700405194961169,0]},"segments":{"8572503183172864208":{"Block":[8953122646148321222,0]}}},"10662400792057666129":{"id":10662400792057666129,"active":false,"origin":{"Output":[16997336006853255364,0]},"segments":{"7611774187484034587":{"Block":[6948700405194961169,0]}}},"17461459182539826599":{"id":17461459182539826599,"active":false,"origin":{"Output":[17292695365849631448,1]},"segments":{"9031088541180828430":{"Block":[16997336006853255364,1]}}},"7345923671473693104":{"id":7345923671473693104,"active":false,"origin":{"Output":[17292695365849631448,2]},"segments":{"4382706148638993365":{"Block":[6948700405194961169,1]}}},"17817569241488365919":{"id":17817569241488365919,"active":false,"origin":{"Output":[17292695365849631448,0]},"segments":{"18417871976226709345":{"Block":[16997336006853255364,0]}}},"8053859255388208832":{"id":8053859255388208832,"active":false,"origin":{"Output":[8717508397863664973,0]},"segments":{"7143166582827326744":{"Block":[17292695365849631448,1]}}}},"states":[{"blocks":{},"connections":{}}]},"input_block":17292695365849631448,"output_block":8953122646148321222,"cache":{}}}},"main_plot":{"blocks":{"12296640868233041834":{"id":12296640868233041834,"name":"Lamp","position":[1499,399],"size":[75,75],"unique":false,"passthrough":true,"inputs":[7290080070024776007],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":true},"color":null},"6926156250368804656":{"id":6926156250368804656,"name":"Not","position":[240,111],"size":[75,75],"unique":false,"passthrough":true,"inputs":[5585119377181521717],"outputs":[880428322000585084],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"4763956481880025684":{"id":4763956481880025684,"name":"3bitAnd","position":[977,312],"size":[75,125],"unique":false,"passthrough":true,"inputs":[880428322000585084,12242807097950048654,1077548654642870287],"outputs":[16128151557194409544],"state":{"Inherit":{"blocks":{"6948700405194961169":{"Direct":0},"16997336006853255364":{"Direct":0},"2223862272419019123":{"Direct":0},"8953122646148321222":{"Direct":0},"8717508397863664973":{"Direct":0},"7740736621524242644":{"Direct":0},"17292695365849631448":{"Direct":0}},"connections":{"10662400792057666129":false,"7345923671473693104":true,"4974037787672064312":true,"17817569241488365919":true,"8053859255388208832":true,"17461459182539826599":false,"6429374803292863683":false,"11570067050577817514":true}}},"output_state":0,"decoration":"None","color":null},"16009703443443156865":{"id":16009703443443156865,"name":"Switch","position":[498,599],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[6683256341705708285],"state":{"Direct":0},"output_state":1,"decoration":{"Switch":true},"color":[1.0,0.47058824,0.0,1.0]},"16067247870739005925":{"id":16067247870739005925,"name":"Switch","position":[75,654],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[12242807097950048654],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":[0.20784314,0.5176471,0.89411765,1.0]},"9342021910530117146":{"id":9342021910530117146,"name":"4bitOr","position":[1287,370],"size":[75,150],"unique":false,"passthrough":true,"inputs":[12455575928772733664,16128151557194409544,11075966540010021113,15106205427703604738],"outputs":[7290080070024776007],"state":{"Inherit":{"blocks":{"10223082420416540366":{"Direct":0},"9017659792229562936":{"Direct":0},"12166928311404177694":{"Direct":0},"15981728760744330715":{"Direct":0},"5637598222391782080":{"Direct":0},"8463179022162666903":{"Direct":0},"4064320683642352758":{"Direct":1},"11639949932439738998":{"Direct":0},"18278660270663525882":{"Direct":0}},"connections":{"9402177740616183468":true,"482923191737322998":false,"12920192117471586588":false,"11893518677564875527":false,"10089032244369624877":true,"17838084143709526958":false,"9850323938053374053":true,"262314561853221528":false,"10784981009563256597":false,"1495283470360210198":false,"7576821208456737720":false}}},"output_state":1,"decoration":"None","color":null},"1206119741337672409":{"id":1206119741337672409,"name":"3bitAnd","position":[988,761],"size":[75,125],"unique":false,"passthrough":true,"inputs":[5585119377181521717,12242807097950048654,7912973208937525149],"outputs":[15106205427703604738],"state":{"Inherit":{"blocks":{"8717508397863664973":{"Direct":0},"7740736621524242644":{"Direct":0},"2223862272419019123":{"Direct":0},"16997336006853255364":{"Direct":0},"17292695365849631448":{"Direct":0},"8953122646148321222":{"Direct":0},"6948700405194961169":{"Direct":0}},"connections":{"11570067050577817514":true,"17461459182539826599":false,"8053859255388208832":true,"4974037787672064312":true,"7345923671473693104":true,"6429374803292863683":false,"17817569241488365919":false,"10662400792057666129":false}}},"output_state":0,"decoration":"None","color":null},"10792046667421019612":{"id":10792046667421019612,"name":"3bitAnd","position":[985,551],"size":[75,125],"unique":false,"passthrough":true,"inputs":[5585119377181521717,8588405659925627803,6683256341705708285],"outputs":[11075966540010021113],"state":{"Inherit":{"blocks":{"6948700405194961169":{"Direct":0},"2223862272419019123":{"Direct":0},"16997336006853255364":{"Direct":0},"8953122646148321222":{"Direct":0},"8717508397863664973":{"Direct":0},"17292695365849631448":{"Direct":0},"7740736621524242644":{"Direct":0}},"connections":{"10662400792057666129":false,"11570067050577817514":true,"17461459182539826599":true,"7345923671473693104":true,"4974037787672064312":true,"17817569241488365919":false,"6429374803292863683":false,"8053859255388208832":true}}},"output_state":0,"decoration":"None","color":null},"11229144482577710398":{"id":11229144482577710398,"name":"Not","position":[248,502],"size":[75,75],"unique":false,"passthrough":true,"inputs":[12242807097950048654],"outputs":[8588405659925627803],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"2597674599453362689":{"id":2597674599453362689,"name":"Switch","position":[67,191],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[5585119377181521717],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":[0.20784314,0.5176471,0.89411765,1.0]},"8936523564391697190":{"id":8936523564391697190,"name":"Switch","position":[492,194],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[8698093123294626364],"state":{"Direct":0},"output_state":1,"decoration":{"Switch":true},"color":[1.0,0.47058824,0.0,1.0]},"4752686097948544488":{"id":4752686097948544488,"name":"3bitAnd","position":[975,104],"size":[75,125],"unique":false,"passthrough":true,"inputs":[880428322000585084,8588405659925627803,8698093123294626364],"outputs":[12455575928772733664],"state":{"Inherit":{"blocks":{"7740736621524242644":{"Direct":0},"8953122646148321222":{"Direct":1},"17292695365849631448":{"Direct":0},"8717508397863664973":{"Direct":0},"6948700405194961169":{"Direct":0},"16997336006853255364":{"Direct":0},"2223862272419019123":{"Direct":0}},"connections":{"17461459182539826599":true,"8053859255388208832":true,"7345923671473693104":true,"11570067050577817514":true,"6429374803292863683":true,"10662400792057666129":true,"4974037787672064312":true,"17817569241488365919":true}}},"output_state":1,"decoration":"None","color":null},"4917648420729787235":{"id":4917648420729787235,"name":"Switch","position":[494,357],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[1077548654642870287],"state":{"Direct":0},"output_state":1,"decoration":{"Switch":true},"color":[1.0,0.47058824,0.0,1.0]},"14886272867940653513":{"id":14886272867940653513,"name":"Switch","position":[494,801],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[7912973208937525149],"state":{"Direct":0},"output_state":1,"decoration":{"Switch":true},"color":[1.0,0.47058824,0.0,1.0]}},"connections":{"1077548654642870287":{"id":1077548654642870287,"active":true,"origin":{"Output":[4917648420729787235,0]},"segments":{"4195298288738525767":{"Block":[4763956481880025684,2]}}},"5585119377181521717":{"id":5585119377181521717,"active":false,"origin":{"Output":[2597674599453362689,0]},"segments":{"9072643954736559743":{"Block":[10792046667421019612,0]},"9972917248501126237":{"Block":[1206119741337672409,0]},"15039347598044827159":{"Block":[6926156250368804656,0]}}},"15106205427703604738":{"id":15106205427703604738,"active":false,"origin":{"Output":[1206119741337672409,0]},"segments":{"410167890082854626":{"Block":[9342021910530117146,3]}}},"11075966540010021113":{"id":11075966540010021113,"active":false,"origin":{"Output":[10792046667421019612,0]},"segments":{"5023043357690042756":{"Block":[9342021910530117146,2]}}},"7290080070024776007":{"id":7290080070024776007,"active":true,"origin":{"Output":[9342021910530117146,0]},"segments":{"4786149870296038701":{"Block":[12296640868233041834,0]}}},"8698093123294626364":{"id":8698093123294626364,"active":true,"origin":{"Output":[8936523564391697190,0]},"segments":{"18126607850171788925":{"Block":[4752686097948544488,2]}}},"880428322000585084":{"id":880428322000585084,"active":true,"origin":{"Output":[6926156250368804656,0]},"segments":{"16868679635950346522":{"Block":[4752686097948544488,0]},"11552236343013448095":{"Block":[4763956481880025684,0]}}},"16128151557194409544":{"id":16128151557194409544,"active":false,"origin":{"Output":[4763956481880025684,0]},"segments":{"7592503867589820601":{"Block":[9342021910530117146,1]}}},"8588405659925627803":{"id":8588405659925627803,"active":true,"origin":{"Output":[11229144482577710398,0]},"segments":{"9179716284408263233":{"Block":[10792046667421019612,1]},"7444562220757013370":{"Block":[4752686097948544488,1]}}},"12242807097950048654":{"id":12242807097950048654,"active":false,"origin":{"Output":[16067247870739005925,0]},"segments":{"175947720167177821":{"Block":[4763956481880025684,1]},"17202268497098556282":{"Block":[1206119741337672409,1]},"13644843529571495706":{"Block":[11229144482577710398,0]}}},"6683256341705708285":{"id":6683256341705708285,"active":true,"origin":{"Output":[16009703443443156865,0]},"segments":{"5693831327473864157":{"Block":[10792046667421019612,2]}}},"7912973208937525149":{"id":7912973208937525149,"active":true,"origin":{"Output":[14886272867940653513,0]},"segments":{"13552730686124026293":{"Block":[1206119741337672409,2]}}},"12455575928772733664":{"id":12455575928772733664,"active":true,"origin":{"Output":[4752686097948544488,0]},"segments":{"11693562776394932668":{"Block":[9342021910530117146,0]}}}},"states":[{"blocks":{},"connections":{}}]},"tps":10} \ No newline at end of file diff --git a/examples/4_2_encoder.lrsproj b/examples/4_2_encoder.lrsproj new file mode 100644 index 0000000..e403ea6 --- /dev/null +++ b/examples/4_2_encoder.lrsproj @@ -0,0 +1 @@ +{"modules":{},"main_plot":{"blocks":{"5780997842342358994":{"id":5780997842342358994,"name":"Or","position":[648,486],"size":[75,100],"unique":false,"passthrough":true,"inputs":[5907144371647562198,1051902172786586110],"outputs":[16982161174616937788],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null},"9083588765215147153":{"id":9083588765215147153,"name":"Or","position":[641,274],"size":[75,100],"unique":false,"passthrough":true,"inputs":[5907144371647562198,1764610214494617033],"outputs":[420941256879378525],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null},"661382560239866371":{"id":661382560239866371,"name":"Switch","position":[175,559],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[1051902172786586110],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"8239294321627158241":{"id":8239294321627158241,"name":"Lamp","position":[860,490],"size":[75,75],"unique":false,"passthrough":true,"inputs":[16982161174616937788],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"9472469054356857550":{"id":9472469054356857550,"name":"Switch","position":[175,347],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[1764610214494617033],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"9582365227418021798":{"id":9582365227418021798,"name":"Lamp","position":[842,280],"size":[75,75],"unique":false,"passthrough":true,"inputs":[420941256879378525],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"9895893471863207574":{"id":9895893471863207574,"name":"Switch","position":[176,217],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[5907144371647562198],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null}},"connections":{"1051902172786586110":{"id":1051902172786586110,"active":false,"origin":{"Output":[661382560239866371,0]},"segments":{"4741576959098351316":{"Block":[5780997842342358994,1]}}},"5907144371647562198":{"id":5907144371647562198,"active":false,"origin":{"Output":[9895893471863207574,0]},"segments":{"11558284457416720797":{"Block":[9083588765215147153,0]},"2439397003263232559":{"Block":[5780997842342358994,0]}}},"1764610214494617033":{"id":1764610214494617033,"active":false,"origin":{"Output":[9472469054356857550,0]},"segments":{"3316933849184145659":{"Block":[9083588765215147153,1]}}},"420941256879378525":{"id":420941256879378525,"active":false,"origin":{"Output":[9083588765215147153,0]},"segments":{"10881981534714980186":{"Block":[9582365227418021798,0]}}},"16982161174616937788":{"id":16982161174616937788,"active":false,"origin":{"Output":[5780997842342358994,0]},"segments":{"1764725558178439431":{"Block":[8239294321627158241,0]}}}},"states":[{"blocks":{},"connections":{}}]},"tps":10} \ No newline at end of file diff --git a/examples/4_2_priority_encoder.lrsproj b/examples/4_2_priority_encoder.lrsproj new file mode 100644 index 0000000..1a8775d --- /dev/null +++ b/examples/4_2_priority_encoder.lrsproj @@ -0,0 +1 @@ +{"modules":{"4bitOR":{"name":"4bitOR","category":"Custom","builtin":false,"num_inputs":4,"num_outputs":1,"decoration":"None","custom_data":{"plot":{"blocks":{"7261545547897439171":{"id":7261545547897439171,"name":"Switch","position":[96,199],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[2394202178950453078],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"11831951173631644795":{"id":11831951173631644795,"name":"Output","position":[1093,268],"size":[75,75],"unique":true,"passthrough":true,"inputs":[7828962739849814286],"outputs":[null],"state":{"Direct":0},"output_state":0,"decoration":{"Label":">|"},"color":null},"13470369146777191247":{"id":13470369146777191247,"name":"Switch","position":[235,473],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[4480998803060816814],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"14097714230167987269":{"id":14097714230167987269,"name":"Switch","position":[207,89],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[11275844753259754109],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"2192911254854882565":{"id":2192911254854882565,"name":"Or","position":[904,265],"size":[75,100],"unique":false,"passthrough":true,"inputs":[1853634152462068933,17944999038851309831],"outputs":[7828962739849814286],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null},"16066048010897865659":{"id":16066048010897865659,"name":"Input","position":[458,234],"size":[75,150],"unique":true,"passthrough":true,"inputs":[11275844753259754109,2394202178950453078,1397311947179044976,4480998803060816814],"outputs":[4222097757551165818,314466242896085718,8006587210391735050,10156350082819441373],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"|>"},"color":null},"10483170946680177305":{"id":10483170946680177305,"name":"Or","position":[721,410],"size":[75,100],"unique":false,"passthrough":true,"inputs":[8006587210391735050,10156350082819441373],"outputs":[17944999038851309831],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null},"13508947528641412387":{"id":13508947528641412387,"name":"Switch","position":[98,328],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[1397311947179044976],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"15907146539919060847":{"id":15907146539919060847,"name":"Or","position":[711,163],"size":[75,100],"unique":false,"passthrough":true,"inputs":[4222097757551165818,314466242896085718],"outputs":[1853634152462068933],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null}},"connections":{"7828962739849814286":{"id":7828962739849814286,"active":false,"origin":{"Output":[2192911254854882565,0]},"segments":{"17081743216726503005":{"Block":[11831951173631644795,0]}}},"17944999038851309831":{"id":17944999038851309831,"active":false,"origin":{"Output":[10483170946680177305,0]},"segments":{"10189387855164851878":{"Block":[2192911254854882565,1]}}},"4222097757551165818":{"id":4222097757551165818,"active":false,"origin":{"Output":[16066048010897865659,0]},"segments":{"13902391358607565571":{"Block":[15907146539919060847,0]}}},"314466242896085718":{"id":314466242896085718,"active":false,"origin":{"Output":[16066048010897865659,1]},"segments":{"10243658707543148602":{"Block":[15907146539919060847,1]}}},"4480998803060816814":{"id":4480998803060816814,"active":false,"origin":{"Output":[13470369146777191247,0]},"segments":{"9977722351397687032":{"Block":[16066048010897865659,3]}}},"1853634152462068933":{"id":1853634152462068933,"active":false,"origin":{"Output":[15907146539919060847,0]},"segments":{"11358579009848483232":{"Block":[2192911254854882565,0]}}},"1397311947179044976":{"id":1397311947179044976,"active":false,"origin":{"Output":[13508947528641412387,0]},"segments":{"18370711443285524338":{"Block":[16066048010897865659,2]}}},"10156350082819441373":{"id":10156350082819441373,"active":false,"origin":{"Output":[16066048010897865659,3]},"segments":{"3203224063662653276":{"Block":[10483170946680177305,1]}}},"8006587210391735050":{"id":8006587210391735050,"active":false,"origin":{"Output":[16066048010897865659,2]},"segments":{"2689524646134975881":{"Block":[10483170946680177305,0]}}},"11275844753259754109":{"id":11275844753259754109,"active":false,"origin":{"Output":[14097714230167987269,0]},"segments":{"3950601846181594593":{"Block":[16066048010897865659,0]}}},"2394202178950453078":{"id":2394202178950453078,"active":false,"origin":{"Output":[7261545547897439171,0]},"segments":{"5589068559658787932":{"Block":[16066048010897865659,1]}}}},"states":[{"blocks":{},"connections":{}}]},"input_block":16066048010897865659,"output_block":11831951173631644795,"cache":{}}}},"main_plot":{"blocks":{"11551186679312468524":{"id":11551186679312468524,"name":"Or","position":[1013,403],"size":[75,100],"unique":false,"passthrough":true,"inputs":[6297096340359421525,13084866136728277492],"outputs":[8864244267956967508],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null},"2284675418505280863":{"id":2284675418505280863,"name":"Switch","position":[129,158],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[6297096340359421525],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"5450933050212885463":{"id":5450933050212885463,"name":"Or","position":[1016,179],"size":[75,100],"unique":false,"passthrough":true,"inputs":[6297096340359421525,694858405286344934],"outputs":[16495642631128294690],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"≥1"},"color":null},"4948209736856450130":{"id":4948209736856450130,"name":"Switch","position":[127,691],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[16165881969304332184],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"16962542668576102297":{"id":16962542668576102297,"name":"Not","position":[276,394],"size":[75,75],"unique":false,"passthrough":true,"inputs":[694858405286344934],"outputs":[17577133980667618779],"state":{"Direct":0},"output_state":340282366920938463463374607431768211455,"decoration":{"NotLabel":"1"},"color":null},"8332521650258777770":{"id":8332521650258777770,"name":"Lamp","position":[1269,186],"size":[75,75],"unique":false,"passthrough":true,"inputs":[16495642631128294690],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"5177431392760312561":{"id":5177431392760312561,"name":"Lamp","position":[1269,398],"size":[75,75],"unique":false,"passthrough":true,"inputs":[8864244267956967508],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"2029172593914211886":{"id":2029172593914211886,"name":"Lamp","position":[1276,620],"size":[75,75],"unique":false,"passthrough":true,"inputs":[9237163386040865538],"outputs":[],"state":{"Direct":0},"output_state":0,"decoration":{"Lamp":false},"color":null},"5054884804175412643":{"id":5054884804175412643,"name":"And","position":[449,421],"size":[75,100],"unique":false,"passthrough":true,"inputs":[17577133980667618779,16466250935828158951],"outputs":[13084866136728277492],"state":{"Direct":0},"output_state":0,"decoration":{"Label":"&"},"color":null},"7863713429300518790":{"id":7863713429300518790,"name":"4bitOR","position":[1024,609],"size":[75,150],"unique":false,"passthrough":true,"inputs":[6297096340359421525,694858405286344934,16466250935828158951,16165881969304332184],"outputs":[9237163386040865538],"state":{"Inherit":{"blocks":{"2192911254854882565":{"Direct":0},"13470369146777191247":{"Direct":0},"14097714230167987269":{"Direct":0},"10483170946680177305":{"Direct":0},"11831951173631644795":{"Direct":0},"16066048010897865659":{"Direct":0},"7261545547897439171":{"Direct":0},"13508947528641412387":{"Direct":0},"15907146539919060847":{"Direct":0}},"connections":{"10156350082819441373":false,"7828962739849814286":false,"314466242896085718":false,"8006587210391735050":false,"17944999038851309831":false,"1397311947179044976":false,"4222097757551165818":false,"11275844753259754109":false,"1853634152462068933":false,"2394202178950453078":false,"4480998803060816814":false}}},"output_state":0,"decoration":"None","color":null},"11079611158168038280":{"id":11079611158168038280,"name":"Switch","position":[129,297],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[694858405286344934],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null},"17192571683992508181":{"id":17192571683992508181,"name":"Switch","position":[125,488],"size":[75,75],"unique":false,"passthrough":true,"inputs":[],"outputs":[16466250935828158951],"state":{"Direct":0},"output_state":0,"decoration":{"Switch":false},"color":null}},"connections":{"16495642631128294690":{"id":16495642631128294690,"active":false,"origin":{"Output":[5450933050212885463,0]},"segments":{"701171361066018777":{"Block":[8332521650258777770,0]}}},"8864244267956967508":{"id":8864244267956967508,"active":false,"origin":{"Output":[11551186679312468524,0]},"segments":{"10311212254298815258":{"Block":[5177431392760312561,0]}}},"694858405286344934":{"id":694858405286344934,"active":false,"origin":{"Output":[11079611158168038280,0]},"segments":{"10358145600573650200":{"Block":[5450933050212885463,1]},"14260616926294807401":{"Block":[16962542668576102297,0]},"8508113115449385411":{"Block":[7863713429300518790,1]}}},"16466250935828158951":{"id":16466250935828158951,"active":false,"origin":{"Output":[17192571683992508181,0]},"segments":{"16082590213361342076":{"Block":[7863713429300518790,2]},"5606620935144440922":{"Block":[5054884804175412643,1]}}},"17577133980667618779":{"id":17577133980667618779,"active":true,"origin":{"Output":[16962542668576102297,0]},"segments":{"3415368845432867229":{"Block":[5054884804175412643,0]}}},"13084866136728277492":{"id":13084866136728277492,"active":false,"origin":{"Output":[5054884804175412643,0]},"segments":{"7032357362551845238":{"Block":[11551186679312468524,1]}}},"16165881969304332184":{"id":16165881969304332184,"active":false,"origin":{"Output":[4948209736856450130,0]},"segments":{"6090260380269457471":{"Block":[7863713429300518790,3]}}},"9237163386040865538":{"id":9237163386040865538,"active":false,"origin":{"Output":[7863713429300518790,0]},"segments":{"10926765228748332697":{"Block":[2029172593914211886,0]}}},"6297096340359421525":{"id":6297096340359421525,"active":false,"origin":{"Output":[2284675418505280863,0]},"segments":{"12113207045824839991":{"Block":[5450933050212885463,0]},"3120098212902730276":{"Block":[11551186679312468524,0]},"11565904187042372518":{"Block":[7863713429300518790,0]}}}},"states":[{"blocks":{},"connections":{}}]},"tps":10} \ No newline at end of file diff --git a/snippets/win-script.nsi b/snippets/win-script.nsi new file mode 100644 index 0000000..f5ab75b --- /dev/null +++ b/snippets/win-script.nsi @@ -0,0 +1,7 @@ +Outfile "logicrs-windows-x86_64.exe" +Section + StrCpy $INSTDIR $EXEDIR + StrCpy $INSTDIR "$INSTDIR\logicrs" + SetOutPath $INSTDIR + File /r "win-files\*.*" +SectionEnd \ No newline at end of file diff --git a/src/application/action.rs b/src/application/action.rs index 4a30079..338d229 100644 --- a/src/application/action.rs +++ b/src/application/action.rs @@ -1,4 +1,10 @@ -use crate::{simulator::*, config, project::ProjectRef, renderer::{vector::Vector2, Color}, id::Id}; +use crate::{ + config, + id::Id, + project::ProjectRef, + renderer::{vector::Vector2, Color}, + simulator::*, +}; use super::*; @@ -6,7 +12,7 @@ use super::*; pub struct ActionStack { actions: Vec, next: usize, - dirty: bool + dirty: bool, } impl ActionStack { @@ -29,7 +35,7 @@ impl ActionStack { if let Some(action) = self.actions.get_mut(self.next) { self.next += 1; self.dirty = true; - + info!("Re-doing action {}", self.next - 1); action.exec(app); @@ -48,7 +54,7 @@ impl ActionStack { } action.exec(app); - + self.next += 1; self.dirty = true; self.actions.push(action); @@ -92,29 +98,39 @@ pub enum Action { impl Action { fn exec(&mut self, app: &Application) { match self { - Self::NewBlock(plot_provider, block) => { // place a new block + Self::NewBlock(plot_provider, block) => { + // place a new block plot_provider.with_mut(|plot| plot.add_block(block.clone())); app.imp().rerender_editor(); } Self::PasteBlocks(plot_provier, blocks, connections) => { plot_provier.with_mut(|plot| { - blocks.iter().for_each(|block| plot.add_block(block.clone())); - connections.iter().for_each(|connection| unsafe { plot.add_connection_unsafe(connection.clone()) }); + blocks + .iter() + .for_each(|block| plot.add_block(block.clone())); + connections.iter().for_each(|connection| unsafe { + plot.add_connection_unsafe(connection.clone()) + }); }); app.imp().rerender_editor(); } Self::MoveBlock(plot_provider, block_id, _from, to) => { - plot_provider.with_mut(|plot| if let Some(block) = plot.get_block_mut(*block_id) { - block.set_position(*to); + plot_provider.with_mut(|plot| { + if let Some(block) = plot.get_block_mut(*block_id) { + block.set_position(*to); + } }); app.imp().rerender_editor(); } Self::MoveWaypoint(plot_provider, segment_id, _from, to) => { - plot_provider.with_mut(|plot| - if let Some(waypoint) = plot.get_connection_mut(segment_id.connection_id()).and_then(|c| c.get_segment_mut(segment_id.location())) { + plot_provider.with_mut(|plot| { + if let Some(waypoint) = plot + .get_connection_mut(segment_id.connection_id()) + .and_then(|c| c.get_segment_mut(segment_id.location())) + { waypoint.set_position(*to); } - ); + }); app.imp().rerender_editor(); } Self::NewConnection(plot_provider, connection) => { @@ -123,17 +139,29 @@ impl Action { }); app.imp().rerender_editor(); } - Self::WaypointToConnection(plot_provider, segment_id, _segment, block_id, block_port) => { - plot_provider.with_mut(|plot| - if let Some(waypoint) = plot.get_connection_mut(segment_id.connection_id()).and_then(|c| c.get_segment_mut(segment_id.location())) { + Self::WaypointToConnection( + plot_provider, + segment_id, + _segment, + block_id, + block_port, + ) => { + plot_provider.with_mut(|plot| { + if let Some(waypoint) = plot + .get_connection_mut(segment_id.connection_id()) + .and_then(|c| c.get_segment_mut(segment_id.location())) + { waypoint.convert(*block_id, *block_port); if let Some(block) = plot.get_block_mut(*block_id) { - block.set_connection(Connector::Input(*block_port), Some(*segment_id.connection_id())); + block.set_connection( + Connector::Input(*block_port), + Some(*segment_id.connection_id()), + ); plot.add_block_to_update(*block_id); } } - ); + }); app.imp().rerender_editor(); } Self::AddSegment(plot_provider, segment_id, segment, index) => { @@ -156,13 +184,16 @@ impl Action { } Self::ChangeBorderColor(plot_provider, new_color, block_ids, old_colors) => { let old = plot_provider.with_mut(|plot| { - block_ids.iter().filter_map(|block_id| { - plot.get_block_mut(*block_id).map(|block| { - let old_color = *block.color(); - block.set_color(Some(*new_color)); - old_color + block_ids + .iter() + .filter_map(|block_id| { + plot.get_block_mut(*block_id).map(|block| { + let old_color = *block.color(); + block.set_color(Some(*new_color)); + old_color + }) }) - }).collect() + .collect() }); if let Some(old) = old { *old_colors = old; @@ -172,19 +203,23 @@ impl Action { } Self::DeleteSelection(plot_provider, blocks, connections, incoming) => { //println!("delete connections: {connections:?} incoming: {incoming:?}"); - *incoming = plot_provider.with_mut(|plot| { - for connection in &*connections { - plot.remove_connection(connection.id()); - } + *incoming = plot_provider + .with_mut(|plot| { + for connection in &*connections { + plot.remove_connection(connection.id()); + } - let mut incoming = vec![]; - for block in blocks.iter() { - incoming.append(&mut plot.delete_block(block.id())) - } - incoming - }).unwrap_or_default(); + let mut incoming = vec![]; + for block in blocks.iter() { + incoming.append(&mut plot.delete_block(block.id())) + } + incoming + }) + .unwrap_or_default(); - blocks.iter_mut().for_each(|block| block.set_highlighted(false)); + blocks + .iter_mut() + .for_each(|block| block.set_highlighted(false)); app.imp().rerender_editor(); } Self::CreateModule(project, module) => { @@ -204,13 +239,14 @@ impl Action { fn undo(&self, app: &Application) { match self { - Self::NewBlock(plot_provider, block) => { // remove a block + Self::NewBlock(plot_provider, block) => { + // remove a block plot_provider.with_mut(|plot| plot.delete_block(block.id())); app.imp().rerender_editor(); } Self::PasteBlocks(plot_provier, blocks, connections) => { - plot_provier.with_mut(|plot| { - blocks.iter().for_each(|block| { + plot_provier.with_mut(|plot| { + blocks.iter().for_each(|block| { plot.delete_block(block.id()); }); connections.iter().for_each(|connection| { @@ -220,19 +256,22 @@ impl Action { app.imp().rerender_editor(); } Self::MoveBlock(plot_provider, block_id, from, _to) => { - plot_provider.with_mut(|plot| + plot_provider.with_mut(|plot| { if let Some(block) = plot.get_block_mut(*block_id) { block.set_position(*from); } - ); + }); app.imp().rerender_editor(); } Self::MoveWaypoint(plot_provider, segment_id, from, _to) => { - plot_provider.with_mut(|plot| - if let Some(waypoint) = plot.get_connection_mut(segment_id.connection_id()).and_then(|c| c.get_segment_mut(segment_id.location())) { + plot_provider.with_mut(|plot| { + if let Some(waypoint) = plot + .get_connection_mut(segment_id.connection_id()) + .and_then(|c| c.get_segment_mut(segment_id.location())) + { waypoint.set_position(*from); } - ); + }); app.imp().rerender_editor(); } Self::NewConnection(plot_provider, connection) => { @@ -241,9 +280,18 @@ impl Action { }); app.imp().rerender_editor(); } - Self::WaypointToConnection(plot_provider, segment_id, segment, block_id, block_port) => { + Self::WaypointToConnection( + plot_provider, + segment_id, + segment, + block_id, + block_port, + ) => { plot_provider.with_mut(|plot| { - if let Some(waypoint) = plot.get_connection_mut(segment_id.connection_id()).and_then(|c| c.get_segment_mut(segment_id.location())) { + if let Some(waypoint) = plot + .get_connection_mut(segment_id.connection_id()) + .and_then(|c| c.get_segment_mut(segment_id.location())) + { *waypoint = segment.clone(); } @@ -270,11 +318,14 @@ impl Action { } Self::ChangeBorderColor(plot_provider, _new_color, block_ids, old_colors) => { plot_provider.with_mut(|plot| { - block_ids.iter().zip(old_colors).for_each(|(block_id, old_color)| { - if let Some(block) = plot.get_block_mut(*block_id) { - block.set_color(*old_color); - } - }); + block_ids + .iter() + .zip(old_colors) + .for_each(|(block_id, old_color)| { + if let Some(block) = plot.get_block_mut(*block_id) { + block.set_color(*old_color); + } + }); }); app.imp().rerender_editor(); @@ -282,8 +333,15 @@ impl Action { Self::DeleteSelection(plot_provider, blocks, connections, incoming) => { println!("restore connections: {connections:?} incoming: {incoming:?}"); plot_provider.with_mut(|plot| { - blocks.iter().for_each(|block| plot.add_block(block.clone())); - connections.iter().chain(incoming.iter()).for_each(|connection| unsafe { plot.add_connection_unsafe(connection.clone()) }); + blocks + .iter() + .for_each(|block| plot.add_block(block.clone())); + connections + .iter() + .chain(incoming.iter()) + .for_each(|connection| unsafe { + plot.add_connection_unsafe(connection.clone()) + }); }); app.imp().rerender_editor(); } diff --git a/src/application/clipboard.rs b/src/application/clipboard.rs index 735e211..e07e4ac 100644 --- a/src/application/clipboard.rs +++ b/src/application/clipboard.rs @@ -1,5 +1,5 @@ -use crate::{simulator::*, renderer::vector::*, id::Id}; -use serde::{Serialize, Deserialize}; +use crate::{id::Id, renderer::vector::*, simulator::*}; +use serde::{Deserialize, Serialize}; use super::{action::Action, selection::*}; @@ -7,7 +7,7 @@ use super::{action::Action, selection::*}; pub enum Clipboard { Empty, Blocks(Vec, Vec), - Module(Box) + Module(Box), } impl Default for Clipboard { @@ -25,17 +25,26 @@ impl Clipboard { serde_json::from_str(data).map_err(|err| err.to_string()) } - pub fn paste_to(&self, plot_provider: PlotProvider, position: Vector2) -> Result { + pub fn paste_to( + &self, + plot_provider: PlotProvider, + position: Vector2, + ) -> Result { if let Clipboard::Blocks(blocks, connections) = self { let mut data = (blocks.to_owned(), connections.to_owned()); data.prepare_pasting(position); plot_provider.with_mut(|plot| { plot.unhighlight(); - plot.set_selection(Selection::Many(data.0.iter().map(|block| Selectable::Block(block.id())).collect())); + plot.set_selection(Selection::Many( + data.0 + .iter() + .map(|block| Selectable::Block(block.id())) + .collect(), + )); }); return Ok(Action::PasteBlocks(plot_provider, data.0, data.1)); } - + panic!("called `paste_to()` on clipboard != Clipboard::Blocks") } } @@ -86,8 +95,7 @@ impl Copyable<(&Plot, Vec)> for (Vec, Vec) { let mut connection = connection.clone(); if connection.remove_unselected_branches(&block_ids) { *c = None; - } - else { + } else { connections.push(connection); } } @@ -120,7 +128,12 @@ trait Pasteable { impl Pasteable> for (Vec, Vec) { fn prepare_pasting(&mut self, position: Vector2) -> &mut Self { - let min = self.0.iter().map(|block| block.position()).min().unwrap_or_default(); + let min = self + .0 + .iter() + .map(|block| block.position()) + .min() + .unwrap_or_default(); let offset = Vector2::cast(position) - min; self.0.iter_mut().for_each(|block| { @@ -130,28 +143,31 @@ impl Pasteable> for (Vec, Vec) { block.set_position(block.position() + offset); block.set_highlighted(true); - self.1.iter_mut().for_each(|connection| connection.refactor_id(old_id, new_id)); + self.1 + .iter_mut() + .for_each(|connection| connection.refactor_id(old_id, new_id)); }); self.1.iter_mut().for_each(|connection| { let old_id = connection.id(); let new_id = Id::new(); connection.set_id(new_id); - connection.for_each_mut_segment(|segment| + connection.for_each_mut_segment(|segment| { if let Some(position) = segment.position() { segment.set_position(*position + offset) } - ); + }); - self.0.iter_mut().for_each(|block| - block.connections_mut() + self.0.iter_mut().for_each(|block| { + block + .connections_mut() .filter_map(|c| c.as_mut()) - .for_each(|c| + .for_each(|c| { if *c == old_id { *c = new_id; } - ) - ); + }) + }); }); self diff --git a/src/application/editor.rs b/src/application/editor.rs index 755c28c..df768db 100644 --- a/src/application/editor.rs +++ b/src/application/editor.rs @@ -1,17 +1,20 @@ -use crate::renderer::{Renderable, COLOR_THEME, vector::{Vector2, VectorCast}}; +use crate::renderer::{ + vector::{Vector2, VectorCast}, + Renderable, COLOR_THEME, +}; #[derive(Default, Copy, Clone)] pub enum EditorMode { #[default] Normal, - Grid + Grid, } impl From for EditorMode { fn from(b: bool) -> Self { match b { - true => Self::Grid, - false => Self::Normal + true => Self::Grid, + false => Self::Normal, } } } @@ -23,17 +26,19 @@ impl EditorMode { pub fn align(&self, position: Vector2) -> Vector2 { match self { Self::Grid => position / GRID_SIZE.into() * GRID_SIZE.into(), - _ => position + _ => position, } - } + } } // defines at which point the grid doesn't get rendered anymore -const SCALE_CUTOFF: f64 = 0.30; +const SCALE_CUTOFF: f64 = 0.30; impl Renderable for EditorMode { fn render(&self, renderer: &R, _data: &crate::simulator::Plot) -> Result<(), R::Error> - where R: crate::renderer::Renderer { + where + R: crate::renderer::Renderer, + { match self { EditorMode::Grid => { if renderer.scale() < SCALE_CUTOFF { @@ -46,13 +51,15 @@ impl Renderable for EditorMode { renderer.set_color(unsafe { &COLOR_THEME.grid_color }); for i in (offset.0..end.0 as i32).step_by(GRID_SIZE as usize) { for j in (offset.1..end.1 as i32).step_by(GRID_SIZE as usize) { - renderer.rectangle(Vector2(i - 1, j - 1), Vector2(2, 2)).fill()?; + renderer + .rectangle(Vector2(i - 1, j - 1), Vector2(2, 2)) + .fill()?; } } Ok(()) } - _ => Ok(()) + _ => Ok(()), } } } diff --git a/src/application/gactions.rs b/src/application/gactions.rs index dc18cf0..91c08da 100644 --- a/src/application/gactions.rs +++ b/src/application/gactions.rs @@ -3,6 +3,8 @@ use super::{*, selection::Selectable}; use crate::{fatal::*, project::Project, simulator::Simulator, FileExtension, export::ModuleFile}; use crate::application::user_settings::UserSettingsKey::ThemeKey; use crate::application::user_settings::UserSettingsValue::ThemeValue; +use super::{selection::Selectable, *}; +use crate::{export::ModuleFile, fatal::*, project::Project, simulator::Simulator, FileExtension}; #[derive(Default, Clone, Copy, Serialize, Deserialize)] pub enum Theme { @@ -24,7 +26,7 @@ impl From for Theme { 0 => Self::SystemPreference, 1 => Self::Dark, 2 => Self::Light, - _ => panic!() + _ => panic!(), } } } @@ -34,7 +36,7 @@ impl From for adw::ColorScheme { match val { Theme::SystemPreference => adw::ColorScheme::Default, Theme::Dark => adw::ColorScheme::ForceDark, - Theme::Light => adw::ColorScheme::ForceLight + Theme::Light => adw::ColorScheme::ForceLight, } } } @@ -91,22 +93,115 @@ lazy_static! { GAction::new("save-as", &["S"], None, None, Application::gaction_save_as), GAction::new("open", &["O"], None, None, Application::gaction_open), GAction::new("new", &["N"], None, None, Application::gaction_new), - GAction::new("delete-block", &["Delete"], None, None, Application::gaction_delete_block), - GAction::new("create-new-module", &["N"], None, None, Application::gaction_create_new_module), - GAction::new("undo", &["Z"], None, None, Application::gaction_undo), - GAction::new("redo", &["Y"], None, None, Application::gaction_redo), - GAction::new("copy", &["C"], None, None, Application::gaction_copy), + GAction::new( + "delete-block", + &["Delete", "BackSpace"], + None, + None, + Application::gaction_delete_block + ), + GAction::new( + "create-new-module", + &["N"], + None, + None, + Application::gaction_create_new_module + ), + GAction::new( + "undo", + &["Z"], + None, + None, + Application::gaction_undo + ), + GAction::new( + "redo", + &["Y"], + None, + None, + Application::gaction_redo + ), + GAction::new( + "copy", + &["C"], + None, + None, + Application::gaction_copy + ), GAction::new("cut", &["X"], None, None, Application::gaction_cut), - GAction::new("paste", &["V"], None, None, Application::gaction_paste), - GAction::new("select-all", &["A"], None, None, Application::gaction_select_all), - GAction::new("set-selection-color", &[], None, None, Application::gaction_set_selection_color), - GAction::new("delete-module", &[], Some(glib::VariantTy::STRING), None, Application::gaction_delete_module), - GAction::new("edit-module", &[], Some(glib::VariantTy::STRING), None, Application::gaction_edit_module), - GAction::new("search-module", &["F"], None, None, Application::gaction_search_module), - GAction::new("change-theme", &[], None, Some((glib::VariantTy::BYTE, Theme::SystemPreference.to_variant())), Application::gaction_change_theme), - GAction::new("change-tick-speed", &[], None, Some((glib::VariantTy::INT32, Simulator::DEFAULT_TICKS_PER_SECOND.to_variant())), Application::gaction_change_tps), - GAction::new("export-module", &[], Some(glib::VariantTy::STRING), None, Application::gaction_export_module), - GAction::new("import-module", &[], None, None, Application::gaction_import_module) + GAction::new( + "paste", + &["V"], + None, + None, + Application::gaction_paste + ), + GAction::new( + "select-all", + &["A"], + None, + None, + Application::gaction_select_all + ), + GAction::new( + "set-selection-color", + &[], + None, + None, + Application::gaction_set_selection_color + ), + GAction::new( + "delete-module", + &[], + Some(glib::VariantTy::STRING), + None, + Application::gaction_delete_module + ), + GAction::new( + "edit-module", + &[], + Some(glib::VariantTy::STRING), + None, + Application::gaction_edit_module + ), + GAction::new( + "search-module", + &["F"], + None, + None, + Application::gaction_search_module + ), + GAction::new( + "change-theme", + &[], + None, + Some((glib::VariantTy::BYTE, Theme::SystemPreference.to_variant())), + Application::gaction_change_theme + ), + GAction::new( + "change-tick-speed", + &[], + None, + Some(( + glib::VariantTy::INT32, + Simulator::DEFAULT_TICKS_PER_SECOND.to_variant() + )), + Application::gaction_change_tps + ), + GAction::new( + "export-module", + &[], + Some(glib::VariantTy::STRING), + None, + Application::gaction_export_module + ), + GAction::new( + "import-module", + &[], + None, + None, + Application::gaction_import_module + ) ]; } @@ -157,7 +252,12 @@ impl Application { } }).collect() )).unwrap_or_default(); - self.new_action(Action::DeleteSelection(plot_provider, blocks, connections, vec![])); + self.new_action(Action::DeleteSelection( + plot_provider, + blocks, + connections, + vec![], + )); } } @@ -175,7 +275,6 @@ impl Application { self.redo_action(); } - fn gaction_copy(self, _: &gio::SimpleAction, _: Option<&glib::Variant>) { self.copy_clipboard(false); } @@ -212,14 +311,19 @@ impl Application { fn gaction_edit_module(self, _: &gio::SimpleAction, parameter: Option<&glib::Variant>) { let module_name = parameter .expect("Could not get module name target.") - .get::().unwrap(); + .get::() + .unwrap(); self.imp().edit_module(module_name); } fn gaction_search_module(self, _: &gio::SimpleAction, _: Option<&glib::Variant>) { - self.imp().window() - .borrow().as_ref().unwrap() - .module_list().show_search(); + self.imp() + .window() + .borrow() + .as_ref() + .unwrap() + .module_list() + .show_search(); } fn gaction_change_theme(self, action: &gio::SimpleAction, parameter: Option<&glib::Variant>) { @@ -247,9 +351,7 @@ impl Application { .get::() .expect("the parameter needs to be of type `u8`"); - self.imp() - .project() - .lock().unwrap().set_tps(new); + self.imp().project().lock().unwrap().set_tps(new); action.set_state(&new.to_variant()); } @@ -356,7 +458,13 @@ impl Application { .transient_for(&window) .modal(true) .heading("Save File?") - .body(format!("There are unsaved changes in \"{}\". Do you want to save them?", self.imp().file_name()).as_str()) + .body( + format!( + "There are unsaved changes in \"{}\". Do you want to save them?", + self.imp().file_name() + ) + .as_str(), + ) .close_response("Cancel") .default_response("Yes") .build(); @@ -423,7 +531,7 @@ impl Application { .cancel_label("Cancel") .filter(&Project::file_filter()) .build(); - + open_dialog.connect_response({ let file_chooser = RefCell::new(Some(open_dialog.clone())); glib::clone!(@weak app, @weak window => move |_, response| { @@ -449,7 +557,7 @@ impl Application { } }) }); - + open_dialog.show(); })); } @@ -513,4 +621,4 @@ impl Application { dialog.present(); } -} \ No newline at end of file +} diff --git a/src/application/mod.rs b/src/application/mod.rs index 465701c..43b2675 100644 --- a/src/application/mod.rs +++ b/src/application/mod.rs @@ -1,24 +1,25 @@ -pub mod template; -pub mod gactions; pub mod action; pub mod clipboard; pub mod editor; +pub mod gactions; pub mod selection; pub mod user_settings; +pub mod template; +use crate::{application::clipboard::Clipboard, config, ui::dialogs}; use action::*; -use std::cell::RefCell; use adw::traits::MessageDialogExt; -use gtk::{prelude::*, subclass::prelude::*, gio, glib}; +use gtk::{gio, glib, prelude::*, subclass::prelude::*}; use selection::SelectionField; use crate::{config, ui::dialogs, application::clipboard::Clipboard}; use crate::application::gactions::Theme; use crate::application::user_settings::UserSettingsKey::ThemeKey; use crate::application::user_settings::UserSettingsValue::ThemeValue; +use std::cell::RefCell; glib::wrapper! { pub struct Application(ObjectSubclass) - @extends gio::Application, gtk::Application, + @extends gio::Application, gtk::Application, @implements gio::ActionGroup, gio::ActionMap; } @@ -32,7 +33,6 @@ impl Application { pub fn new() -> Self { gio::resources_register_include!("logicrs.gresource").expect("Failed to register resources."); - glib::Object::new::(&[ ("application-id", &"com.spydr06.logicrs"), ("flags", &gio::ApplicationFlags::HANDLES_OPEN), @@ -54,15 +54,21 @@ impl Application { pub fn apply_clipboard(&self, clipboard: Clipboard) { match clipboard { Clipboard::Blocks(..) => { - let position = self.imp() + let position = self + .imp() .current_circuit_view() .map(|view| view.mouse_world_position()) .unwrap_or_default(); - + match clipboard.paste_to(self.imp().current_plot().unwrap(), position) { Ok(action) => self.new_action(action), - Err(err) => dialogs::run(self.to_owned(), self.active_window().unwrap(), err, dialogs::basic_error) + Err(err) => dialogs::run( + self.to_owned(), + self.active_window().unwrap(), + err, + dialogs::basic_error, + ), } } Clipboard::Module(_) => todo!(), @@ -72,21 +78,29 @@ impl Application { pub fn paste_clipboard(&self) { let display = RootExt::display(&self.active_window().unwrap()); - display.clipboard().read_text_async(None as Option<&gio::Cancellable>, glib::clone!(@weak self as app => move |pasted| { - match pasted - .map_err(|err| err.to_string()) - .and_then(|text| text.ok_or(String::new())) - .and_then(|text| Clipboard::deserialize(text.as_str())) - { - Ok(clipboard) => app.apply_clipboard(clipboard), - Err(err) => warn!("Error pasting from clipboard: {err}") - } - })); + display.clipboard().read_text_async( + None as Option<&gio::Cancellable>, + glib::clone!(@weak self as app => move |pasted| { + match pasted + .map_err(|err| err.to_string()) + .and_then(|text| text.ok_or(String::new())) + .and_then(|text| Clipboard::deserialize(text.as_str())) + { + Ok(clipboard) => app.apply_clipboard(clipboard), + Err(err) => warn!("Error pasting from clipboard: {err}") + } + }), + ); } pub fn cut_clipboard(&self, clipboard: Clipboard) { match clipboard { - Clipboard::Blocks(blocks, connections) => self.new_action(Action::DeleteSelection(self.imp().current_plot().unwrap(), blocks, connections, vec![])), + Clipboard::Blocks(blocks, connections) => self.new_action(Action::DeleteSelection( + self.imp().current_plot().unwrap(), + blocks, + connections, + vec![], + )), Clipboard::Module(_) => todo!(), Clipboard::Empty => {} } @@ -103,7 +117,7 @@ impl Application { self.cut_clipboard(clipboard); } } - Err(err) => warn!("Error serializing clipboard: {err}") + Err(err) => warn!("Error serializing clipboard: {err}"), } } @@ -131,6 +145,16 @@ impl Application { pub(self) fn setup_gactions(&self) { gactions::ACTIONS.iter().for_each(|gaction| { + let mut accels = gaction.accels().to_vec(); + let temp: Vec; + if cfg!(target_os = "macos") { + temp = gaction + .accels() + .iter() + .map(|s| s.replace("", "")) + .collect::>(); + accels = temp.iter().map(|x| &**x).collect::>(); + } let callback = gaction.callback(); let action = gio::SimpleAction::from(gaction); @@ -148,7 +172,7 @@ impl Application { @weak self as app => move |action, parameter| callback(app, action, parameter) )); self.add_action(&action); - self.set_accels_for_action(&format!("app.{}", gaction.name()), gaction.accels()); + self.set_accels_for_action(&format!("app.{}", gaction.name()), &accels); }); } } diff --git a/src/application/selection.rs b/src/application/selection.rs index 653cfd5..1d496f5 100644 --- a/src/application/selection.rs +++ b/src/application/selection.rs @@ -1,19 +1,22 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; -use crate::{renderer::{Renderable, COLOR_THEME, vector::Vector2}, simulator::{Plot, Block, BlockID, SegmentID}}; +use crate::{ + renderer::{vector::Vector2, Renderable, COLOR_THEME}, + simulator::{render_block_connector, render_line, Block, BlockID, Plot, SegmentID}, +}; use std::cmp; #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] pub enum Selectable { Block(BlockID), - Waypoint(SegmentID) + Waypoint(SegmentID), } impl Selectable { pub fn block_id(&self) -> Option { match self { Self::Block(block_id) => Some(*block_id), - _ => None + _ => None, } } } @@ -21,7 +24,7 @@ impl Selectable { #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] pub enum ConnectionSource { Block(BlockID, u8), - Waypoint(SegmentID) + Waypoint(SegmentID), } #[derive(Clone, Serialize, Deserialize, Debug)] @@ -32,19 +35,22 @@ pub enum Selection { MouseEvent(BlockID), MoveBlock(Box), Connection(ConnectionSource, Vector2, Vector2), - None + None, } impl Selection { pub fn connecting(&self) -> bool { - matches!(self, Self::Connection {..} | Self::Single(Selectable::Waypoint(..), ..)) + matches!( + self, + Self::Connection { .. } | Self::Single(Selectable::Waypoint(..), ..) + ) } pub fn blocks(&self) -> Vec { match self { Self::Single(Selectable::Block(block_id), _) => vec![*block_id], Self::Many(selected) => selected.iter().filter_map(|s| s.block_id()).collect(), - _ => vec![] + _ => vec![], } } } @@ -57,33 +63,28 @@ impl Default for Selection { impl Renderable for Selection { fn render(&self, renderer: &R, data: &Plot) -> Result<(), R::Error> - where R: crate::renderer::Renderer { - + where + R: crate::renderer::Renderer, + { match self { Self::Area(start, end) => { let position = cmp::min(start, end); let size = *cmp::max(start, end) - *position; - renderer.rectangle(*position, size) + renderer + .rectangle(*position, size) .set_line_width(1.) .set_color(unsafe { &COLOR_THEME.accent_bg_color }) .fill_preserve()? .set_color(unsafe { &COLOR_THEME.accent_fg_color }) - .stroke().map(|_| ()) + .stroke() + .map(|_| ()) } Self::Connection(_, start, end) => { - let offset = Vector2( - Vector2(start.0 + ((end.0 - start.0) as f32 * 0.7) as i32, start.1), - Vector2(end.0 + ((start.0 - end.0) as f32 * 0.7) as i32, end.1), - ); - - renderer.set_line_width(4.) - .set_color(unsafe { &COLOR_THEME.disabled_bg_color }) - .move_to(*start) - .curve_to(offset.0, offset.1, *end) - .stroke().map(|_| ()) + render_line(false, *start, *end, renderer)?; + render_block_connector(*end, false, false, renderer) } Self::MoveBlock(block) => block.render(renderer, data), - _ => Ok(()) + _ => Ok(()), } } } diff --git a/src/application/template.rs b/src/application/template.rs index dceeee5..662d679 100644 --- a/src/application/template.rs +++ b/src/application/template.rs @@ -3,15 +3,21 @@ use adw::subclass::prelude::*; use std::cell::RefCell; use adw::ColorScheme; use crate::{ - ui::{main_window::MainWindow, circuit_view::CircuitView, dialogs}, - fatal::*, project::*, simulator::*, renderer::Theme, + fatal::*, + project::*, + renderer::Theme, + simulator::*, + ui::{circuit_view::CircuitView, dialogs, main_window::MainWindow}, }; use crate::application::gactions; use crate::application::user_settings::UserSettings; use crate::application::user_settings::UserSettingsKey::ThemeKey; use crate::application::user_settings::UserSettingsValue::ThemeValue; +use adw::subclass::prelude::*; +use gtk::{gdk, gio, glib, prelude::*}; +use std::cell::RefCell; -use super::{action::*, clipboard::Clipboard, Application, selection::*}; +use super::{action::*, clipboard::Clipboard, selection::*, Application}; #[derive(Default)] pub struct ApplicationTemplate { @@ -27,7 +33,8 @@ impl ApplicationTemplate { const CSS_RESOURCE: &'static str = "/style/style.css"; fn start_simulation(&self) { - *self.simulator.borrow_mut() = Some(Simulator::new(self.project.clone(), self.window.clone())) + *self.simulator.borrow_mut() = + Some(Simulator::new(self.project.clone(), self.window.clone())) } fn stop_simulation(&self) { @@ -46,7 +53,7 @@ impl ApplicationTemplate { gtk::StyleContext::add_provider_for_display( &gdk::Display::default().expect("Could not connect to a display."), &provider, - gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, ); // build the application window and UI @@ -65,15 +72,14 @@ impl ApplicationTemplate { } pub fn save(&self, then: fn(&Application)) -> Result<(), String> { - if let Some(file) = self.file.borrow().as_ref() { + if let Some(file) = self.file.borrow().as_ref() { let project = self.project.lock().unwrap(); project.write_to(file)?; if let Some(window) = self.window.borrow().as_ref() { window.set_subtitle(&self.file_name()); } then(&self.instance()); - } - else { + } else { self.instance().save_as(then); } @@ -87,7 +93,7 @@ impl ApplicationTemplate { let mut old = self.project.lock().unwrap(); *old = project; drop(old); - + self.file.replace(file); self.action_stack.borrow_mut().reset(); if let Some(window) = self.window.borrow().as_ref() { @@ -119,7 +125,7 @@ impl ApplicationTemplate { pub fn file_name(&self) -> String { match self.file.borrow().as_ref() { Some(file) => file.path().unwrap().into_os_string().into_string().unwrap(), - None => String::from("New File") + None => String::from("New File"), } } @@ -128,7 +134,9 @@ impl ApplicationTemplate { } pub fn current_circuit_view(&self) -> Option { - self.window.borrow().as_ref() + self.window + .borrow() + .as_ref() .and_then(|window| window.imp().circuit_panel.imp().view.selected_page()) .and_then(|page| page.child().downcast::().ok()) } @@ -150,11 +158,23 @@ impl ApplicationTemplate { } pub fn undo_button(&self) -> gtk::Button { - self.window.borrow().as_ref().unwrap().panel().undo_button().to_owned() + self.window + .borrow() + .as_ref() + .unwrap() + .panel() + .undo_button() + .to_owned() } pub fn redo_button(&self) -> gtk::Button { - self.window.borrow().as_ref().unwrap().panel().redo_button().to_owned() + self.window + .borrow() + .as_ref() + .unwrap() + .panel() + .redo_button() + .to_owned() } pub fn action_stack(&self) -> &RefCell { @@ -188,18 +208,21 @@ impl ApplicationTemplate { .map(|(id, _)| *id) .collect::>(); - delete.iter().for_each(|id| { plot.delete_block(*id); }); + delete.iter().for_each(|id| { + plot.delete_block(*id); + }); }; remove_dependencies(locked.main_plot_mut()); - locked.modules_mut().iter_mut().for_each(|(_, module)| + locked.modules_mut().iter_mut().for_each(|(_, module)| { if let Some(plot) = module.plot_mut() { remove_dependencies(plot); } - ); - + }); + drop(locked); - self.instance().new_action(Action::DeleteModule(self.project.clone(), owned_module)); + self.instance() + .new_action(Action::DeleteModule(self.project.clone(), owned_module)); } } @@ -209,7 +232,12 @@ impl ApplicationTemplate { let module_name = module.name().clone(); let provider = PlotProvider::Module(self.project.clone(), module_name); drop(project); - self.window.borrow().as_ref().unwrap().panel().open_tab(provider); + self.window + .borrow() + .as_ref() + .unwrap() + .panel() + .open_tab(provider); } } } @@ -257,7 +285,12 @@ impl ApplicationImpl for ApplicationTemplate { self.create_window(&self.instance()); self.start_simulation(); - dialogs::run(self.instance().to_owned(), self.instance().active_window().unwrap(), err, dialogs::basic_error); + dialogs::run( + self.instance().to_owned(), + self.instance().active_window().unwrap(), + err, + dialogs::basic_error, + ); } } } diff --git a/src/config.rs b/src/config.rs index e3461e9..23bd968 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,9 @@ -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -pub const APP_ID: &str = env!("CARGO_PKG_NAME"); +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const APP_ID: &str = env!("CARGO_PKG_NAME"); pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); -pub const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); -pub const COPYRIGHT: &str = "© 2022 - 2023 Spydr06"; -pub const REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY"); +pub const AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); +pub const COPYRIGHT: &str = "© 2022 - 2023 Spydr06"; +pub const REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY"); #[cfg(debug_assertions)] pub const APP_ICON_NAME: &str = "com.spydr06.logicrs.Devel"; diff --git a/src/export.rs b/src/export.rs index d6c00e3..76bafc0 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,13 +1,17 @@ -use crate::{simulator::Module, project::Project, FileExtension, application::Application}; +use crate::{application::Application, project::Project, simulator::Module, FileExtension}; -use serde::{Serialize, Deserialize}; use gtk::{gio, prelude::FileExt, subclass::prelude::ObjectSubclassIsExt}; -use std::{fs::{OpenOptions, File}, io::{Write, BufReader}, collections::HashMap}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + fs::{File, OpenOptions}, + io::{BufReader, Write}, +}; #[derive(Serialize, Deserialize)] pub struct ModuleFile { main_name: String, - modules: HashMap + modules: HashMap, } impl FileExtension for ModuleFile { @@ -27,7 +31,7 @@ impl ModuleFile { project.module(&mod_name).map(|module| { let mut mod_file = Self { main_name: mod_name.clone(), - modules: HashMap::from([(mod_name.clone(), module.clone())]) + modules: HashMap::from([(mod_name.clone(), module.clone())]), }; project.collect_dependencies(&mod_name, &mut mod_file.modules); @@ -36,36 +40,46 @@ impl ModuleFile { } pub fn export(&self, file: &gio::File) -> Result<(), String> { - info!("Exporting to `{}`...", file.path().unwrap().to_str().unwrap()); + info!( + "Exporting to `{}`...", + file.path().unwrap().to_str().unwrap() + ); let mut f = OpenOptions::new() .write(true) .truncate(true) .open(file.path().unwrap()) .map_err(|err| err.to_string())?; - let serialized = serde_json::to_string(self) - .map_err(|err| err.to_string())?; - let bytes_written = f.write(serialized.as_bytes()) + let serialized = serde_json::to_string(self).map_err(|err| err.to_string())?; + let bytes_written = f + .write(serialized.as_bytes()) .map_err(|err| err.to_string())?; - info!("Wrote {bytes_written} bytes to `{}` successfully", file.path().unwrap().to_str().unwrap()); + info!( + "Wrote {bytes_written} bytes to `{}` successfully", + file.path().unwrap().to_str().unwrap() + ); Ok(()) } pub fn import(file: &gio::File) -> Result { - let f = File::open(file.path().unwrap()) - .map_err(|err| err.to_string())?; - let mod_file: Self = serde_json::from_reader(BufReader::new(f)) - .map_err(|err| err.to_string())?; + let f = File::open(file.path().unwrap()).map_err(|err| err.to_string())?; + let mod_file: Self = + serde_json::from_reader(BufReader::new(f)).map_err(|err| err.to_string())?; - info!("Imported module `{}` from file `{}`", mod_file.main_name, file.path().unwrap().to_str().unwrap()); + info!( + "Imported module `{}` from file `{}`", + mod_file.main_name, + file.path().unwrap().to_str().unwrap() + ); Ok(mod_file) } - fn check_compat(&self, project: &mut Project) -> Vec { - self.modules.keys() + fn check_compat(&self, project: &Project) -> Vec { + self.modules + .keys() + .filter(|&name| project.module(name).is_some()) .cloned() - .filter(|name| project.module(name).is_some()) .collect::>() } @@ -87,7 +101,8 @@ impl ModuleFile { // construct error message let message = format!( "Error importing `{}`; Conflicting modules exist:\n\t{}", - self.main_name, conflicting.join(",\n\t") + self.main_name, + conflicting.join(",\n\t") ); Err(message) } diff --git a/src/fatal.rs b/src/fatal.rs index 3df63d8..ea1e9c4 100644 --- a/src/fatal.rs +++ b/src/fatal.rs @@ -3,7 +3,9 @@ pub trait FatalResult { } impl FatalResult for Result -where E: std::fmt::Display { +where + E: std::fmt::Display, +{ fn unwrap_or_die(self) -> T { self.unwrap_or_else(|err| die(&err.to_string())) } diff --git a/src/id.rs b/src/id.rs index 90ff914..556a96d 100644 --- a/src/id.rs +++ b/src/id.rs @@ -1,11 +1,13 @@ -use std::hash::Hasher; use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; // Id: Struct that already stores a pre-hashed Uuid value to make HashTable lookups faster -#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive( + Debug, Default, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, +)] pub struct Id(u64); impl Id { diff --git a/src/main.rs b/src/main.rs index c0857dd..f4705da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,14 +5,14 @@ #![feature(if_let_guard)] mod application; -mod ui; -mod renderer; -mod simulator; mod config; -mod fatal; -mod project; mod export; +mod fatal; mod id; +mod project; +mod renderer; +mod simulator; +mod ui; #[macro_use] extern crate log; @@ -32,8 +32,8 @@ trait FileExtension { fn main() { env_logger::init(); - info!("Starting up LogicRs..."); - + info!("Starting up LogicRs..."); + let application = Application::new(); std::process::exit(application.run()); } diff --git a/src/project.rs b/src/project.rs index 9e131b9..0a7e617 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,7 +1,16 @@ -use std::{collections::*, sync::*, fs::{OpenOptions, File}, io::{Write, BufReader}}; -use serde::{Serialize, Deserialize, ser::SerializeStruct}; +use crate::{ + renderer::vector::Vector2, + simulator::{builtin::BUILTINS, *}, + FileExtension, +}; use gtk::{gio, prelude::FileExt}; -use crate::{simulator::{*, builtin::BUILTINS}, renderer::vector::Vector2, FileExtension}; +use serde::{ser::SerializeStruct, Deserialize, Serialize}; +use std::{ + collections::*, + fs::{File, OpenOptions}, + io::{BufReader, Write}, + sync::*, +}; pub type ProjectRef = Arc>; @@ -9,21 +18,32 @@ pub type ProjectRef = Arc>; pub struct Project { modules: HashMap, main_plot: Plot, - tps: i32 + tps: i32, } impl Default for Project { fn default() -> Self { - Self::new(builtin::BUILTINS.iter().map(|(_, builtin)| builtin.module().clone()).collect()) + Self::new( + builtin::BUILTINS + .iter() + .map(|(_, builtin)| builtin.module().clone()) + .collect(), + ) } } impl Serialize for Project { fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer { + where + S: serde::Serializer, + { let mut state = serializer.serialize_struct("Project", 2)?; - state.serialize_field("modules", &HashMap::<&String, &Module>::from_iter(self.modules.iter().filter(|(_, module)| !module.builtin())))?; + state.serialize_field( + "modules", + &HashMap::<&String, &Module>::from_iter( + self.modules.iter().filter(|(_, module)| !module.builtin()), + ), + )?; state.serialize_field("main_plot", &self.main_plot)?; state.serialize_field("tps", &self.tps)?; state.end() @@ -45,40 +65,55 @@ impl FileExtension for Project { impl Project { pub fn new(modules: Vec) -> Self { Self { - modules: modules.iter().map(|module| (module.name().to_owned(), module.clone())).collect(), + modules: modules + .iter() + .map(|module| (module.name().to_owned(), module.clone())) + .collect(), main_plot: Plot::new(), - tps: Simulator::DEFAULT_TICKS_PER_SECOND + tps: Simulator::DEFAULT_TICKS_PER_SECOND, } } pub fn load_from(file: &gio::File) -> Result { - let f = File::open(file.path().unwrap()) - .map_err(|err| err.to_string())?; - let mut project: Self = serde_json::from_reader(BufReader::new(f)) - .map_err(|err| err.to_string())?; - - BUILTINS.iter().for_each(|(_, builtin)| project.add_module(builtin.module().clone())); - - info!("Loaded from file `{}`", file.path().unwrap().to_str().unwrap()); - - project.iter_plots_mut().for_each(|plot| plot.update_all_blocks()); + let f = File::open(file.path().unwrap()).map_err(|err| err.to_string())?; + let mut project: Self = + serde_json::from_reader(BufReader::new(f)).map_err(|err| err.to_string())?; + + BUILTINS + .iter() + .for_each(|(_, builtin)| project.add_module(builtin.module().clone())); + + info!( + "Loaded from file `{}`", + file.path().unwrap().to_str().unwrap() + ); + + project + .iter_plots_mut() + .for_each(|plot| plot.update_all_blocks()); Ok(project) } pub fn write_to(&self, file: &gio::File) -> Result<(), String> { - info!("Writing to `{}` ...", file.path().unwrap().to_str().unwrap()); + info!( + "Writing to `{}` ...", + file.path().unwrap().to_str().unwrap() + ); let mut f = OpenOptions::new() .write(true) .truncate(true) .open(file.path().unwrap()) .map_err(|err| err.to_string())?; - let serialized = serde_json::to_string(self) - .map_err(|err| err.to_string())?; - let bytes_written = f.write(serialized.as_bytes()) + let serialized = serde_json::to_string(self).map_err(|err| err.to_string())?; + let bytes_written = f + .write(serialized.as_bytes()) .map_err(|err| err.to_string())?; - info!("Wrote {bytes_written} bytes to `{}` successfully", file.path().unwrap().to_str().unwrap()); + info!( + "Wrote {bytes_written} bytes to `{}` successfully", + file.path().unwrap().to_str().unwrap() + ); Ok(()) } @@ -106,12 +141,26 @@ impl Project { if module.plot().is_some() && !module.has_io_blocks() { let num_inputs = module.get_num_inputs(); let num_outputs = module.get_num_outputs(); - + let input_module = self.modules.get(&*builtin::INPUT_MODULE_NAME).unwrap(); - let input_block = Block::new_sized(&input_module, Vector2(50, 50), true, num_inputs, num_inputs, None); - + let input_block = Block::new_sized( + &input_module, + Vector2(50, 50), + true, + num_inputs, + num_inputs, + None, + ); + let output_module = self.modules.get(&*builtin::OUTPUT_MODULE_NAME).unwrap(); - let output_block = Block::new_sized(&output_module, Vector2(400, 50), true, num_outputs, num_outputs, None); + let output_block = Block::new_sized( + &output_module, + Vector2(400, 50), + true, + num_outputs, + num_outputs, + None, + ); module.set_io_blocks(input_block.id(), output_block.id()); @@ -133,7 +182,9 @@ impl Project { } pub fn plot(&self, module_name: &String) -> Option<&Plot> { - self.modules.get(module_name).and_then(|module| module.plot()) + self.modules + .get(module_name) + .and_then(|module| module.plot()) } pub fn main_plot_mut(&mut self) -> &mut Plot { @@ -141,11 +192,14 @@ impl Project { } pub fn plot_mut(&mut self, module_name: &String) -> Option<&mut Plot> { - self.modules.get_mut(module_name).and_then(|module| module.plot_mut()) + self.modules + .get_mut(module_name) + .and_then(|module| module.plot_mut()) } pub fn iter_plots_mut(&mut self) -> impl Iterator { - self.modules.iter_mut() + self.modules + .iter_mut() .filter_map(|(_, module)| module.plot_mut()) .chain(std::iter::once(&mut self.main_plot)) } @@ -160,14 +214,16 @@ impl Project { pub fn collect_dependencies(&self, mod_name: &String, modules: &mut HashMap) { if let Some(plot) = self.modules.get(mod_name).and_then(|module| module.plot()) { - plot.blocks() - .iter() - .for_each(|(_, block)| { - if let Some(module) = self.modules.get(block.module_id()).filter(|m| !m.builtin() && !modules.contains_key(m.name())) { - modules.insert(module.name().clone(), module.clone()); - self.collect_dependencies(module.name(), modules); - } - }) + plot.blocks().iter().for_each(|(_, block)| { + if let Some(module) = self + .modules + .get(block.module_id()) + .filter(|m| !m.builtin() && !modules.contains_key(m.name())) + { + modules.insert(module.name().clone(), module.clone()); + self.collect_dependencies(module.name(), modules); + } + }) } } } diff --git a/src/renderer/cairo.rs b/src/renderer/cairo.rs index 75a0c7a..e5a41ad 100644 --- a/src/renderer/cairo.rs +++ b/src/renderer/cairo.rs @@ -1,11 +1,7 @@ -use crate::{simulator::Plot, application::selection::*}; +use crate::{application::selection::*, simulator::Plot}; use super::*; -use gtk::cairo::{ - Context, - Antialias, - Error, FontFace -}; +use gtk::cairo::{Antialias, Context, Error, FontFace}; pub struct CairoRenderer { size: Vector2, @@ -14,7 +10,7 @@ pub struct CairoRenderer { original_translation: Vector2, font: FontFace, context: Option, - editor_mode: EditorMode + editor_mode: EditorMode, } impl CairoRenderer { @@ -26,7 +22,12 @@ impl CairoRenderer { original_translation: Vector2::default(), context: None, editor_mode: EditorMode::default(), - font: FontFace::toy_create("Cascadia Code", gtk::cairo::FontSlant::Normal, gtk::cairo::FontWeight::Normal).unwrap() + font: FontFace::toy_create( + "Cascadia Code", + gtk::cairo::FontSlant::Normal, + gtk::cairo::FontWeight::Normal, + ) + .unwrap(), } } @@ -49,15 +50,26 @@ impl CairoRenderer { } impl Default for CairoRenderer { - fn default() -> Self { Self::new() } + fn default() -> Self { + Self::new() + } } impl Renderer for CairoRenderer { type Context = cairo::Context; type Error = cairo::Error; - fn callback(&mut self, plot: &Plot, mode: EditorMode, _area: &DrawingArea, context: &Self::Context, width: i32, height: i32) -> Result<&mut Self, Self::Error> { - self.set_size(Vector2(width, height)).set_context(Some(context.clone())); + fn callback( + &mut self, + plot: &Plot, + mode: EditorMode, + _area: &DrawingArea, + context: &Self::Context, + width: i32, + height: i32, + ) -> Result<&mut Self, Self::Error> { + self.set_size(Vector2(width, height)) + .set_context(Some(context.clone())); self.set_editor_mode(mode); if width == 0 || height == 0 { return Ok(self); @@ -67,7 +79,7 @@ impl Renderer for CairoRenderer { context.set_antialias(Antialias::Default); context.translate(self.translation.x(), self.translation.y()); context.scale(self.scale, self.scale); - + context.set_font_face(&self.font); context.set_font_size(DEFAULT_FONT_SIZE); @@ -78,7 +90,7 @@ impl Renderer for CairoRenderer { // draw the editor grid if enabled mode.render(self, plot)?; - + // draw the actual contents of the editor plot.render(self, plot)?; @@ -122,7 +134,12 @@ impl Renderer for CairoRenderer { #[inline] fn set_color(&self, color: &Color) -> &Self { if let Some(context) = &self.context { - context.set_source_rgba(color.0 as f64, color.1 as f64, color.2 as f64, color.3 as f64); + context.set_source_rgba( + color.0 as f64, + color.1 as f64, + color.2 as f64, + color.3 as f64, + ); } self } @@ -147,7 +164,7 @@ impl Renderer for CairoRenderer { fn fill(&self) -> Result<&Self, Self::Error> { match &self.context { Some(context) => context.fill().map(|_| self), - None => Ok(self) // TODO: error handling + None => Ok(self), // TODO: error handling } } @@ -155,7 +172,7 @@ impl Renderer for CairoRenderer { fn fill_preserve(&self) -> Result<&Self, Self::Error> { match &self.context { Some(context) => context.fill_preserve().map(|_| self), - None => Ok(self) // TODO: error handling + None => Ok(self), // TODO: error handling } } @@ -163,7 +180,7 @@ impl Renderer for CairoRenderer { fn stroke(&self) -> Result<&Self, Self::Error> { match &self.context { Some(context) => context.stroke().map(|_| self), - None => Ok(self) // TODO: error handling + None => Ok(self), // TODO: error handling } } @@ -171,7 +188,7 @@ impl Renderer for CairoRenderer { fn show_text(&self, text: &str) -> Result<&Self, Error> { match &self.context { Some(context) => context.show_text(text).map(|_| self), - None => Ok(self) + None => Ok(self), } } @@ -186,7 +203,12 @@ impl Renderer for CairoRenderer { #[inline] fn rectangle(&self, position: Vector2, size: Vector2) -> &Self { if let Some(context) = &self.context { - context.rectangle(position.0 as f64, position.1 as f64, size.0 as f64, size.1 as f64); + context.rectangle( + position.0 as f64, + position.1 as f64, + size.0 as f64, + size.1 as f64, + ); } self } @@ -203,9 +225,12 @@ impl Renderer for CairoRenderer { fn curve_to(&self, start: Vector2, mid: Vector2, end: Vector2) -> &Self { if let Some(context) = &self.context { context.curve_to( - start.0 as f64, start.1 as f64, - mid.0 as f64, mid.1 as f64, - end.0 as f64, end.1 as f64 + start.0 as f64, + start.1 as f64, + mid.0 as f64, + mid.1 as f64, + end.0 as f64, + end.1 as f64, ); } self diff --git a/src/renderer/color.rs b/src/renderer/color.rs index f4a5793..73bf71f 100644 --- a/src/renderer/color.rs +++ b/src/renderer/color.rs @@ -24,9 +24,9 @@ impl IntoRGBA for Color { pub const fn hex_to_color(hex: Hex) -> (f32, f32, f32, f32) { ( ((hex >> 16) & 0xff) as f32 / 255.0, - ((hex >> 8) & 0xff) as f32 / 255.0, - (hex & 0xff) as f32 / 255.0, - ((hex >> 24) & 0xff) as f32 / 255.0 + ((hex >> 8) & 0xff) as f32 / 255.0, + (hex & 0xff) as f32 / 255.0, + ((hex >> 24) & 0xff) as f32 / 255.0, ) } @@ -41,7 +41,7 @@ pub struct Theme { pub block_fg_color: Color, pub grid_color: Color, - + // accent colors (selection, etc.) pub accent_bg_color: Color, pub accent_fg_color: Color, @@ -63,17 +63,21 @@ pub struct Theme { impl From<&adw::StyleManager> for Theme { fn from(style_manager: &adw::StyleManager) -> Self { - if style_manager.is_dark() { Self::DARK } else { Self::LIGHT } + if style_manager.is_dark() { + Self::DARK + } else { + Self::LIGHT + } } } impl Theme { pub fn init() { let style_manager = adw::StyleManager::default(); - style_manager.connect_dark_notify(|style_manager| unsafe { + style_manager.connect_dark_notify(|style_manager| unsafe { COLOR_THEME = Self::from(style_manager) }); - unsafe { COLOR_THEME = Self::from(&style_manager) } + unsafe { COLOR_THEME = Self::from(&style_manager) } } const DARK: Self = Self { diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index bfd7d99..686b41c 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -4,8 +4,8 @@ pub mod cairo; pub mod color; pub mod vector; +use crate::{application::editor::EditorMode, simulator::Plot}; pub use {cairo::*, color::*}; -use crate::{simulator::Plot, application::editor::EditorMode}; use self::vector::*; @@ -18,7 +18,8 @@ pub type ScreenSpace = Vector2>; pub trait Renderable { fn render(&self, renderer: &R, data: &Plot) -> Result<(), R::Error> - where R: Renderer; + where + R: Renderer; } pub trait Renderer: Default { @@ -26,7 +27,15 @@ pub trait Renderer: Default { type Error; // render callback - fn callback(&mut self, data: &Plot, mode: EditorMode, area: &DrawingArea, context: &Self::Context, width: i32, height: i32) -> Result<&mut Self, Self::Error>; + fn callback( + &mut self, + data: &Plot, + mode: EditorMode, + area: &DrawingArea, + context: &Self::Context, + width: i32, + height: i32, + ) -> Result<&mut Self, Self::Error>; // getter/setter fn translate(&mut self, translation: Vector2) -> &mut Self; @@ -44,7 +53,7 @@ pub trait Renderer: Default { fn screen_space(&self) -> ScreenSpace { Vector2( self.screen_to_world(Vector2::default()), - self.screen_to_world(Vector2(self.size().0 as f64, self.size().1 as f64)) + self.screen_to_world(Vector2(self.size().0 as f64, self.size().1 as f64)), ) } @@ -59,7 +68,7 @@ pub trait Renderer: Default { fn zoom(&mut self, amount: f64, screen_position: Option>) { let screen_position = match screen_position { Some(position) => position, - None => (self.size().0 as f64 / 2., self.size().1 as f64 / 2.).into() + None => (self.size().0 as f64 / 2., self.size().1 as f64 / 2.).into(), }; let p = self.screen_to_world(screen_position); @@ -91,30 +100,30 @@ pub trait Renderer: Default { self.line_to(position + Vector2(size.0 - radius, 0)); self.curve_to( - Vector2(position.0 + size.0 - radius, position.1), - Vector2(position.0 + size.0, position.1), - Vector2(position.0 + size.0, position.1 + radius), + Vector2(position.0 + size.0 - radius, position.1), + Vector2(position.0 + size.0, position.1), + Vector2(position.0 + size.0, position.1 + radius), ); - + self.line_to(Vector2(position.0 + size.0, position.1 + size.1 - radius)); self.curve_to( Vector2(position.0 + size.0, position.1 + size.1 - radius), Vector2(position.0 + size.0, position.1 + size.1), Vector2(position.0 + size.0 - radius, position.1 + size.1), ); - + self.line_to(Vector2(position.0 + radius, position.1 + size.1)); self.curve_to( Vector2(position.0 + radius, position.1 + size.1), - Vector2 (position.0, position.1 + size.1), - Vector2(position.0, position.1 + size.1 - radius) + Vector2(position.0, position.1 + size.1), + Vector2(position.0, position.1 + size.1 - radius), ); - + self.line_to(Vector2(position.0, position.1 + radius)); self.curve_to( Vector2(position.0, position.1 + radius), position, - Vector2(position.0 + radius, position.1) + Vector2(position.0 + radius, position.1), ) } @@ -122,18 +131,18 @@ pub trait Renderer: Default { self.move_to(Vector2(position.0 + radius, position.1)); self.line_to(Vector2(position.0 + size.0 - radius, position.1)); self.curve_to( - Vector2(position.0 + size.0 - radius, position.1), - Vector2(position.0 + size.0, position.1), - Vector2(position.0 + size.0, position.1 + radius), + Vector2(position.0 + size.0 - radius, position.1), + Vector2(position.0 + size.0, position.1), + Vector2(position.0 + size.0, position.1 + radius), ); - + self.line_to(Vector2(position.0 + size.0, position.1 + size.1)); self.line_to(Vector2(position.0, position.1 + size.1)); self.line_to(Vector2(position.0, position.1 + radius)); self.curve_to( Vector2(position.0, position.1 + radius), position, - Vector2(position.0 + radius, position.1) + Vector2(position.0 + radius, position.1), ) } } diff --git a/src/renderer/vector.rs b/src/renderer/vector.rs index 7443cd7..ba5305a 100644 --- a/src/renderer/vector.rs +++ b/src/renderer/vector.rs @@ -1,6 +1,6 @@ use std::ops::*; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; pub trait VectorCast { fn cast(value: Self) -> Vector2; @@ -12,7 +12,7 @@ pub struct Vector2(pub T, pub T); impl Vector2 { pub fn new(x: T, y: T) -> Self { Self(x, y) - } + } #[inline] pub fn x(&self) -> T { @@ -107,4 +107,4 @@ impl DivAssign for Vector2 { self.0 /= rhs.0; self.1 /= rhs.1; } -} \ No newline at end of file +} diff --git a/src/simulator/block.rs b/src/simulator/block.rs index 964049f..8d476d8 100644 --- a/src/simulator/block.rs +++ b/src/simulator/block.rs @@ -1,13 +1,21 @@ -use std::{f64, cmp, collections::{HashSet, HashMap}}; - -use crate::{renderer::{*, vector::Vector2}, application::selection::SelectionField, id::Id}; -use serde::{Serialize, Deserialize}; +use std::{ + cmp, + collections::{HashMap, HashSet}, + f64, +}; + +use crate::{ + application::selection::SelectionField, + id::Id, + renderer::{vector::Vector2, *}, +}; +use serde::{Deserialize, Serialize}; use super::*; pub enum Connector { Input(u8), - Output(u8) + Output(u8), } pub type BlockID = Id; @@ -30,9 +38,9 @@ pub struct Block { state: State, output_state: u128, - + decoration: Decoration, - color: Option + color: Option, } impl Identifiable for Block { @@ -42,14 +50,21 @@ impl Identifiable for Block { impl Block { pub const MAX_CONNECTIONS: u8 = 128; - pub fn new_sized(module: &&Module, position: Vector2, unique: bool, num_inputs: u8, num_outputs: u8, color: Option) -> Self { + pub fn new_sized( + module: &&Module, + position: Vector2, + unique: bool, + num_inputs: u8, + num_outputs: u8, + color: Option, + ) -> Self { let name = module.name().clone(); Self { id: Id::new(), position, size: Vector2( cmp::max(75, (name.len() * 10) as i32), - cmp::max(num_inputs, num_outputs) as i32 * 25 + 50 + cmp::max(num_inputs, num_outputs) as i32 * 25 + 50, ), highlighted: false, unique, @@ -57,15 +72,26 @@ impl Block { inputs: vec![None; num_inputs as usize], outputs: vec![None; num_outputs as usize], name, - state: if module.builtin() { State::Direct(0) } else { State::Inherit(PlotState::default()) }, + state: if module.builtin() { + State::Direct(0) + } else { + State::Inherit(PlotState::default()) + }, decoration: module.decoration().clone(), color, - output_state: 0 + output_state: 0, } } pub fn new(module: &&Module, position: Vector2, color: Option) -> Self { - Self::new_sized(module, position, false, module.get_num_inputs(), module.get_num_outputs(), color) + Self::new_sized( + module, + position, + false, + module.get_num_inputs(), + module.get_num_outputs(), + color, + ) } pub fn set_color(&mut self, mut color: Option) { @@ -101,17 +127,17 @@ impl Block { } pub fn is_in_area(&self, area: &Vector2>) -> bool { - !( - self.position.0 > area.1.0 as i32 || - self.position.1 > area.1.1 as i32 || - self.position.0 + self.size.0 < area.0.0 as i32 || - self.position.1 + self.size.1 < area.0.1 as i32 - ) + !(self.position.0 > area.1 .0 as i32 + || self.position.1 > area.1 .1 as i32 + || self.position.0 + self.size.0 < area.0 .0 as i32 + || self.position.1 + self.size.1 < area.0 .1 as i32) } pub fn touches(&self, point: Vector2) -> bool { - point.0 > self.position.0 - 3 && point.0 < self.position.0 + self.size.0 + 3 && - point.1 > self.position.1 - 3 && point.1 < self.position.1 + self.size.1 + 3 + point.0 > self.position.0 - 3 + && point.0 < self.position.0 + self.size.0 + 3 + && point.1 > self.position.1 - 3 + && point.1 < self.position.1 + self.size.1 + 3 } pub fn set_highlighted(&mut self, highlighted: bool) { @@ -135,7 +161,11 @@ impl Block { } pub fn connected_to(&self) -> Vec { - self.inputs.iter().chain(self.outputs.iter()).filter_map(|a| *a).collect() + self.inputs + .iter() + .chain(self.outputs.iter()) + .filter_map(|a| *a) + .collect() } pub fn connections_mut(&mut self) -> impl Iterator> { @@ -145,14 +175,21 @@ impl Block { pub fn get_connector_pos(&self, connector: Connector) -> Vector2 { match connector { Connector::Input(i) => Vector2(self.position.0, self.position.1 + 25 * i as i32 + 50), - Connector::Output(i) => Vector2(self.position.0 + self.size.0, self.position.1 + 25 * i as i32 + 50) + Connector::Output(i) => Vector2( + self.position.0 + self.size.0, + self.position.1 + 25 * i as i32 + 50, + ), } } - pub fn set_connection(&mut self, connector: Connector, connection: Option) -> &mut Self { + pub fn set_connection( + &mut self, + connector: Connector, + connection: Option, + ) -> &mut Self { match connector { Connector::Input(index) => self.inputs[index as usize] = connection, - Connector::Output(index) => self.outputs[index as usize] = connection + Connector::Output(index) => self.outputs[index as usize] = connection, } self } @@ -160,14 +197,14 @@ impl Block { pub fn connection(&self, connector: Connector) -> Option { match connector { Connector::Input(port) => self.inputs[port as usize], - Connector::Output(port) => self.outputs[port as usize] + Connector::Output(port) => self.outputs[port as usize], } } pub fn outputs(&self) -> &Vec> { &self.outputs } - + pub fn outputs_mut(&mut self) -> &mut Vec> { &mut self.outputs } @@ -207,7 +244,7 @@ impl Block { pub fn bytes(&self) -> u128 { match self.state { State::Direct(bytes) => bytes, - _ => panic!() + _ => panic!(), } } @@ -221,11 +258,13 @@ impl Block { pub fn on_mouse_press(&mut self, mut position: Vector2) -> bool { position -= self.position; - if position.0 > 15 && position.1 > 25 && - position.0 < self.size.0 - 15 && position.1 < self.size.1 - 10 { + if position.0 > 15 + && position.1 > 25 + && position.0 < self.size.0 - 15 + && position.1 < self.size.1 - 10 + { self.decoration.on_mouse_press() - } - else { + } else { false } } @@ -238,15 +277,21 @@ impl Block { if is_input { for i in 0..self.inputs.len() { let connector_pos = (self.position.0, self.position.1 + 25 * i as i32 + 50); - if (position.0 - connector_pos.0).abs() < Segment::HITBOX_SIZE && (position.1 - connector_pos.1).abs() < Segment::HITBOX_SIZE { + if (position.0 - connector_pos.0).abs() < Segment::HITBOX_SIZE + && (position.1 - connector_pos.1).abs() < Segment::HITBOX_SIZE + { return Some(i as u8); } } - } - else { + } else { for i in 0..self.outputs.len() { - let connector_pos = (self.position.0 + self.size.0, self.position.1 + 25 * i as i32 + 50); - if (position.0 - connector_pos.0).abs() < Segment::HITBOX_SIZE && (position.1 - connector_pos.1).abs() < Segment::HITBOX_SIZE { + let connector_pos = ( + self.position.0 + self.size.0, + self.position.1 + 25 * i as i32 + 50, + ); + if (position.0 - connector_pos.0).abs() < Segment::HITBOX_SIZE + && (position.1 - connector_pos.1).abs() < Segment::HITBOX_SIZE + { return Some(i as u8); } } @@ -254,25 +299,36 @@ impl Block { None } - pub fn simulate(&mut self, connections: &mut HashMap, to_update: &mut HashSet, queued: &mut HashSet, project: &mut Project, call_stack: &mut HashSet) -> SimResult<()> { + pub fn simulate( + &mut self, + connections: &mut HashMap, + to_update: &mut HashSet, + queued: &mut HashSet, + project: &mut Project, + call_stack: &mut HashSet, + ) -> SimResult<()> { // collect input states let inputs = self.inputs.collect(connections); - + let mut_ref_ptr = project as *mut Project; if let Some(module) = project.module_mut(&self.name) { // simulate the block - self.output_state = module.simulate(inputs, self, unsafe { &mut *mut_ref_ptr }, call_stack)?; + self.output_state = + module.simulate(inputs, self, unsafe { &mut *mut_ref_ptr }, call_stack)?; // dissect output state for (i, connection_id) in self.outputs.iter().enumerate() { - if let Some(connection) = connection_id.map(|connection_id| connections.get_mut(&connection_id)).flatten() { + if let Some(connection) = connection_id + .map(|connection_id| connections.get_mut(&connection_id)) + .flatten() + { let active = (self.output_state >> i as u128) & 1 != 0; if active != connection.is_active() { - for dest_id in connection.destinations().iter().map(|dest| dest.block_id()) { + for dest_id in connection.destinations().iter().map(|dest| dest.block_id()) + { if dest_id == self.id { queued.insert(dest_id); - } - else { + } else { to_update.insert(dest_id); } } @@ -280,8 +336,7 @@ impl Block { } } } - } - else { + } else { error!("no module named {} found", self.name); } @@ -289,50 +344,83 @@ impl Block { } } - impl Renderable for Block { fn render(&self, renderer: &R, plot: &Plot) -> Result<(), R::Error> - where R: Renderer + where + R: Renderer, { - let border_color = self.color.as_ref().unwrap_or(unsafe { &COLOR_THEME.border_color }); + let border_color = self + .color + .as_ref() + .unwrap_or(unsafe { &COLOR_THEME.border_color }); renderer.set_line_width(2.); - renderer.rounded_rect(self.position, self.size, 5) - .set_color(unsafe { &COLOR_THEME.block_bg_color }).fill()?; + renderer + .rounded_rect(self.position, self.size, 5) + .set_color(unsafe { &COLOR_THEME.block_bg_color }) + .fill()?; - renderer.top_rounded_rect(self.position, Vector2(self.size.0, 25), 5) + renderer + .top_rounded_rect(self.position, Vector2(self.size.0, 25), 5) .set_color(border_color) .fill()?; - renderer.move_to(Vector2(self.position.0 + 5, self.position.1 + 18)) + renderer + .move_to(Vector2(self.position.0 + 5, self.position.1 + 18)) .set_color(unsafe { &COLOR_THEME.block_fg_color }) .show_text(self.name.as_str())?; renderer.rounded_rect(self.position, self.size, 5); match self.highlighted { true => renderer.set_color(unsafe { &COLOR_THEME.accent_fg_color }), - false => renderer.set_color(border_color) + false => renderer.set_color(border_color), }; renderer.stroke()?; let show_suggestion = plot.selection().connecting(); - let connector = |position, is_input, is_active| + let connector = |position, is_input, is_active| { renderer .arc(position, 6., 0., f64::consts::TAU) - .set_color(unsafe {if show_suggestion && is_input { &COLOR_THEME.suggestion_fg_color } else if is_active { &COLOR_THEME.enabled_fg_color } else { &COLOR_THEME.disabled_fg_color }} ) + .set_color(unsafe { + if show_suggestion && is_input { + &COLOR_THEME.suggestion_fg_color + } else if is_active { + &COLOR_THEME.enabled_fg_color + } else { + &COLOR_THEME.disabled_fg_color + } + }) .fill_preserve()? - .set_color(unsafe {if self.highlighted { &COLOR_THEME.accent_fg_color } else { border_color }}) - .stroke(); + .set_color(unsafe { + if self.highlighted { + &COLOR_THEME.accent_fg_color + } else { + border_color + } + }) + .stroke() + }; renderer.set_line_width(1.); for (i, _) in self.inputs.iter().enumerate().filter(|(_, c)| c.is_none()) { - connector(Vector2(self.position.0, self.position.1 + 25 * i as i32 + 50), true, false)?; + connector( + Vector2(self.position.0, self.position.1 + 25 * i as i32 + 50), + true, + false, + )?; } for (i, _) in self.outputs.iter().enumerate().filter(|(_, c)| c.is_none()) { - connector(Vector2(self.position.0 + self.size.0, self.position.1 + 25 * i as i32 + 50), false, (self.output_state >> i as u128) & 1 != 0)?; + connector( + Vector2( + self.position.0 + self.size.0, + self.position.1 + 25 * i as i32 + 50, + ), + false, + (self.output_state >> i as u128) & 1 != 0, + )?; } self.decoration.render(renderer, self).map(|_| ()) } -} \ No newline at end of file +} diff --git a/src/simulator/builtin.rs b/src/simulator/builtin.rs index f34bb01..416084f 100644 --- a/src/simulator/builtin.rs +++ b/src/simulator/builtin.rs @@ -1,19 +1,19 @@ use std::collections::HashMap; -use crate::simulator::{Decoration, Category}; +use crate::simulator::{Category, Decoration}; -use super::{Module, SimulatorFn, Block}; +use super::{Block, Module, SimulatorFn}; pub struct Builtin { module: Module, - simulator_fn: SimulatorFn + simulator_fn: SimulatorFn, } impl Builtin { pub fn new(module: Module, simulator_fn: SimulatorFn) -> Builtin { Self { module, - simulator_fn + simulator_fn, } } @@ -32,98 +32,324 @@ lazy_static! { pub static ref BUILTINS: HashMap<&'static str, Builtin> = { let mut builtins = HashMap::new(); - builtins.insert("Low", Builtin::new( - Module::new_builtin("Low", Category::Basic, 0, 1, Decoration::Label("0".to_string())), - |_, _| { 0 } - )); - - builtins.insert("High", Builtin::new( - Module::new_builtin("High", Category::Basic, 0, 1, Decoration::Label("1".to_string())), - |_, _| { std::u128::MAX } - )); - - builtins.insert("And", Builtin::new( - Module::new_builtin("And", Category::Gate, 2, 1, Decoration::Label(String::from("&"))), - |input, _| (input == 0b11) as u128 - )); - - builtins.insert("Nand", Builtin::new( - Module::new_builtin("Nand", Category::Gate, 2, 1, Decoration::NotLabel(String::from("&"))), - |input, _| (input != 0b11) as u128 - )); - - builtins.insert("Or", Builtin::new( - Module::new_builtin("Or", Category::Gate, 2, 1, Decoration::Label(String::from("≥1"))), - |input, _| (input != 0b00) as u128 - )); - - builtins.insert("Nor", Builtin::new( - Module::new_builtin("Nor", Category::Gate, 2, 1, Decoration::NotLabel(String::from("≥1"))), - |input, _| (input == 0b00) as u128 - )); - - builtins.insert("Not", Builtin::new( - Module::new_builtin("Not", Category::Gate, 1, 1, Decoration::NotLabel(String::from("1"))), - |input, _| !input - )); - - builtins.insert("Xor", Builtin::new( - Module::new_builtin("Xor", Category::Gate, 2, 1, Decoration::Label(String::from("=1"))), - |input, _| (input == 0b01 || input == 0b10) as u128 - )); - - builtins.insert("Xnor", Builtin::new( - Module::new_builtin("Xnor", Category::Gate, 2, 1, Decoration::NotLabel(String::from("=1"))), - |input, _| (input == 0b00 || input == 0b11) as u128 - )); - - builtins.insert("Button", Builtin::new( - Module::new_builtin("Button", Category::InputOutput, 0, 1, Decoration::Button(false)), - |_, instance| instance.is_active() as u128 - )); - - builtins.insert("Switch", Builtin::new( - Module::new_builtin("Switch", Category::InputOutput, 0, 1, Decoration::Switch(false)), - |_, instance| instance.is_active() as u128 - )); - - builtins.insert("Lamp", Builtin::new( - Module::new_builtin("Lamp", Category::InputOutput, 1, 0, Decoration::Lamp(false)), - |input, instance| { - instance.set_active(input & 0b01 == 0b01); - 0 - } - )); - - builtins.insert("Input", Builtin::new( - Module::new_builtin("Input", Category::Hidden, Block::MAX_CONNECTIONS, Block::MAX_CONNECTIONS, Decoration::Label("|>".to_string())), - |input, instance| if instance.passthrough() { input } else { instance.bytes() } - )); - - builtins.insert("Output", Builtin::new( - Module::new_builtin("Output", Category::Hidden, Block::MAX_CONNECTIONS, Block::MAX_CONNECTIONS, Decoration::Label(">|".to_string())), - |input, instance| { instance.set_bytes(input); input } - )); - - builtins.insert("SR Nand Latch", Builtin::new( - Module::new_builtin("SR Nand Latch", Category::Latch, 2, 2, Decoration::NotLabel("SR".to_string())), - sr_nand_latch - )); - - builtins.insert("SR Latch", Builtin::new( - Module::new_builtin("SR Latch", Category::Latch, 2, 1, Decoration::Label("SR".to_string())), - sr_latch - )); - - builtins.insert("JK Latch", Builtin::new( - Module::new_builtin("JK Latch", Category::Latch, 2, 1, Decoration::Label("JK".to_string())), - jk_latch - )); - - builtins.insert("T Flip-Flop", Builtin::new( - Module::new_builtin("T Flip-Flop", Category::FlipFlop, 1, 1, Decoration::Label("T".to_string())), - t_flip_flop - )); + builtins.insert( + "Low", + Builtin::new( + Module::new_builtin( + "Low", + Category::Basic, + 0, + 1, + Decoration::Label("0".to_string()), + ), + |_, _| 0, + ), + ); + + builtins.insert( + "High", + Builtin::new( + Module::new_builtin( + "High", + Category::Basic, + 0, + 1, + Decoration::Label("1".to_string()), + ), + |_, _| std::u128::MAX, + ), + ); + + builtins.insert( + "And", + Builtin::new( + Module::new_builtin( + "And", + Category::Gate, + 2, + 1, + Decoration::Label(String::from("&")), + ), + |input, _| (input == 0b11) as u128, + ), + ); + + builtins.insert( + "Nand", + Builtin::new( + Module::new_builtin( + "Nand", + Category::Gate, + 2, + 1, + Decoration::NotLabel(String::from("&")), + ), + |input, _| (input != 0b11) as u128, + ), + ); + + builtins.insert( + "Or", + Builtin::new( + Module::new_builtin( + "Or", + Category::Gate, + 2, + 1, + Decoration::Label(String::from("≥1")), + ), + |input, _| (input != 0b00) as u128, + ), + ); + + builtins.insert( + "Nor", + Builtin::new( + Module::new_builtin( + "Nor", + Category::Gate, + 2, + 1, + Decoration::NotLabel(String::from("≥1")), + ), + |input, _| (input == 0b00) as u128, + ), + ); + + builtins.insert( + "Not", + Builtin::new( + Module::new_builtin( + "Not", + Category::Gate, + 1, + 1, + Decoration::NotLabel(String::from("1")), + ), + |input, _| !input, + ), + ); + + builtins.insert( + "Xor", + Builtin::new( + Module::new_builtin( + "Xor", + Category::Gate, + 2, + 1, + Decoration::Label(String::from("=1")), + ), + |input, _| (input == 0b01 || input == 0b10) as u128, + ), + ); + + builtins.insert( + "Xnor", + Builtin::new( + Module::new_builtin( + "Xnor", + Category::Gate, + 2, + 1, + Decoration::NotLabel(String::from("=1")), + ), + |input, _| (input == 0b00 || input == 0b11) as u128, + ), + ); + + builtins.insert( + "Button", + Builtin::new( + Module::new_builtin( + "Button", + Category::InputOutput, + 0, + 1, + Decoration::Button(false), + ), + |_, instance| instance.is_active() as u128, + ), + ); + + builtins.insert( + "Switch", + Builtin::new( + Module::new_builtin( + "Switch", + Category::InputOutput, + 0, + 1, + Decoration::Switch(false), + ), + |_, instance| instance.is_active() as u128, + ), + ); + + builtins.insert( + "Lamp", + Builtin::new( + Module::new_builtin("Lamp", Category::InputOutput, 1, 0, Decoration::Lamp(false)), + |input, instance| { + instance.set_active(input & 0b01 == 0b01); + 0 + }, + ), + ); + + builtins.insert( + "Input", + Builtin::new( + Module::new_builtin( + "Input", + Category::Hidden, + Block::MAX_CONNECTIONS, + Block::MAX_CONNECTIONS, + Decoration::Label("|>".to_string()), + ), + |input, instance| { + if instance.passthrough() { + input + } else { + instance.bytes() + } + }, + ), + ); + + builtins.insert( + "Output", + Builtin::new( + Module::new_builtin( + "Output", + Category::Hidden, + Block::MAX_CONNECTIONS, + Block::MAX_CONNECTIONS, + Decoration::Label(">|".to_string()), + ), + |input, instance| { + instance.set_bytes(input); + input + }, + ), + ); + + builtins.insert( + "Mux", + Builtin::new( + Module::new_builtin( + "Mux", + Category::Combinational, + 3, + 1, + Decoration::Label(String::from("Mux")), + ), + |input, _| (input & 0b101 == 0b001 || input & 0b110 == 0b110) as u128, + ), + ); + + builtins.insert( + "Demux", + Builtin::new( + Module::new_builtin( + "Demux", + Category::Combinational, + 2, + 2, + Decoration::Label(String::from("Demux")), + ), + |input, _| ((((input == 0b11) as u128) << 1) | (input == 0b01) as u128), + ), + ); + + builtins.insert( + "D Latch", + Builtin::new( + Module::new_builtin( + "D Latch", + Category::Latch, + 2, + 1, + Decoration::Label("D L".to_string()), + ), + |input, instance| { + if input & 0b10 > 0 { + instance.set_bytes(input); + } + instance.bytes() + }, + ), + ); + + builtins.insert( + "SR Nand Latch", + Builtin::new( + Module::new_builtin( + "SR Nand Latch", + Category::Latch, + 2, + 2, + Decoration::NotLabel("SR".to_string()), + ), + sr_nand_latch, + ), + ); + + builtins.insert( + "SR Latch", + Builtin::new( + Module::new_builtin( + "SR Latch", + Category::Latch, + 2, + 1, + Decoration::Label("SR".to_string()), + ), + sr_latch, + ), + ); + + builtins.insert( + "JK Latch", + Builtin::new( + Module::new_builtin( + "JK Latch", + Category::Latch, + 2, + 1, + Decoration::Label("JK".to_string()), + ), + jk_latch, + ), + ); + + builtins.insert( + "D Flip-Flop", + Builtin::new( + Module::new_builtin( + "D Flip-Flop", + Category::FlipFlop, + 2, + 2, + Decoration::Label("D".to_string()), + ), + d_flip_flop, + ), + ); + + builtins.insert( + "T Flip-Flop", + Builtin::new( + Module::new_builtin( + "T Flip-Flop", + Category::FlipFlop, + 2, + 2, + Decoration::Label("T".to_string()), + ), + t_flip_flop, + ), + ); builtins }; @@ -135,11 +361,9 @@ fn jk_latch(input: u128, instance: &mut Block) -> u128 { if j && k { instance.set_bytes((instance.bytes() == 0) as u128); - } - else if j { + } else if j { instance.set_bytes(1); - } - else if k { + } else if k { instance.set_bytes(0); } @@ -172,10 +396,18 @@ fn sr_nand_latch(input: u128, instance: &mut Block) -> u128 { instance.bytes() | (((instance.bytes() == 0) as u128) << 1) } +fn d_flip_flop(input: u128, instance: &mut Block) -> u128 { + if input & 0b10 > 0 && instance.bytes() & 0b10 == 0 { + instance.set_bytes(input); + } + instance.set_bytes(instance.bytes() & !0b10 | (input & 0b10)); + instance.bytes() & 1 | !instance.bytes() << 1 +} + fn t_flip_flop(input: u128, instance: &mut Block) -> u128 { - if input & 1 > 0 && instance.bytes() & 0b10 == 0 { + if input == 0b11 && instance.bytes() & 0b10 == 0 { instance.set_bytes(instance.bytes() ^ 1); } - instance.set_bytes((instance.bytes() & !0b10) | (input << 1)); - instance.bytes() & 1 + instance.set_bytes((instance.bytes() & !0b10) | (input & 0b10)); + instance.bytes() & 1 | !instance.bytes() << 1 } diff --git a/src/simulator/connection.rs b/src/simulator/connection.rs index 5e46903..773e1d6 100644 --- a/src/simulator/connection.rs +++ b/src/simulator/connection.rs @@ -1,6 +1,10 @@ -use crate::{renderer::{*, vector::*}, id::Id, application::editor::EditorMode}; use super::*; -use serde::{Serialize, Deserialize}; +use crate::{ + application::editor::{self, EditorMode}, + id::Id, + renderer::{vector::*, *}, +}; +use serde::{Deserialize, Serialize}; use std::f64; pub type ConnectionID = Id; @@ -8,7 +12,7 @@ pub type ConnectionID = Id; #[derive(Debug, Serialize, Deserialize, Copy, Clone)] pub enum Port { Input(BlockID, u8), - Output(BlockID, u8) + Output(BlockID, u8), } impl Port { @@ -16,7 +20,7 @@ impl Port { match self { Self::Input(_, index) | Self::Output(_, index) => *index, } - } + } pub fn block_id(&self) -> BlockID { match self { @@ -26,7 +30,7 @@ impl Port { pub fn set_block_id(&mut self, block_id: BlockID) { match self { - Self::Input(id, _) | Self::Output(id, _) => *id = block_id + Self::Input(id, _) | Self::Output(id, _) => *id = block_id, } } } @@ -35,7 +39,7 @@ impl From for Connector { fn from(value: Port) -> Self { match value { Port::Input(_, port) => Self::Input(port), - Port::Output(_, port) => Self::Output(port) + Port::Output(_, port) => Self::Output(port), } } } @@ -45,12 +49,15 @@ pub type SegmentLocation = Vec; #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct SegmentID { connection_id: ConnectionID, - location: SegmentLocation + location: SegmentLocation, } impl SegmentID { fn new(connection_id: ConnectionID, location: Vec) -> Self { - Self { connection_id, location } + Self { + connection_id, + location, + } } pub fn connection_id(&self) -> &ConnectionID { @@ -65,16 +72,16 @@ impl SegmentID { #[derive(Debug, Serialize, Deserialize, Clone)] pub enum Segment { Block(BlockID, u8), - Waypoint(HashMap, Vector2, bool) + Waypoint(HashMap, Vector2, bool), } impl Segment { - pub const HITBOX_SIZE: i32 = 6; + pub const HITBOX_SIZE: i32 = editor::GRID_SIZE / 2; pub fn position(&self) -> Option<&Vector2> { match self { Self::Waypoint(_, position, _) => Some(position), - _ => None + _ => None, } } @@ -94,8 +101,7 @@ impl Segment { if let Self::Waypoint(_, position, _) = self { let hs = Vector2::new(Self::HITBOX_SIZE, Self::HITBOX_SIZE); !(Vector2::cast(*position - hs) > area.1 || Vector2::cast(*position + hs) < area.0) - } - else { + } else { false } } @@ -105,8 +111,7 @@ impl Segment { let id = Id::new(); segments.insert(id, segment); Some(id) - } - else { + } else { None } } @@ -121,8 +126,15 @@ impl Segment { *self = Self::Block(block_id, port) } - fn render(&self, active: bool, start: Vector2, renderer: &R, plot: &Plot) -> Result<(), R::Error> - where R: Renderer + fn render( + &self, + active: bool, + start: Vector2, + renderer: &R, + plot: &Plot, + ) -> Result<(), R::Error> + where + R: Renderer, { match self { Self::Block(block_id, port) => { @@ -130,7 +142,7 @@ impl Segment { let end = end_block.get_connector_pos(Connector::Input(*port)); render_line(active, start, end, renderer)?; render_block_connector(end, active, end_block.highlighted(), renderer) - }, + } Self::Waypoint(segments, position, highlighted) => { render_line(active, start, *position, renderer)?; @@ -145,26 +157,24 @@ impl Segment { fn destinations(&self, ports: &mut Vec) { match self { - Self::Block(block_id, port) => { - ports.push(Port::Input(*block_id, *port)) - } - Self::Waypoint(segments, ..) => { - segments.iter().for_each(|(_, segment)| segment.destinations(ports)) - } + Self::Block(block_id, port) => ports.push(Port::Input(*block_id, *port)), + Self::Waypoint(segments, ..) => segments + .iter() + .for_each(|(_, segment)| segment.destinations(ports)), } } fn is_empty(&self) -> bool { match self { Self::Waypoint(segments, ..) => segments.is_empty(), - _ => false + _ => false, } } fn remove_unselected_branches(&mut self, selection: &Vec) -> bool { match self { Self::Block(block_id, ..) => !selection.contains(block_id), - Self::Waypoint(segments, ..) => { + Self::Waypoint(segments, ..) => { segments.retain(|_, segment| !segment.remove_unselected_branches(selection)); self.is_empty() } @@ -174,18 +184,22 @@ impl Segment { fn refactor_id(&mut self, old_id: BlockID, new_id: BlockID) { match self { Self::Block(block_id, ..) if *block_id == old_id => *block_id = new_id, - Self::Waypoint(segments, ..) => segments.iter_mut().for_each(|(_, segment)| segment.refactor_id(old_id, new_id)), - _ => () + Self::Waypoint(segments, ..) => segments + .iter_mut() + .for_each(|(_, segment)| segment.refactor_id(old_id, new_id)), + _ => (), } } fn touches(&self, point: Vector2) -> bool { match self { Self::Waypoint(_, position, _) => { - point.0 > position.0 - Self::HITBOX_SIZE && point.0 < position.0 + Self::HITBOX_SIZE && - point.1 > position.1 - Self::HITBOX_SIZE && point.1 < position.1 + Self::HITBOX_SIZE - }, - _ => false + point.0 > position.0 - Self::HITBOX_SIZE + && point.0 < position.0 + Self::HITBOX_SIZE + && point.1 > position.1 - Self::HITBOX_SIZE + && point.1 < position.1 + Self::HITBOX_SIZE + } + _ => false, } } @@ -200,23 +214,28 @@ impl Segment { } result }), - _ => false + _ => false, } } - fn get_segment_mut(&mut self, location: &SegmentLocation, depth: &mut usize) -> Option<&mut Segment> { + fn get_segment_mut( + &mut self, + location: &SegmentLocation, + depth: &mut usize, + ) -> Option<&mut Segment> { let mut_ref_ptr = self as *mut _; match self { Self::Waypoint(segments, ..) => { *depth += 1; if let Some(next) = location.get(*depth) { - segments.get_mut(next).and_then(|segment| segment.get_segment_mut(location, depth)) - } - else { + segments + .get_mut(next) + .and_then(|segment| segment.get_segment_mut(location, depth)) + } else { Some(unsafe { &mut *mut_ref_ptr }) } } - _ => Some(unsafe { &mut *mut_ref_ptr }) + _ => Some(unsafe { &mut *mut_ref_ptr }), } } @@ -225,18 +244,20 @@ impl Segment { Self::Waypoint(segments, ..) => { *depth += 1; if let Some(next) = location.get(*depth) { - segments.get(next).and_then(|segment| segment.get_segment(location, depth)) - } - else { + segments + .get(next) + .and_then(|segment| segment.get_segment(location, depth)) + } else { Some(self) } } - _ => Some(self) + _ => Some(self), } } fn for_each_mut_segment(&mut self, func: &F) - where F: Fn(&mut Segment) + where + F: Fn(&mut Segment), { func(self); if let Self::Waypoint(segments, ..) = self { @@ -246,8 +267,9 @@ impl Segment { } } - fn for_each_mut_segment_id(&mut self, func: &mut F, waypoint_id: &mut SegmentID) - where F: FnMut(&mut Segment, &SegmentID) + fn for_each_mut_segment_id(&mut self, func: &mut F, waypoint_id: &mut SegmentID) + where + F: FnMut(&mut Segment, &SegmentID), { let mut_ref_ptr = self as *mut _; if let Self::Waypoint(segments, ..) = self { @@ -266,7 +288,7 @@ pub struct Connection { id: ConnectionID, active: bool, origin: Port, - segments: HashMap + segments: HashMap, } impl Identifiable for Connection { @@ -279,20 +301,31 @@ impl Connection { id: Id::new(), active: false, origin, - segments: segments.into_iter().map(|segment| (Id::new(), segment)).collect() + segments: segments + .into_iter() + .map(|segment| (Id::new(), segment)) + .collect(), } } - pub fn new_basic(origin_block: BlockID, origin_port: u8, destination_block: BlockID, destination_port: u8) -> Self { + pub fn new_basic( + origin_block: BlockID, + origin_port: u8, + destination_block: BlockID, + destination_port: u8, + ) -> Self { Self { id: Id::new(), active: false, origin: Port::Output(origin_block, origin_port), segments: { let mut segments = HashMap::new(); - segments.insert(Id::new(), Segment::Block(destination_block, destination_port)); + segments.insert( + Id::new(), + Segment::Block(destination_block, destination_port), + ); segments - } + }, } } @@ -322,7 +355,9 @@ impl Connection { pub fn destinations(&self) -> Vec { let mut ports = vec![]; - self.segments.iter().for_each(|(_, segment)| segment.destinations(&mut ports)); + self.segments + .iter() + .for_each(|(_, segment)| segment.destinations(&mut ports)); ports } @@ -331,25 +366,30 @@ impl Connection { } pub fn remove_unselected_branches(&mut self, selected: &Vec) -> bool { - self.segments.retain(|_, segment| !segment.remove_unselected_branches(selected)); + self.segments + .retain(|_, segment| !segment.remove_unselected_branches(selected)); self.segments.is_empty() } pub fn refactor_id(&mut self, old_id: BlockID, new_id: BlockID) { if self.origin.block_id() == old_id { self.origin.set_block_id(new_id) - } - else { - self.segments.iter_mut().for_each(|(_, segment)| segment.refactor_id(old_id, new_id)) + } else { + self.segments + .iter_mut() + .for_each(|(_, segment)| segment.refactor_id(old_id, new_id)) } } pub fn waypoint_at(&self, position: Vector2) -> Option { let mut location = vec![Id::empty()]; - self.segments.iter().any(|(id, segment)| { - location[0] = *id; - segment.waypoint_at(position, &mut location) - }).then_some(SegmentID::new(self.id, location)) + self.segments + .iter() + .any(|(id, segment)| { + location[0] = *id; + segment.waypoint_at(position, &mut location) + }) + .then_some(SegmentID::new(self.id, location)) } pub fn add_segment(&mut self, segment: Segment) { @@ -366,7 +406,9 @@ impl Connection { } let mut i = 0; - self.segments.get(&location[i]).and_then(|segment| segment.get_segment(location, &mut i)) + self.segments + .get(&location[i]) + .and_then(|segment| segment.get_segment(location, &mut i)) } pub fn get_segment_mut(&mut self, location: &SegmentLocation) -> Option<&mut Segment> { @@ -375,11 +417,14 @@ impl Connection { } let mut i = 0; - self.segments.get_mut(&location[i]).and_then(|segment| segment.get_segment_mut(location, &mut i)) + self.segments + .get_mut(&location[i]) + .and_then(|segment| segment.get_segment_mut(location, &mut i)) } - pub fn for_each_mut_segment(&mut self, func: F) - where F: Fn(&mut Segment) + pub fn for_each_mut_segment(&mut self, func: F) + where + F: Fn(&mut Segment), { for segment in self.segments.values_mut() { segment.for_each_mut_segment(&func); @@ -387,7 +432,8 @@ impl Connection { } pub fn for_each_mut_segment_id(&mut self, mut func: F) - where F: FnMut(&mut Segment, &SegmentID) + where + F: FnMut(&mut Segment, &SegmentID), { let mut waypoint_id = SegmentID::new(self.id, vec![Id::empty()]); for (id, segment) in self.segments.iter_mut() { @@ -397,65 +443,113 @@ impl Connection { } } -fn render_waypoint(position: Vector2, active: bool, highlighted: bool, renderer: &R) -> Result<(), R::Error> - where R: Renderer +fn render_waypoint( + position: Vector2, + active: bool, + highlighted: bool, + renderer: &R, +) -> Result<(), R::Error> +where + R: Renderer, { - let connector_color = unsafe { if active { &COLOR_THEME.enabled_fg_color } else { &COLOR_THEME.disabled_fg_color } }; + let connector_color = unsafe { + if active { + &COLOR_THEME.enabled_fg_color + } else { + &COLOR_THEME.disabled_fg_color + } + }; renderer .set_line_width(1.) .arc(position, 6., 0., f64::consts::TAU) .set_color(connector_color) .fill_preserve()? - .set_color(unsafe { if highlighted { &COLOR_THEME.accent_fg_color} else { &COLOR_THEME.border_color }}) + .set_color(unsafe { + if highlighted { + &COLOR_THEME.accent_fg_color + } else { + &COLOR_THEME.border_color + } + }) .stroke() .map(|_| ()) } -fn render_block_connector(position: Vector2, active: bool, highlighted: bool, renderer: &R) -> Result<(), R::Error> - where R: Renderer +pub fn render_block_connector( + position: Vector2, + active: bool, + highlighted: bool, + renderer: &R, +) -> Result<(), R::Error> +where + R: Renderer, { - let connector_color = unsafe { if active { &COLOR_THEME.enabled_fg_color } else { &COLOR_THEME.disabled_fg_color } }; + let connector_color = unsafe { + if active { + &COLOR_THEME.enabled_fg_color + } else { + &COLOR_THEME.disabled_fg_color + } + }; renderer .set_line_width(1.) .arc(position, 6., 0., f64::consts::TAU) .set_color(connector_color) .fill_preserve()? - .set_color(unsafe { if highlighted { &COLOR_THEME.accent_fg_color} else { &COLOR_THEME.border_color }}) + .set_color(unsafe { + if highlighted { + &COLOR_THEME.accent_fg_color + } else { + &COLOR_THEME.border_color + } + }) .stroke() .map(|_| ()) } -fn render_line(active: bool, start: Vector2, end: Vector2, renderer: &R) -> Result<(), R::Error> - where R: Renderer +pub fn render_line( + active: bool, + start: Vector2, + end: Vector2, + renderer: &R, +) -> Result<(), R::Error> +where + R: Renderer, { - renderer.set_color(unsafe { if active { &COLOR_THEME.enabled_bg_color } else { &COLOR_THEME.disabled_bg_color } }) + renderer + .set_color(unsafe { + if active { + &COLOR_THEME.enabled_bg_color + } else { + &COLOR_THEME.disabled_bg_color + } + }) .set_line_width(4.); - + match renderer.editor_mode() { EditorMode::Normal => { let offset = ( Vector2(start.0 + ((end.0 - start.0) as f32 * 0.7) as i32, start.1), Vector2(end.0 + ((start.0 - end.0) as f32 * 0.7) as i32, end.1), ); - renderer.move_to(start) + renderer + .move_to(start) .curve_to(offset.0, offset.1, end) .stroke() } - EditorMode::Grid => { - renderer.move_to(start) - .line_to(end) - .stroke() - } - }.map(|_| ()) + EditorMode::Grid => renderer.move_to(start).line_to(end).stroke(), + } + .map(|_| ()) } impl Renderable for Connection { fn render(&self, renderer: &R, plot: &Plot) -> Result<(), R::Error> - where R: Renderer + where + R: Renderer, { let origin_block = plot.get_block(self.origin.block_id()); if origin_block.is_none() { - return Ok(()) + return Ok(()); } let origin_block = origin_block.unwrap(); let origin_pos = origin_block.get_connector_pos(self.origin.into()); @@ -464,6 +558,11 @@ impl Renderable for Connection { segment.render(self.active, origin_pos, renderer, plot)? } - render_block_connector(origin_pos, self.active, origin_block.highlighted(), renderer) + render_block_connector( + origin_pos, + self.active, + origin_block.highlighted(), + renderer, + ) } } diff --git a/src/simulator/decoration.rs b/src/simulator/decoration.rs index 6cb5911..5a38750 100644 --- a/src/simulator/decoration.rs +++ b/src/simulator/decoration.rs @@ -1,5 +1,5 @@ -use crate::renderer::{*, vector::Vector2}; -use serde::{Serialize, Deserialize}; +use crate::renderer::{vector::Vector2, *}; +use serde::{Deserialize, Serialize}; use std::f64; use super::Block; @@ -11,7 +11,7 @@ pub enum Decoration { NotLabel(String), Button(bool), Switch(bool), - Lamp(bool) + Lamp(bool), } impl Default for Decoration { @@ -22,56 +22,86 @@ impl Default for Decoration { impl Decoration { pub(super) fn render(&self, renderer: &R, block: &Block) -> Result<(), R::Error> - where R: Renderer + where + R: Renderer, { match self { Self::Label(label) => { renderer - .set_font_size(26.0) - .move_to(Vector2(block.position().0 + (block.size().0 / 2 - 7 * label.chars().count() as i32), block.position().1 + (block.size().1 / 2 + 20))) - .set_color(unsafe { &COLOR_THEME.decoration_fg_color }) - .show_text(label)? - .set_font_size(DEFAULT_FONT_SIZE); + .set_font_size(26.0) + .move_to(Vector2( + block.position().0 + + (block.size().0 / 2 - 7 * label.chars().count() as i32), + block.position().1 + (block.size().1 / 2 + 20), + )) + .set_color(unsafe { &COLOR_THEME.decoration_fg_color }) + .show_text(label)? + .set_font_size(DEFAULT_FONT_SIZE); Ok(()) - }, + } Self::NotLabel(label) => { let offset = Vector2( 7 * label.chars().count() as i32, - block.position().1 + block.size().1 / 2 - 2 + block.position().1 + block.size().1 / 2 - 2, ); - let position = block.position() + (block.size() / 2.into() + Vector2(-offset.0, 20)); + let position = + block.position() + (block.size() / 2.into() + Vector2(-offset.0, 20)); renderer - .set_font_size(26.0) - .move_to(position) - .set_color(unsafe { &COLOR_THEME.decoration_fg_color }) - .show_text(label)? - .set_font_size(DEFAULT_FONT_SIZE) - .move_to(Vector2(position.0, offset.1)) - .set_line_width(2.5) - .line_to(Vector2(position.0 + 2 * offset.0, offset.1)) - .stroke() - .map(|_| ()) + .set_font_size(26.0) + .move_to(position) + .set_color(unsafe { &COLOR_THEME.decoration_fg_color }) + .show_text(label)? + .set_font_size(DEFAULT_FONT_SIZE) + .move_to(Vector2(position.0, offset.1)) + .set_line_width(2.5) + .line_to(Vector2(position.0 + 2 * offset.0, offset.1)) + .stroke() + .map(|_| ()) } - Self::Lamp(active) => { - renderer - .arc(Vector2(block.position().0 + block.size().0 / 2, block.position().1 + 50), 12., 0., f64::consts::TAU) - .set_color(unsafe { if *active { &COLOR_THEME.suggestion_fg_color } else { &COLOR_THEME.border_color }}) + Self::Lamp(active) => renderer + .arc( + Vector2( + block.position().0 + block.size().0 / 2, + block.position().1 + 50, + ), + 12., + 0., + f64::consts::TAU, + ) + .set_color(unsafe { + if *active { + &COLOR_THEME.suggestion_fg_color + } else { + &COLOR_THEME.border_color + } + }) .fill_preserve()? .set_line_width(1.5) .set_color(unsafe { &COLOR_THEME.border_color }) .stroke() - .map(|_| ()) - } - Self::Button(active) | Self::Switch(active) => { - renderer - .arc(Vector2(block.position().0 + block.size().0 / 2, block.position().1 + 50), 12., 0., f64::consts::TAU) - .set_color(unsafe { if *active { &COLOR_THEME.button_active_color } else { &COLOR_THEME.button_inactive_color }}) + .map(|_| ()), + Self::Button(active) | Self::Switch(active) => renderer + .arc( + Vector2( + block.position().0 + block.size().0 / 2, + block.position().1 + 50, + ), + 12., + 0., + f64::consts::TAU, + ) + .set_color(unsafe { + if *active { + &COLOR_THEME.button_active_color + } else { + &COLOR_THEME.button_inactive_color + } + }) .fill_preserve()? .set_line_width(1.5) .set_color(unsafe { &COLOR_THEME.border_color }) .stroke() - .map(|_| ()) - } + .map(|_| ()), _ => Ok(()), } } @@ -80,19 +110,15 @@ impl Decoration { impl Decoration { pub fn set_active(&mut self, is_active: bool) { match self { - Self::Button(active) | - Self::Switch(active) | - Self::Lamp(active) => *active = is_active, + Self::Button(active) | Self::Switch(active) | Self::Lamp(active) => *active = is_active, _ => {} } } pub fn is_active(&self) -> bool { match self { - Self::Button(active) | - Self::Switch(active) | - Self::Lamp(active) => *active, - _ => false + Self::Button(active) | Self::Switch(active) | Self::Lamp(active) => *active, + _ => false, } } @@ -101,11 +127,11 @@ impl Decoration { Self::Switch(active) => { *active = !*active; true - }, + } Self::Button(active) => { *active = true; true - }, + } _ => false, } } diff --git a/src/simulator/mod.rs b/src/simulator/mod.rs index 3b216a2..b604481 100644 --- a/src/simulator/mod.rs +++ b/src/simulator/mod.rs @@ -1,20 +1,29 @@ pub mod block; +pub mod builtin; pub mod connection; -pub mod plot; pub mod decoration; -pub mod builtin; pub mod modules; +pub mod plot; pub mod state; -pub use {block::*, connection::*, plot::*, decoration::*, modules::*, state::*}; +use gtk::{prelude::Cast, subclass::prelude::ObjectSubclassIsExt}; use std::{ + cell::RefCell, + collections::{HashMap, HashSet}, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::{self, Receiver, Sender}, + Arc, + }, thread::{self, JoinHandle}, time::{Duration, Instant}, - sync::{Arc, atomic::{AtomicBool, Ordering}, mpsc::{Receiver, Sender, self}}, cell::RefCell, collections::{HashMap, HashSet} }; -use gtk::{subclass::prelude::ObjectSubclassIsExt, prelude::Cast}; +pub use {block::*, connection::*, decoration::*, modules::*, plot::*, state::*}; -use crate::{project::{ProjectRef, Project}, ui::{main_window::MainWindow, circuit_view::CircuitView}}; +use crate::{ + project::{Project, ProjectRef}, + ui::{circuit_view::CircuitView, main_window::MainWindow}, +}; pub trait Identifiable { type ID; @@ -28,35 +37,39 @@ thread_local! { pub enum UICallback { Redraw, - Error(String) + Error(String), } impl UICallback { pub fn handle(self, tx: &Sender) { tx.send(self).unwrap(); - gtk::glib::source::idle_add_once(|| UI_CALLBACK.with(|ui_callback| { - if let Some((window, rx)) = &*ui_callback.borrow() { - let received = rx.recv().unwrap(); - received.exec(window); - } - })); + gtk::glib::source::idle_add_once(|| { + UI_CALLBACK.with(|ui_callback| { + if let Some((window, rx)) = &*ui_callback.borrow() { + let received = rx.recv().unwrap(); + received.exec(window); + } + }) + }); } fn exec(&self, window: &RefCell>) { match self { Self::Redraw => { - if let Some(view) = window.borrow().as_ref() + if let Some(view) = window + .borrow() + .as_ref() .and_then(|window| window.imp().circuit_panel.imp().view.selected_page()) - .and_then(|page| page.child().downcast::().ok()) { - view.rerender(); + .and_then(|page| page.child().downcast::().ok()) + { + view.rerender(); } } Self::Error(err) => { - if let Some(panel) = window.borrow().as_ref() - .map(|window| window.panel()) { - panel.push_error(err.clone()); - } + if let Some(panel) = window.borrow().as_ref().map(|window| window.panel()) { + panel.push_error(err.clone()); + } } } } @@ -66,7 +79,7 @@ pub type SimResult = Result; pub struct Simulator { running: Arc, - thread: JoinHandle<()> + thread: JoinHandle<()>, } impl Simulator { @@ -107,7 +120,7 @@ impl Simulator { let mut project = project.lock().unwrap(); let tps = project.tps(); - + // if we halt the simulation, check again in 0.5 seconds if tps == 0 { drop(project); @@ -136,14 +149,21 @@ impl Simulator { project.iter_plots_mut().for_each(|plot| { plot.pop_state(); match plot.simulate(unsafe { &mut *mut_ref_ptr }, &mut call_stack) { - Ok(c) => if c { changes = true }, - Err(err) => UICallback::Error(err).handle(tx) + Ok(c) => { + if c { + changes = true + } + } + Err(err) => UICallback::Error(err).handle(tx), } plot.push_state(); }); project.iter_plots_mut().for_each(|plot| plot.pop_state()); - assert!(call_stack.is_empty(), "callstack wasn't empty: {call_stack:?}"); + assert!( + call_stack.is_empty(), + "callstack wasn't empty: {call_stack:?}" + ); if changes { UICallback::Redraw.handle(tx) } @@ -158,7 +178,10 @@ impl Collect> for Vec) -> u128 { let mut inputs = 0; for (i, connection_id) in self.iter().enumerate() { - if let Some(connection) = connection_id.map(|connection_id| connections.get(&connection_id)).flatten() { + if let Some(connection) = connection_id + .map(|connection_id| connections.get(&connection_id)) + .flatten() + { inputs |= (connection.is_active() as u128) << i as u128; } } diff --git a/src/simulator/modules.rs b/src/simulator/modules.rs index b67dadf..bd96324 100644 --- a/src/simulator/modules.rs +++ b/src/simulator/modules.rs @@ -1,8 +1,11 @@ use std::collections::HashSet; -use crate::{simulator::{*, builtin::BUILTINS}, id::Id}; +use crate::{ + id::Id, + simulator::{builtin::BUILTINS, *}, +}; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; pub type SimulatorFn = fn(u128, &mut Block) -> u128; @@ -12,10 +15,11 @@ pub enum Category { Basic, InputOutput, Gate, + Combinational, Latch, FlipFlop, Hidden, - Custom + Custom, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -23,7 +27,7 @@ pub struct Custom { plot: Plot, input_block: BlockID, output_block: BlockID, - cache: HashMap + cache: HashMap, } impl Custom { @@ -32,7 +36,7 @@ impl Custom { plot, input_block: Id::default(), output_block: Id::default(), - cache: HashMap::new() + cache: HashMap::new(), } } @@ -67,11 +71,17 @@ impl Module { custom_data: Some(Custom::new(Plot::new())), num_inputs, num_outputs, - decoration: Decoration::None + decoration: Decoration::None, } } - pub fn new_builtin(name: &str, category: Category, num_inputs: u8, num_outputs: u8, decoration: Decoration) -> Self { + pub fn new_builtin( + name: &str, + category: Category, + num_inputs: u8, + num_outputs: u8, + decoration: Decoration, + ) -> Self { Self { name: name.to_string(), category, @@ -79,21 +89,21 @@ impl Module { custom_data: None, num_inputs, num_outputs, - decoration + decoration, } } pub fn plot(&self) -> Option<&Plot> { match &self.custom_data { Some(data) => Some(data.plot()), - None => None + None => None, } } pub fn plot_mut(&mut self) -> Option<&mut Plot> { match &mut self.custom_data { Some(data) => Some(data.plot_mut()), - None => None + None => None, } } @@ -106,10 +116,8 @@ impl Module { pub fn has_io_blocks(&self) -> bool { match &self.custom_data { - Some(data) => { - data.input_block != Id::empty() && data.output_block != Id::empty() - } - _ => false + Some(data) => data.input_block != Id::empty() && data.output_block != Id::empty(), + _ => false, } } @@ -141,8 +149,14 @@ impl Module { &self.decoration } - pub fn simulate(&mut self, inputs: u128, instance: &mut Block, project: &mut Project, call_stack: &mut HashSet) -> SimResult { - let outputs = + pub fn simulate( + &mut self, + inputs: u128, + instance: &mut Block, + project: &mut Project, + call_stack: &mut HashSet, + ) -> SimResult { + let outputs = if self.builtin && let Some(builtin) = BUILTINS.get(self.name.as_str()) { builtin.simulate(inputs, instance) } @@ -164,7 +178,7 @@ impl Module { plot.add_block_to_update(custom_data.input_block); let err = plot.simulate(project, call_stack).err(); - + if let Some(input) = plot.get_block_mut(custom_data.input_block) { input.set_bytes(0); input.set_passthrough(true); @@ -181,14 +195,21 @@ impl Module { outputs }; - debug!("simulate module {} with inputs: {inputs:#b} generates: {outputs:#b}", self.name); + debug!( + "simulate module {} with inputs: {inputs:#b} generates: {outputs:#b}", + self.name + ); Ok(outputs) } } impl Ord for Module { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.name.chars().next().unwrap().cmp(&other.name().chars().next().unwrap()) + self.name + .chars() + .next() + .unwrap() + .cmp(&other.name().chars().next().unwrap()) } } @@ -204,4 +225,4 @@ impl PartialOrd for Module { fn partial_cmp(&self, other: &Self) -> Option { Some(self.name.cmp(other.name())) } -} \ No newline at end of file +} diff --git a/src/simulator/plot.rs b/src/simulator/plot.rs index 3f8ea2a..8d772bd 100644 --- a/src/simulator/plot.rs +++ b/src/simulator/plot.rs @@ -1,12 +1,19 @@ use super::*; -use crate::{renderer::{*, vector::Vector2}, application::selection::*, project::{ProjectRef, Project}}; -use std::{collections::{HashMap, HashSet}, cmp}; -use serde::{Serialize, Deserialize}; +use crate::{ + application::selection::*, + project::{Project, ProjectRef}, + renderer::{vector::Vector2, *}, +}; +use serde::{Deserialize, Serialize}; +use std::{ + cmp, + collections::{HashMap, HashSet}, +}; #[derive(Clone, PartialEq, Eq, Hash)] pub enum PlotDescriptor { Main(), - Module(String) + Module(String), } impl From<&PlotProvider> for PlotDescriptor { @@ -14,7 +21,7 @@ impl From<&PlotProvider> for PlotDescriptor { match value { PlotProvider::Main(_) => Self::Main(), PlotProvider::Module(_, name) => Self::Module(name.clone()), - _ => panic!() + _ => panic!(), } } } @@ -32,7 +39,7 @@ impl From for PlotDescriptor { match value { PlotProvider::None => panic!(), PlotProvider::Main(_) => PlotDescriptor::Main(), - PlotProvider::Module(_, name) => PlotDescriptor::Module(name) + PlotProvider::Module(_, name) => PlotDescriptor::Module(name), } } } @@ -43,11 +50,7 @@ impl PlotProvider { match self { Self::None => None, Self::Main(project) => Some(func(project.lock().unwrap().main_plot())), - Self::Module(project, module) => project - .lock() - .unwrap() - .plot(module) - .map(func) + Self::Module(project, module) => project.lock().unwrap().plot(module).map(func), } } @@ -56,19 +59,14 @@ impl PlotProvider { match self { Self::None => None, Self::Main(project) => Some(func(project.lock().unwrap().main_plot_mut())), - Self::Module(project, module) => project - .lock() - .unwrap() - .plot_mut(module) - .map(func), + Self::Module(project, module) => project.lock().unwrap().plot_mut(module).map(func), } } pub fn project(&self) -> Option { match self { - Self::Main(project) | - Self::Module(project, _) => Some(project.clone()), - _ => None + Self::Main(project) | Self::Module(project, _) => Some(project.clone()), + _ => None, } } @@ -79,7 +77,7 @@ impl PlotProvider { pub fn is_module(&self) -> Option<&String> { match self { Self::Module(_, name) => Some(name), - _ => None + _ => None, } } } @@ -95,7 +93,7 @@ pub struct Plot { selection: Selection, #[serde(skip)] - to_update: HashSet + to_update: HashSet, } impl Identifiable for Plot { @@ -109,7 +107,7 @@ impl Plot { connections: HashMap::new(), states: vec![PlotState::default()], selection: Selection::None, - to_update: HashSet::new() + to_update: HashSet::new(), } } @@ -121,8 +119,7 @@ impl Plot { pub fn pop_state(&mut self) { if let Some(state) = self.states.pop() { state.apply(self); - } - else { + } else { error!("Plot::pop_state() failed: stack is empty.") } } @@ -151,7 +148,7 @@ impl Plot { pub fn get_waypoint_at(&self, position: Vector2) -> Option { for connection in self.connections.values() { if let Some(waypoint) = connection.waypoint_at(position) { - return Some(waypoint) + return Some(waypoint); } } None @@ -185,7 +182,10 @@ impl Plot { fn patch_destinations(&mut self, destinations: Vec, connection_id: ConnectionID) { for destination in destinations { - let block = self.blocks.get_mut(&destination.block_id()).expect("faulty destination block"); + let block = self + .blocks + .get_mut(&destination.block_id()) + .expect("faulty destination block"); block.set_connection(destination.into(), Some(connection_id)); } } @@ -207,15 +207,18 @@ impl Plot { } pub fn add_connection(&mut self, connection: Connection) { - let origin = self.blocks.get_mut(&connection.origin().block_id()).expect("faulty origin block"); + let origin = self + .blocks + .get_mut(&connection.origin().block_id()) + .expect("faulty origin block"); if let Some(existing) = origin.connection(connection.origin().into()) { self.add_to_existing_connection(existing, &connection); return; } - + origin.set_connection(connection.origin().into(), Some(connection.id())); - + self.patch_destinations(connection.destinations(), connection.id()); self.to_update.insert(connection.origin().block_id()); self.connections.insert(connection.id(), connection); @@ -250,7 +253,8 @@ impl Plot { let mut unique = false; if let Some(block) = self.blocks.get(&id) { unique = block.unique(); - deleted_connections = block.connected_to() + deleted_connections = block + .connected_to() .iter() .filter_map(|id| self.remove_connection(*id)) .collect(); @@ -274,7 +278,7 @@ impl Plot { pub fn to_update_mut(&mut self) -> &mut HashSet { &mut self.to_update } - + pub fn update_all_blocks(&mut self) { for block_id in self.blocks.keys().copied() { self.to_update.insert(block_id); @@ -283,7 +287,11 @@ impl Plot { const RECURSION_CAP: u8 = 100; - pub fn simulate(&mut self, project: &mut Project, call_stack: &mut HashSet) -> SimResult { + pub fn simulate( + &mut self, + project: &mut Project, + call_stack: &mut HashSet, + ) -> SimResult { let mut updated = HashMap::new(); let mut queued = HashSet::new(); let mut changes = false; @@ -291,7 +299,7 @@ impl Plot { while !self.to_update.is_empty() { let to_update = std::mem::take(&mut self.to_update); changes = true; - + for block_id in to_update.iter() { if updated.contains_key(block_id) { let occurrences = updated.get_mut(block_id).unwrap(); @@ -303,10 +311,16 @@ impl Plot { } if let Some(block) = self.blocks.get_mut(block_id) { - block.simulate(&mut self.connections, &mut self.to_update, &mut queued, project, call_stack)?; + block.simulate( + &mut self.connections, + &mut self.to_update, + &mut queued, + project, + call_stack, + )?; if !updated.contains_key(block_id) { - updated.insert(*block_id, 0); + updated.insert(*block_id, 0); } } } @@ -320,12 +334,17 @@ impl Plot { impl Renderable for Plot { fn render(&self, renderer: &R, plot: &Plot) -> Result<(), R::Error> - where R: Renderer + where + R: Renderer, { let screen_space = renderer.screen_space(); // render all blocks - for (_, block) in self.blocks.iter().filter(|(_, block)| block.is_in_area(&screen_space)) { + for (_, block) in self + .blocks + .iter() + .filter(|(_, block)| block.is_in_area(&screen_space)) + { block.render(renderer, plot)?; } @@ -387,7 +406,7 @@ impl SelectionField for Plot { match self.selection.clone() { Selection::Single(id, _) => vec![id], Selection::Many(ids) => ids, - _ => vec![] + _ => vec![], } } @@ -395,10 +414,16 @@ impl SelectionField for Plot { if let Selection::Area(selection_start, selection_end) = self.selection { let mut selected = Vec::new(); - let min = Vector2::new(cmp::min(selection_start.0, selection_end.0) as f64, cmp::min(selection_start.1, selection_end.1) as f64); - let max = Vector2::new(cmp::max(selection_start.0, selection_end.0) as f64, cmp::max(selection_start.1, selection_end.1) as f64); + let min = Vector2::new( + cmp::min(selection_start.0, selection_end.0) as f64, + cmp::min(selection_start.1, selection_end.1) as f64, + ); + let max = Vector2::new( + cmp::max(selection_start.0, selection_end.0) as f64, + cmp::max(selection_start.1, selection_end.1) as f64, + ); let area = Vector2::new(min, max); - + for (_, block) in self.blocks_mut().iter_mut() { if block.is_in_area(&area) { block.set_highlighted(true); @@ -420,10 +445,21 @@ impl SelectionField for Plot { } fn select_all(&mut self) { - self.selection = Selection::Many(self.blocks.keys().map(|id| Selectable::Block(*id)).collect()); - self.blocks.iter_mut().for_each(|(_, block)| block.set_highlighted(true)); - - fn highlight_segment(segment: &mut Segment) { segment.set_highlighted(true) } - self.connections.iter_mut().for_each(|(_, connection)| connection.for_each_mut_segment(highlight_segment)); + self.selection = Selection::Many( + self.blocks + .keys() + .map(|id| Selectable::Block(*id)) + .collect(), + ); + self.blocks + .iter_mut() + .for_each(|(_, block)| block.set_highlighted(true)); + + fn highlight_segment(segment: &mut Segment) { + segment.set_highlighted(true) + } + self.connections + .iter_mut() + .for_each(|(_, connection)| connection.for_each_mut_segment(highlight_segment)); } } diff --git a/src/simulator/state.rs b/src/simulator/state.rs index cd13f94..9c8038d 100644 --- a/src/simulator/state.rs +++ b/src/simulator/state.rs @@ -1,6 +1,6 @@ use super::*; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct PlotState { @@ -17,21 +17,35 @@ impl From<&mut Plot> for PlotState { impl From<&Plot> for PlotState { fn from(plot: &Plot) -> Self { Self { - blocks: plot.blocks().iter().map(|(id, block)| (*id, block.state().clone())).collect(), - connections: plot.connections().iter().map(|(id, connection)| (*id, connection.is_active())).collect() + blocks: plot + .blocks() + .iter() + .map(|(id, block)| (*id, block.state().clone())) + .collect(), + connections: plot + .connections() + .iter() + .map(|(id, connection)| (*id, connection.is_active())) + .collect(), } } } impl PlotState { pub fn apply(&self, plot: &mut Plot) { - plot.blocks_mut().iter_mut().for_each(|(id, block)| if let Some(state) = self.blocks.get(id) { - block.set_state(state.clone()) + plot.blocks_mut().iter_mut().for_each(|(id, block)| { + if let Some(state) = self.blocks.get(id) { + block.set_state(state.clone()) + } }); - plot.connections_mut().iter_mut().for_each(|(id, connection)| if let Some(state) = self.connections.get(id) { - connection.set_active(*state); - }) + plot.connections_mut() + .iter_mut() + .for_each(|(id, connection)| { + if let Some(state) = self.connections.get(id) { + connection.set_active(*state); + } + }) } } @@ -40,13 +54,13 @@ pub enum State { #[default] None, Direct(u128), - Inherit(PlotState) + Inherit(PlotState), } impl State { pub fn apply(&self, plot: &mut Plot) { if let Self::Inherit(state) = self { - state.apply(plot) + state.apply(plot) } } -} \ No newline at end of file +} diff --git a/src/ui/circuit_panel.rs b/src/ui/circuit_panel.rs index aaed09f..5ed5c40 100644 --- a/src/ui/circuit_panel.rs +++ b/src/ui/circuit_panel.rs @@ -1,8 +1,14 @@ -use crate::{application::{Application, editor::EditorMode}, simulator::PlotProvider}; use super::circuit_view::CircuitView; -use gtk::{prelude::*, subclass::prelude::*, gio, glib}; +use crate::{ + application::{editor::EditorMode, Application}, + simulator::PlotProvider, +}; +use gtk::{gio, glib, prelude::*, subclass::prelude::*}; -use std::{cell::{RefCell, Cell}, collections::HashMap}; +use std::{ + cell::{Cell, RefCell}, + collections::HashMap, +}; glib::wrapper! { pub struct CircuitPanel(ObjectSubclass) @@ -40,7 +46,7 @@ impl CircuitPanel { } pub fn redo_button(&self) -> >k::Button { - &self.imp().redo_button + &self.imp().redo_button } pub fn open_tab(&self, plot_provider: PlotProvider) { @@ -55,7 +61,7 @@ impl CircuitPanel { } i += 1; } - + // page not found, create new self.imp().new_tab(module_name, plot_provider.clone()); } @@ -84,8 +90,7 @@ impl CircuitPanel { if !errors.contains(&error) { errors.push(error); } - } - else { + } else { template.info_label.set_label(&error); template.info_bar.show(); } @@ -128,7 +133,7 @@ pub struct CircuitPanelTemplate { application: RefCell, pages: RefCell>, force_closing: Cell, - errors: RefCell> + errors: RefCell>, } impl CircuitPanelTemplate { @@ -157,7 +162,13 @@ impl CircuitPanelTemplate { } fn set_title(&self, title: &str) { - (self.header_bar.title_widget().unwrap().downcast_ref().unwrap() as &adw::WindowTitle).set_subtitle(title); + (self + .header_bar + .title_widget() + .unwrap() + .downcast_ref() + .unwrap() as &adw::WindowTitle) + .set_subtitle(title); } fn close_tabs(&self) { @@ -206,14 +217,16 @@ impl ObjectImpl for CircuitPanelTemplate { true })); - self.info_close_button.connect_clicked(glib::clone!(@weak self as widget => move |_| widget.info_bar.hide())); - self.info_bar.connect_hide(glib::clone!(@weak self as widget => move |bar| { - let mut errors = widget.errors.borrow_mut(); - if let Some(err) = errors.pop() { - widget.info_label.set_label(&err); - bar.show(); - } - })); + self.info_close_button + .connect_clicked(glib::clone!(@weak self as widget => move |_| widget.info_bar.hide())); + self.info_bar + .connect_hide(glib::clone!(@weak self as widget => move |bar| { + let mut errors = widget.errors.borrow_mut(); + if let Some(err) = errors.pop() { + widget.info_label.set_label(&err); + bar.show(); + } + })); } } diff --git a/src/ui/circuit_view.rs b/src/ui/circuit_view.rs index 4abcf7f..323d275 100644 --- a/src/ui/circuit_view.rs +++ b/src/ui/circuit_view.rs @@ -1,6 +1,14 @@ -use std::{cell::{RefCell, Cell}, collections::HashMap}; -use gtk::{prelude::*, subclass::prelude::*, gio, glib, gdk}; -use crate::{renderer::{*, vector::*}, simulator::*, fatal::FatalResult, application::{selection::*, Application, action::Action, editor::EditorMode}}; +use crate::{ + application::{action::Action, editor::EditorMode, selection::*, Application}, + fatal::FatalResult, + renderer::{vector::*, *}, + simulator::*, +}; +use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*}; +use std::{ + cell::{Cell, RefCell}, + collections::HashMap, +}; glib::wrapper! { pub struct CircuitView(ObjectSubclass) @@ -11,7 +19,8 @@ glib::wrapper! { impl CircuitView { pub fn new(app: Application, plot_provider: PlotProvider) -> Self { let circuit_view: Self = glib::Object::new::(&[]); - circuit_view.imp() + circuit_view + .imp() .set_application(app) .set_plot_provider(plot_provider) .initialize(); @@ -30,7 +39,6 @@ impl CircuitView { self.imp().rerender(); } - pub fn plot_provider(&self) -> PlotProvider { self.imp().plot_provider() } @@ -41,7 +49,10 @@ impl CircuitView { } pub fn fetch_border_color(&self) -> Option { - self.imp().border_color_enabled.is_active().then(|| self.imp().border_color_button.rgba().into_color()) + self.imp() + .border_color_enabled + .is_active() + .then(|| self.imp().border_color_button.rgba().into_color()) } } @@ -85,7 +96,7 @@ pub struct CircuitViewTemplate { alt_down: Cell, application: RefCell, editor_mode: RefCell, - mouse_position: Cell> + mouse_position: Cell>, } impl CircuitViewTemplate { @@ -108,42 +119,51 @@ impl CircuitViewTemplate { } fn init_buttons(&self) { - self.zoom_reset.connect_clicked(glib::clone!(@weak self as widget => move |_| { - let mut r = widget.renderer.borrow_mut(); - r.set_scale(DEFAULT_SCALE); - r.translate(Vector2::default()); - widget.drawing_area.queue_draw(); - widget.left_osd_label.set_label("0, 0"); - })); + self.zoom_reset + .connect_clicked(glib::clone!(@weak self as widget => move |_| { + let mut r = widget.renderer.borrow_mut(); + r.set_scale(DEFAULT_SCALE); + r.translate(Vector2::default()); + widget.drawing_area.queue_draw(); + widget.left_osd_label.set_label("0, 0"); + })); - self.zoom_in.connect_clicked(glib::clone!(@weak self as widget => move |_| { - let mut r = widget.renderer.borrow_mut(); - r.zoom(1.1, None); - widget.drawing_area.queue_draw(); - })); + self.zoom_in + .connect_clicked(glib::clone!(@weak self as widget => move |_| { + let mut r = widget.renderer.borrow_mut(); + r.zoom(1.1, None); + widget.drawing_area.queue_draw(); + })); - self.zoom_out.connect_clicked(glib::clone!(@weak self as widget => move |_| { - let mut r = widget.renderer.borrow_mut(); - r.zoom(0.9, None); - widget.drawing_area.queue_draw(); - })); + self.zoom_out + .connect_clicked(glib::clone!(@weak self as widget => move |_| { + let mut r = widget.renderer.borrow_mut(); + r.zoom(0.9, None); + widget.drawing_area.queue_draw(); + })); - self.border_color_enabled.connect_toggled(glib::clone!(@weak self as widget => move |button| { - widget.border_color_button.set_sensitive(button.is_active()); - })); + self.border_color_enabled.connect_toggled( + glib::clone!(@weak self as widget => move |button| { + widget.border_color_button.set_sensitive(button.is_active()); + }), + ); self.border_color_button.set_sensitive(false); } fn init_mouse(&self) { let mouse_controller = gtk::EventControllerMotion::new(); - mouse_controller.connect_motion(glib::clone!(@weak self as widget => move |_, x, y| widget.on_mouse_move(x, y))); + mouse_controller.connect_motion( + glib::clone!(@weak self as widget => move |_, x, y| widget.on_mouse_move(x, y)), + ); self.drawing_area.add_controller(&mouse_controller); - let gesture_drag = gtk::GestureDrag::builder().button(gdk::ffi::GDK_BUTTON_PRIMARY as u32).build(); + let gesture_drag = gtk::GestureDrag::builder() + .button(gdk::ffi::GDK_BUTTON_PRIMARY as u32) + .build(); gesture_drag.connect_drag_begin(glib::clone!(@weak self as widget => move |gesture, x, y| { gesture.set_state(gtk::EventSequenceState::Claimed); widget.drawing_area.grab_focus(); - + if widget.ctrl_down.get() { widget.renderer.borrow_mut().save_translation(); widget.set_left_osd_visible(true); @@ -170,58 +190,69 @@ impl CircuitViewTemplate { })); let app = &*self.application.borrow(); - gesture_drag.connect_drag_end(glib::clone!(@weak self as widget, @weak app => move |gesture, x, y| { - gesture.set_state(gtk::EventSequenceState::Claimed); + gesture_drag.connect_drag_end( + glib::clone!(@weak self as widget, @weak app => move |gesture, x, y| { + gesture.set_state(gtk::EventSequenceState::Claimed); - if widget.ctrl_down.get() && (x != 0. || y != 0.) { - widget.set_left_osd_visible(false); - } - else { - let scale = widget.renderer.borrow().scale(); - widget.drag_end(Vector2((x / scale) as i32, (y / scale) as i32)); - } - })); + if widget.ctrl_down.get() && (x != 0. || y != 0.) { + widget.set_left_osd_visible(false); + } + else { + let scale = widget.renderer.borrow().scale(); + widget.drag_end(Vector2((x / scale) as i32, (y / scale) as i32)); + } + }), + ); self.drawing_area.add_controller(&gesture_drag); } fn init_context_menu(&self) { - let gesture = gtk::GestureClick::builder().button(gdk::ffi::GDK_BUTTON_SECONDARY as u32).build(); - gesture.connect_pressed(glib::clone!(@weak self as widget => move |gesture, _, x, y| { - gesture.set_state(gtk::EventSequenceState::Claimed); - widget.context_menu(x, y); - })); + let gesture = gtk::GestureClick::builder() + .button(gdk::ffi::GDK_BUTTON_SECONDARY as u32) + .build(); + gesture.connect_pressed( + glib::clone!(@weak self as widget => move |gesture, _, x, y| { + gesture.set_state(gtk::EventSequenceState::Claimed); + widget.context_menu(x, y); + }), + ); self.drawing_area.add_controller(&gesture); } fn init_keyboard(&self) { let key_controller = gtk::EventControllerKey::new(); - key_controller.connect_key_pressed(glib::clone!(@weak self as widget => @default-panic, move |_, key, _, _| { - match key { - gdk::Key::Control_L | gdk::Key::Control_R => widget.ctrl_down.set(true), - gdk::Key::Shift_L | gdk::Key::Shift_R => widget.shift_down.set(true), - gdk::Key::Alt_L | gdk::Key::Alt_R => widget.alt_down.set(true), - _ => () - } - gtk::Inhibit(true) - })); + key_controller.connect_key_pressed( + glib::clone!(@weak self as widget => @default-panic, move |_, key, _, _| { + match key { + gdk::Key::Control_L | gdk::Key::Control_R => widget.ctrl_down.set(true), + gdk::Key::Shift_L | gdk::Key::Shift_R => widget.shift_down.set(true), + gdk::Key::Alt_L | gdk::Key::Alt_R => widget.alt_down.set(true), + _ => () + } + gtk::Inhibit(true) + }), + ); - key_controller.connect_key_released(glib::clone!(@weak self as widget => @default-panic, move |_, key, _, _| - match key { - gdk::Key::Control_L | gdk::Key::Control_R => { - widget.set_left_osd_visible(false); - widget.ctrl_down.set(false) - }, - gdk::Key::Shift_L | gdk::Key::Shift_R => widget.shift_down.set(false), - gdk::Key::Alt_L | gdk::Key::Alt_R => widget.alt_down.set(false), - _ => () - } - )); + key_controller.connect_key_released( + glib::clone!(@weak self as widget => @default-panic, move |_, key, _, _| + match key { + gdk::Key::Control_L | gdk::Key::Control_R => { + widget.set_left_osd_visible(false); + widget.ctrl_down.set(false) + }, + gdk::Key::Shift_L | gdk::Key::Shift_R => widget.shift_down.set(false), + gdk::Key::Alt_L | gdk::Key::Alt_R => widget.alt_down.set(false), + _ => () + } + ), + ); self.drawing_area.add_controller(&key_controller); } fn init_scrolling(&self) { - let scroll_controller = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL); + let scroll_controller = + gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL); scroll_controller.connect_scroll(glib::clone!(@weak self as widget => @default-panic, move |_, _, y| { widget.renderer.borrow_mut().zoom(if y > 0. { 0.9 } else { 1.1 }, Some(widget.mouse_position.get())); widget.drawing_area.queue_draw(); @@ -232,26 +263,30 @@ impl CircuitViewTemplate { } fn init_drawing_area(&self) { - self.drawing_area.set_draw_func(glib::clone!(@weak self as widget => move |area, context, width, height| - widget.plot_provider.borrow().with_mut(|plot| - widget.renderer.borrow_mut() - .callback(plot, *widget.editor_mode.borrow(), area, context, width, height) - .map(|_| ()) - .unwrap_or_die() - ); - )); + self.drawing_area.set_draw_func( + glib::clone!(@weak self as widget => move |area, context, width, height| + widget.plot_provider.borrow().with_mut(|plot| + widget.renderer.borrow_mut() + .callback(plot, *widget.editor_mode.borrow(), area, context, width, height) + .map(|_| ()) + .unwrap_or_die() + ); + ), + ); self.drawing_area.set_focusable(true); self.drawing_area.grab_focus(); self.drawing_area.set_focus_on_click(true); - self.drawing_area.connect_has_focus_notify(glib::clone!(@weak self as widget => move |area| - if !area.has_focus() { - widget.shift_down.set(false); - widget.ctrl_down.set(false); - widget.alt_down.set(false); - } - )); + self.drawing_area.connect_has_focus_notify( + glib::clone!(@weak self as widget => move |area| + if !area.has_focus() { + widget.shift_down.set(false); + widget.ctrl_down.set(false); + widget.alt_down.set(false); + } + ), + ); } fn initialize(&self) { @@ -267,18 +302,19 @@ impl CircuitViewTemplate { let position = Vector2(x, y); self.mouse_position.set(position); - self.plot_provider.borrow_mut().with_mut(|plot| + self.plot_provider.borrow_mut().with_mut(|plot| { if let Selection::MoveBlock(block) = plot.selection_mut() { - let position = self.editor_mode.borrow() - .align(VectorCast::cast(self.renderer.borrow().screen_to_world(position))); - + let position = self.editor_mode.borrow().align(VectorCast::cast( + self.renderer.borrow().screen_to_world(position), + )); + block.set_position(position); self.drawing_area.queue_draw(); } - ); + }); } - fn context_menu(&self, x: f64, y: f64) { + fn context_menu(&self, x: f64, y: f64) { let position = self.renderer.borrow().screen_to_world(Vector2(x, y)); self.plot_provider.borrow_mut().with_mut(|plot| { @@ -291,23 +327,31 @@ impl CircuitViewTemplate { match plot.selection() { Selection::None => { plot.unhighlight(); - plot.set_selection(Selection::Single(Selectable::Block(id), start_position)); + plot.set_selection(Selection::Single( + Selectable::Block(id), + start_position, + )); } Selection::Many(sel) if sel.is_empty() => { plot.unhighlight(); - plot.set_selection(Selection::Single(Selectable::Block(id), start_position)); + plot.set_selection(Selection::Single( + Selectable::Block(id), + start_position, + )); } _ => {} } //drop(plot); - self.context_menu.set_pointing_to(Some(&gdk::Rectangle::new(x as i32, y as i32, 1, 1))); + self.context_menu + .set_pointing_to(Some(&gdk::Rectangle::new(x as i32, y as i32, 1, 1))); self.context_menu.popup(); } } None => { - self.area_context_menu.set_pointing_to(Some(&gdk::Rectangle::new(x as i32, y as i32, 1, 1))); + self.area_context_menu + .set_pointing_to(Some(&gdk::Rectangle::new(x as i32, y as i32, 1, 1))); self.area_context_menu.popup(); } } @@ -325,58 +369,73 @@ impl CircuitViewTemplate { } fn selection_shift_click(&self, selected: Vec, position: Vector2) -> bool { - self.plot_provider.borrow().with_mut(move |p| - if let Some(block_id) = p.get_block_at(position) { - let mut selected = selected.clone(); - if let Some(index) = selected.iter().position(|sel| sel == &Selectable::Block(block_id)) { - selected.remove(index); - p.get_block_mut(block_id).unwrap().set_highlighted(false); - } - else { - p.get_block_mut(block_id).unwrap().set_highlighted(true); - selected.push(Selectable::Block(block_id)); - } - p.set_selection(Selection::Many(selected)); - - true - } - else if let Some(waypoint_id) = p.get_waypoint_at(position) { - let mut selected = selected.clone(); - if let Some(index) = selected.iter().position(|sel| sel == &Selectable::Waypoint(waypoint_id.clone())) { - selected.remove(index); - p.get_connection_mut(waypoint_id.connection_id()).unwrap().get_segment_mut(waypoint_id.location()).unwrap().set_highlighted(false); - } - else { - p.get_connection_mut(waypoint_id.connection_id()).unwrap().get_segment_mut(waypoint_id.location()).unwrap().set_highlighted(true); - selected.push(Selectable::Waypoint(waypoint_id)); - } - p.set_selection(Selection::Many(selected)); + self.plot_provider + .borrow() + .with_mut(move |p| { + if let Some(block_id) = p.get_block_at(position) { + let mut selected = selected.clone(); + if let Some(index) = selected + .iter() + .position(|sel| sel == &Selectable::Block(block_id)) + { + selected.remove(index); + p.get_block_mut(block_id).unwrap().set_highlighted(false); + } else { + p.get_block_mut(block_id).unwrap().set_highlighted(true); + selected.push(Selectable::Block(block_id)); + } + p.set_selection(Selection::Many(selected)); + + true + } else if let Some(waypoint_id) = p.get_waypoint_at(position) { + let mut selected = selected.clone(); + if let Some(index) = selected + .iter() + .position(|sel| sel == &Selectable::Waypoint(waypoint_id.clone())) + { + selected.remove(index); + p.get_connection_mut(waypoint_id.connection_id()) + .unwrap() + .get_segment_mut(waypoint_id.location()) + .unwrap() + .set_highlighted(false); + } else { + p.get_connection_mut(waypoint_id.connection_id()) + .unwrap() + .get_segment_mut(waypoint_id.location()) + .unwrap() + .set_highlighted(true); + selected.push(Selectable::Waypoint(waypoint_id)); + } + p.set_selection(Selection::Many(selected)); - true - } - else { - false - } - ).unwrap_or(false) + true + } else { + false + } + }) + .unwrap_or(false) } fn drag_begin(&self, position: Vector2) { let selection = self.plot_provider.borrow().with(|p| p.selection().clone()); match selection { - Some(Selection::MoveBlock(block)) => - self.application.borrow() - .new_action(Action::NewBlock(self.plot_provider.borrow().clone(), *block)), - Some(Selection::Many(block_ids)) => + Some(Selection::MoveBlock(block)) => self.application.borrow().new_action( + Action::NewBlock(self.plot_provider.borrow().clone(), *block), + ), + Some(Selection::Many(block_ids)) => { if self.shift_down.get() && self.selection_shift_click(block_ids, position) { self.drawing_area.queue_draw(); return; } - Some(Selection::Single(block_id, _)) => + } + Some(Selection::Single(block_id, _)) => { if self.shift_down.get() && self.selection_shift_click(vec![block_id], position) { self.drawing_area.queue_draw(); return; } - _ => () + } + _ => (), } self.plot_provider.borrow().with_mut(|plot| { @@ -387,42 +446,52 @@ impl CircuitViewTemplate { if block.on_mouse_press(position) { plot.set_selection(Selection::MouseEvent(id)); plot.add_block_to_update(id); - } - else if let Some(i) = block.position_on_connection(position, false) { + } else if let Some(i) = block.position_on_connection(position, false) { let start = block.get_connector_pos(Connector::Output(i)); - plot.set_selection(Selection::Connection(ConnectionSource::Block(id, i), start, start)); - } - else { + plot.set_selection(Selection::Connection( + ConnectionSource::Block(id, i), + start, + start, + )); + } else { let start_position = block.position(); block.set_highlighted(true); plot.set_selection(Selection::Single(Selectable::Block(id), start_position)); } - } - else if let Some(id) = plot.get_waypoint_at(position) { - let waypoint = plot.get_connection_mut(id.connection_id()).and_then(|c| c.get_segment_mut(id.location())).unwrap(); + } else if let Some(id) = plot.get_waypoint_at(position) { + let waypoint = plot + .get_connection_mut(id.connection_id()) + .and_then(|c| c.get_segment_mut(id.location())) + .unwrap(); let start = *waypoint.position().unwrap(); if self.alt_down.take() { - plot.set_selection(Selection::Connection(ConnectionSource::Waypoint(id), start, start)) - } - else { + plot.set_selection(Selection::Connection( + ConnectionSource::Waypoint(id), + start, + start, + )) + } else { waypoint.set_highlighted(true); plot.set_selection(Selection::Single(Selectable::Waypoint(id), start)) } - } - else { + } else { plot.set_selection(Selection::Area(position, position)); } }); - + self.drawing_area.queue_draw(); } - + fn drag_update(&self, offset: Vector2) { - self.plot_provider.borrow().with_mut(|plot| - match plot.selection().clone() { + self.plot_provider + .borrow() + .with_mut(|plot| match plot.selection().clone() { Selection::Single(selected, Vector2(start_x, start_y)) => { - let new_position = self.editor_mode.borrow().align(Vector2(start_x, start_y) + offset); + let new_position = self + .editor_mode + .borrow() + .align(Vector2(start_x, start_y) + offset); match selected { Selectable::Block(id) => { @@ -431,11 +500,13 @@ impl CircuitViewTemplate { plot.set_selection(Selection::None); return; } - + block.unwrap().set_position(new_position); } Selectable::Waypoint(id) => { - let waypoint = plot.get_connection_mut(id.connection_id()).and_then(|c| c.get_segment_mut(id.location())); + let waypoint = plot + .get_connection_mut(id.connection_id()) + .and_then(|c| c.get_segment_mut(id.location())); if waypoint.is_none() { plot.set_selection(Selection::None); return; @@ -455,9 +526,8 @@ impl CircuitViewTemplate { plot.set_selection(Selection::Area(area_start, area_start + offset)); self.drawing_area.queue_draw(); } - _ => () - } - ); + _ => (), + }); } fn drag_end(&self, offset: Vector2) { @@ -469,28 +539,56 @@ impl CircuitViewTemplate { return; } - let new_position = self.editor_mode.borrow().align(Vector2(start_x, start_y) + offset); + let new_position = self + .editor_mode + .borrow() + .align(Vector2(start_x, start_y) + offset); match selected { - Selectable::Block(block_id) => self.application.borrow().new_action(Action::MoveBlock(plot_provider.clone(), block_id, Vector2(start_x, start_y), new_position)), + Selectable::Block(block_id) => { + self.application.borrow().new_action(Action::MoveBlock( + plot_provider.clone(), + block_id, + Vector2(start_x, start_y), + new_position, + )) + } Selectable::Waypoint(id) => { - let con_action = plot_provider.with_mut(|plot| { - let block_id = plot.get_block_at(new_position)?; - let port = plot.get_block(block_id)?.position_on_connection(new_position, true)?; - - let segment = plot.get_connection(id.connection_id())?.get_segment(id.location())?; - Some(Action::WaypointToConnection(plot_provider.clone(), id.clone(), segment.clone(), block_id, port)) - }).flatten(); - - self.application.borrow().new_action(if let Some(con_action) = con_action { - con_action - } - else { - Action::MoveWaypoint(plot_provider.clone(), id, Vector2(start_x, start_y), new_position) - }) + let con_action = plot_provider + .with_mut(|plot| { + let block_id = plot.get_block_at(new_position)?; + let port = plot + .get_block(block_id)? + .position_on_connection(new_position, true)?; + + let segment = plot + .get_connection(id.connection_id())? + .get_segment(id.location())?; + Some(Action::WaypointToConnection( + plot_provider.clone(), + id.clone(), + segment.clone(), + block_id, + port, + )) + }) + .flatten(); + + self.application + .borrow() + .new_action(if let Some(con_action) = con_action { + con_action + } else { + Action::MoveWaypoint( + plot_provider.clone(), + id, + Vector2(start_x, start_y), + new_position, + ) + }) } } - }, + } Selection::Connection(ConnectionSource::Block(origin_id, output), _, position) => { let connection = plot_provider.with_mut(|plot| { plot.set_selection(Selection::None); @@ -499,7 +597,7 @@ impl CircuitViewTemplate { let Some(block) = plot.get_block(block_id) && let Some(i) = block.position_on_connection(position, true) { - block.connection(Connector::Input(i)).is_none().then(|| + block.connection(Connector::Input(i)).is_none().then(|| Connection::new_basic(origin_id, output, block_id, i ) ) } @@ -509,7 +607,9 @@ impl CircuitViewTemplate { }).flatten(); if let Some(connection) = connection { - self.application.borrow().new_action(Action::NewConnection(plot_provider.clone(), connection)); + self.application + .borrow() + .new_action(Action::NewConnection(plot_provider.clone(), connection)); } self.drawing_area.queue_draw() @@ -520,16 +620,20 @@ impl CircuitViewTemplate { if let Some(block_id) = plot.get_block_at(position) && let Some(block) = plot.get_block(block_id) && let Some(i) = block.position_on_connection(position, true) { - block.connection(Connector::Input(i)).is_none().then_some(Segment::Block(block_id, i)) - } + } else { Some(Segment::Waypoint(HashMap::new(), position, false)) - } + } }).flatten(); - + if let Some(segment) = segment { - self.application.borrow().new_action(Action::AddSegment(plot_provider.clone(), segment_id, segment, None)) + self.application.borrow().new_action(Action::AddSegment( + plot_provider.clone(), + segment_id, + segment, + None, + )) } self.drawing_area.queue_draw(); @@ -541,7 +645,9 @@ impl CircuitViewTemplate { Selection::MouseEvent(block_id) => { plot_provider.with_mut(|plot| { plot.set_selection(Selection::None); - if let Some(block) = plot.get_block_mut(block_id) { block.on_mouse_release() } + if let Some(block) = plot.get_block_mut(block_id) { + block.on_mouse_release() + } plot.add_block_to_update(block_id); }); self.drawing_area.queue_draw(); @@ -566,7 +672,6 @@ impl ObjectSubclass for CircuitViewTemplate { } } - impl ObjectImpl for CircuitViewTemplate { fn constructed(&self) { self.parent_constructed(); diff --git a/src/ui/dialogs.rs b/src/ui/dialogs.rs index 5744108..89ef18e 100644 --- a/src/ui/dialogs.rs +++ b/src/ui/dialogs.rs @@ -1,14 +1,22 @@ use adw::prelude::*; use gtk::{ - traits::DialogExt, - subclass::prelude::ObjectSubclassIsExt, - ButtonsType, Entry, MessageDialog, ResponseType, Orientation, Box, ColorButton, Label, Align, + subclass::prelude::ObjectSubclassIsExt, traits::DialogExt, Align, Box, ButtonsType, + ColorButton, Entry, Label, MessageDialog, Orientation, ResponseType, }; +use crate::{ + application::{action::Action, selection::SelectionField, Application}, + renderer::{IntoColor, IntoRGBA, COLOR_THEME}, + simulator::Module, +}; use std::future::Future; -use crate::{simulator::Module, application::{Application, action::Action, selection::SelectionField}, renderer::{COLOR_THEME, IntoRGBA, IntoColor}}; -fn create_new_module(app: Application, name: String, num_inputs: u8, num_outputs: u8) -> Result<(), String> { +fn create_new_module( + app: Application, + name: String, + num_inputs: u8, + num_outputs: u8, +) -> Result<(), String> { if name.is_empty() { return Err("Invalid name".to_string()); } @@ -19,8 +27,14 @@ fn create_new_module(app: Application, name: String, num_inputs: u8, num_outputs return Err(err); } - info!("Create new Module \"{}\"\nwith: {} inputs\n {} outputs", name, num_inputs, num_outputs); - app.new_action(Action::CreateModule(app.imp().project().clone(), Module::new(name, num_inputs, num_outputs))); + info!( + "Create new Module \"{}\"\nwith: {} inputs\n {} outputs", + name, num_inputs, num_outputs + ); + app.new_action(Action::CreateModule( + app.imp().project().clone(), + Module::new(name, num_inputs, num_outputs), + )); Ok(()) } @@ -66,7 +80,7 @@ pub async fn new_module(app: Application, window: gtk::Window, _data: ()) { content.append(&input_chooser); let output_adjustment = gtk::Adjustment::new(1.0, 1.0, 129.0, 1.0, 1.0, 1.0); - let output_chooser = gtk::SpinButton::builder() + let output_chooser = gtk::SpinButton::builder() .climb_rate(1.0) .adjustment(&output_adjustment) .margin_start(12) @@ -91,12 +105,17 @@ pub async fn new_module(app: Application, window: gtk::Window, _data: ()) { dialog.close(); if answer == ResponseType::Ok { - // let num_inputs = INPUTS.iter().position(|&elem| elem == input_chooser.active_id().unwrap()).unwrap_or_default() + 1; + // let num_inputs = INPUTS.iter().position(|&elem| elem == input_chooser.active_id().unwrap()).unwrap_or_default() + 1; let num_inputs = input_chooser.value_as_int(); let num_outputs = output_chooser.value_as_int(); // generate new module - if let Err(err) = create_new_module(app, name_input.buffer().text().trim().to_string(), num_inputs as u8, num_outputs as u8) { + if let Err(err) = create_new_module( + app, + name_input.buffer().text().trim().to_string(), + num_inputs as u8, + num_outputs as u8, + ) { gtk::glib::MainContext::default().spawn_local(invalid_module(window, err)); } } @@ -111,7 +130,7 @@ pub async fn basic_error(_app: Application, window: gtk::Window, message: String .text(&message) .title("Error") .build(); - + dialog.run_future().await; dialog.close(); } @@ -125,11 +144,11 @@ pub async fn confirm_delete_module(app: Application, window: gtk::Window, module .text(&format!("Do you really want to delete the module \"{module_name}\"?\nThis action is not reversable!")) .title(&format!("Delete Module \"{module_name}\"?")) .build(); - + let answer = dialog.run_future().await; dialog.close(); - if answer == ResponseType::Yes { + if answer == ResponseType::Yes { app.imp().delete_module(&module_name); } } @@ -143,7 +162,11 @@ pub async fn select_border_color(app: Application, window: gtk::Window, _data: ( .buttons(ButtonsType::OkCancel) .build(); - let label = Label::builder().label("Border Color:").halign(Align::Start).hexpand(false).build(); + let label = Label::builder() + .label("Border Color:") + .halign(Align::Start) + .hexpand(false) + .build(); let color_button = ColorButton::with_rgba(&unsafe { &COLOR_THEME.border_color }.into_rgba()); let content = dialog.content_area(); @@ -165,8 +188,12 @@ pub async fn select_border_color(app: Application, window: gtk::Window, _data: ( } } -pub fn run(application: Application, window: gtk::Window, data: T, dialog: fn(Application, gtk::Window, T) -> F) -where +pub fn run( + application: Application, + window: gtk::Window, + data: T, + dialog: fn(Application, gtk::Window, T) -> F, +) where F: Future + 'static, { gtk::glib::MainContext::default().spawn_local(dialog(application, window, data)); diff --git a/src/ui/main_window.rs b/src/ui/main_window.rs index b084f02..25539be 100644 --- a/src/ui/main_window.rs +++ b/src/ui/main_window.rs @@ -1,7 +1,7 @@ -use gtk::{prelude::*, subclass::prelude::*, gio, glib, IconTheme, gdk::Display}; -use adw::{subclass::prelude::AdwApplicationWindowImpl}; -use crate::{application::*, simulator::*, config}; -use super::{circuit_panel::*, module_list::*, circuit_view::*}; +use super::{circuit_panel::*, circuit_view::*, module_list::*}; +use crate::{application::*, config, simulator::*}; +use adw::subclass::prelude::AdwApplicationWindowImpl; +use gtk::{gdk::Display, gio, glib, prelude::*, subclass::prelude::*, IconTheme}; glib::wrapper! { pub struct MainWindow(ObjectSubclass) @@ -18,10 +18,7 @@ impl MainWindow { gtk::Window::set_default_icon_name(config::APP_ICON_NAME); - let window = glib::Object::new::(&[ - ("application", app), - ("title", &"LogicRs") - ]); + let window = glib::Object::new::(&[("application", app), ("title", &"LogicRs")]); window.initialize(app); window } @@ -30,7 +27,10 @@ impl MainWindow { let panel = &self.imp().circuit_panel; let module_list = &self.imp().module_list; if !module.builtin() { - panel.new_tab(module.name(), PlotProvider::Module(app.imp().project().clone(), module.name().clone())); + panel.new_tab( + module.name(), + PlotProvider::Module(app.imp().project().clone(), module.name().clone()), + ); } module_list.add_module_to_ui(app, module); } @@ -55,13 +55,20 @@ impl MainWindow { self.imp().module_list.init_accels(app); self.set_subtitle(&app.imp().file_name()); self.set_icon_name(Some(config::APP_ICON_NAME)); - + let panel = &self.imp().circuit_panel; - panel.new_tab("Main Circuit", PlotProvider::Main(app.imp().project().clone())); + panel.new_tab( + "Main Circuit", + PlotProvider::Main(app.imp().project().clone()), + ); let project = app.imp().project(); let project = project.lock().unwrap(); - project.modules().iter().filter(|(_, module)| !module.hidden()).for_each(|(_, module)| self.add_module_to_ui(app, module)); + project + .modules() + .iter() + .filter(|(_, module)| !module.hidden()) + .for_each(|(_, module)| self.add_module_to_ui(app, module)); self.connect_close_request(glib::clone!(@weak app => @default-panic, move |_| { app.quit(); @@ -137,8 +144,9 @@ impl ObjectImpl for MainWindowTemplate { let circuit_panel = self.circuit_panel.get(); let circuit_panel_template = CircuitPanelTemplate::from_instance(&circuit_panel); - self.leaflet.set_fold_threshold_policy(adw::FoldThresholdPolicy::Minimum); - + self.leaflet + .set_fold_threshold_policy(adw::FoldThresholdPolicy::Minimum); + self.leaflet.property_expression("folded").bind( &module_list_template.header_bar.get(), "show-end-title-buttons", diff --git a/src/ui/module_list.rs b/src/ui/module_list.rs index 0fb3bdf..d4b3453 100644 --- a/src/ui/module_list.rs +++ b/src/ui/module_list.rs @@ -1,6 +1,10 @@ -use gtk::{prelude::*, subclass::prelude::*, glib, gdk, gio}; +use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*}; -use crate::{application::{Application, selection::*}, simulator::*, renderer::vector::Vector2}; +use crate::{ + application::{selection::*, Application}, + renderer::vector::Vector2, + simulator::*, +}; macro_rules! add_menu_item { ($model: expr, $name: expr, $action: expr, $value: expr) => { @@ -78,7 +82,8 @@ impl ModuleList { #[template_callback] fn search_entry_changed(&self, entry: >k::SearchEntry) { let search_text = entry.text().to_ascii_lowercase(); - self.imp().filter((!search_text.is_empty()).then_some(search_text)); + self.imp() + .filter((!search_text.is_empty()).then_some(search_text)); } #[template_callback] @@ -105,6 +110,9 @@ pub struct ModuleListTemplate { #[template_child] gate_list_box: TemplateChild, + #[template_child] + combinational_list_box: TemplateChild, + #[template_child] latch_list_box: TemplateChild, @@ -118,7 +126,7 @@ pub struct ModuleListTemplate { search_bar: TemplateChild, #[template_child] - search_button: TemplateChild + search_button: TemplateChild, } impl ModuleListTemplate { @@ -127,27 +135,41 @@ impl ModuleListTemplate { Category::Basic => &self.basic_list_box, Category::InputOutput => &self.input_output_list_box, Category::Gate => &self.gate_list_box, + Category::Combinational => &self.combinational_list_box, Category::Latch => &self.latch_list_box, Category::FlipFlop => &self.flip_flop_list_box, Category::Custom => &self.custom_list_box, - Category::Hidden => panic!("no list for hidden modules") + Category::Hidden => panic!("no list for hidden modules"), } } fn lists(&self) -> [>k::ListBox; 6] { - [&self.basic_list_box, &self.input_output_list_box, &self.gate_list_box, &self.latch_list_box, &self.flip_flop_list_box, &self.custom_list_box] + [ + &self.basic_list_box, + &self.input_output_list_box, + &self.gate_list_box, + &self.latch_list_box, + &self.flip_flop_list_box, + &self.custom_list_box, + ] } - fn module_item_content(&self, module: &Module) -> gtk::Box { + fn module_item_content(&self, module: &Module) -> gtk::Box { let b = gtk::Box::builder() .orientation(gtk::Orientation::Horizontal) .build(); - b.append(>k::Image::builder().icon_name("module-symbolic").margin_end(12).build()); - b.append(>k::Label::builder() - .label(module.name()) - .xalign(0.0) - .build() + b.append( + >k::Image::builder() + .icon_name("module-symbolic") + .margin_end(12) + .build(), + ); + b.append( + >k::Label::builder() + .label(module.name()) + .xalign(0.0) + .build(), ); b @@ -158,14 +180,13 @@ impl ModuleListTemplate { .child(&self.module_item_content(module)) .css_classes(vec![String::from("module_list_item")]) .build(); - - self.list_for(module.category()) - .append(&item); - + + self.list_for(module.category()).append(&item); + let left_click_gesture = gtk::GestureClick::builder() .button(gdk::ffi::GDK_BUTTON_PRIMARY as u32) .build(); - + let name = module.name().to_owned(); left_click_gesture.connect_pressed(glib::clone!(@weak application => move |_, _, _, _| { let project = application.imp().project().clone(); @@ -189,17 +210,28 @@ impl ModuleListTemplate { let name = module.name().to_owned(); let is_builtin = module.builtin(); - right_click_gesture.connect_pressed(glib::clone!(@weak self as widget => move |_, _, _, _| { - if !is_builtin { - widget.custom_module_context(&item, &name); - } - })); + right_click_gesture.connect_pressed( + glib::clone!(@weak self as widget => move |_, _, _, _| { + if !is_builtin { + widget.custom_module_context(&item, &name); + } + }), + ); } fn remove_module_from_ui(&self, module_name: &String) { let mut i = 0; while let Some(row) = self.custom_list_box.row_at_index(i) { - if row.child().and_downcast::().unwrap().last_child().and_downcast::().unwrap().label().eq(module_name) { + if row + .child() + .and_downcast::() + .unwrap() + .last_child() + .and_downcast::() + .unwrap() + .label() + .eq(module_name) + { self.custom_list_box.remove(&row); } i += 1; @@ -207,18 +239,23 @@ impl ModuleListTemplate { } fn clear_list(&self) { - self.lists().iter().for_each(|list| + self.lists().iter().for_each(|list| { while let Some(row) = list.row_at_index(0) { list.remove(&row); } - ); + }); } fn custom_module_context(&self, item: >k::ListBoxRow, name: &String) { let model = gio::Menu::new(); - add_menu_item!(model, "_Edit Contents", "app.edit-module", &name.to_variant()); - add_menu_item!(model, "E_xport", "app.export-module", &name.to_variant()); - add_menu_item!(model, "_Delete", "app.delete-module", &name.to_variant()); + add_menu_item!( + model, + "_Edit Contents", + "app.edit-module", + &name.to_variant() + ); + add_menu_item!(model, "E_xport", "app.export-module", &name.to_variant()); + add_menu_item!(model, "_Delete", "app.delete-module", &name.to_variant()); let popover = gtk::PopoverMenu::from_model(Some(&model)); popover.set_parent(item); @@ -235,16 +272,23 @@ impl ModuleListTemplate { let search_text = search_text.clone(); list.set_filter_func(move |item| Self::filter_func(item, &search_text)); }); - } - else { - self.lists().iter().for_each(|list| list.unset_filter_func()); + } else { + self.lists() + .iter() + .for_each(|list| list.unset_filter_func()); } - self.stack.set_visible_child_name(if self.n_visible() == 0 { "empty" } else { "modules" }); + self.stack.set_visible_child_name(if self.n_visible() == 0 { + "empty" + } else { + "modules" + }); } fn filter_func(item: >k::ListBoxRow, search_text: &String) -> bool { - let label = item.label().expect("could not get label from ModuleListItem"); + let label = item + .label() + .expect("could not get label from ModuleListItem"); label.to_ascii_lowercase().contains(search_text) } } @@ -269,19 +313,27 @@ impl ObjectImpl for ModuleListTemplate { fn constructed(&self) { self.parent_constructed(); - self.search_bar.connect_search_mode_enabled_notify(glib::clone!(@weak self as widget => move |search_bar| - widget.search_button.set_active(search_bar.is_search_mode()); - )); - - self.search_button.connect_toggled(glib::clone!(@weak self as widget => move |button| - widget.search_bar.set_search_mode(button.is_active()); - )); - - let order_alphabetically = |a: >k::ListBoxRow, b: >k::ListBoxRow| gtk::Ordering::from( - a.label().expect("could not get label from ModuleListItem") - .cmp(&b.label().expect("could not get label from ModuleListItem")) + self.search_bar.connect_search_mode_enabled_notify( + glib::clone!(@weak self as widget => move |search_bar| + widget.search_button.set_active(search_bar.is_search_mode()); + ), ); - self.lists().iter().for_each(|list| list.set_sort_func(order_alphabetically)); + + self.search_button + .connect_toggled(glib::clone!(@weak self as widget => move |button| + widget.search_bar.set_search_mode(button.is_active()); + )); + + let order_alphabetically = |a: >k::ListBoxRow, b: >k::ListBoxRow| { + gtk::Ordering::from( + a.label() + .expect("could not get label from ModuleListItem") + .cmp(&b.label().expect("could not get label from ModuleListItem")), + ) + }; + self.lists() + .iter() + .for_each(|list| list.set_sort_func(order_alphabetically)); } } diff --git a/src/ui/properties.rs b/src/ui/properties.rs index 589c5b2..319fb6e 100644 --- a/src/ui/properties.rs +++ b/src/ui/properties.rs @@ -1,4 +1,4 @@ -use gtk::{prelude::*, subclass::prelude::*, gio, glib}; +use gtk::{gio, glib, prelude::*, subclass::prelude::*}; glib::wrapper! { pub struct Properties(ObjectSubclass) @@ -6,19 +6,13 @@ glib::wrapper! { @implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager; } -impl Properties { - -} +impl Properties {} #[derive(gtk::CompositeTemplate, Default)] #[template(resource = "/content/properties.ui")] -pub struct PropertiesTemplate { - -} +pub struct PropertiesTemplate {} -impl PropertiesTemplate { - -} +impl PropertiesTemplate {} #[glib::object_subclass] impl ObjectSubclass for PropertiesTemplate { @@ -28,7 +22,7 @@ impl ObjectSubclass for PropertiesTemplate { fn class_init(class: &mut Self::Class) { Self::bind_template(class); - } + } fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); @@ -51,6 +45,4 @@ impl WidgetImpl for PropertiesTemplate { } } -impl BoxImpl for PropertiesTemplate { - -} \ No newline at end of file +impl BoxImpl for PropertiesTemplate {}