Skip to content

Commit

Permalink
Update UI (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
pumken authored Mar 19, 2023
1 parent e5c637f commit c81faa1
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 41 deletions.
2 changes: 2 additions & 0 deletions src/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ pub enum InvalidGraphError {
Discontinuity,
#[error("Molecule is cyclic.")]
Cycle,
#[error("Bond at ({}, {}) has no inconsistent order.", .0.x, .0.y)]
InconsistentBond(Vec2),
#[error("Cell at ({}, {}) is missing bonds.", .0.x, .0.y)]
UnfilledValence(Vec2),
#[error("Cell at ({}, {}) has too many bonds.", .0.x, .0.y)]
Expand Down
43 changes: 41 additions & 2 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
//! The `input` module contains functions that interpret user input.
use crate::groups::debug_branches;
use crate::macros::invoke_macro;
use crate::molecule::BondOrder::{Double, Single, Triple};
use crate::molecule::Element;
use crate::molecule::Element::{C, H, N, O};
use crate::naming::name_molecule;
use crate::spatial::GridState;
use crate::{AppState, Mode};
use ruscii::app::State;
Expand All @@ -18,56 +20,78 @@ pub(crate) fn input_insert_mode(app_state: &State, state: &mut AppState, graph:
KeyEvent::Pressed(Key::B) => {
graph.put_atom(Element::Br);
state.key = "B";
update(state, graph);
}
KeyEvent::Pressed(Key::C) => {
graph.put_atom(C);
state.key = "C";
update(state, graph);
}
KeyEvent::Pressed(Key::F) => {
graph.put_atom(Element::F);
state.key = "F";
update(state, graph);
}
KeyEvent::Pressed(Key::H) => {
graph.put_atom(H);
state.key = "H";
update(state, graph);
}
KeyEvent::Pressed(Key::I) => {
graph.put_atom(Element::I);
state.key = "I";
update(state, graph);
}
KeyEvent::Pressed(Key::L) => {
graph.put_atom(Element::Cl);
state.key = "L";
update(state, graph);
}
KeyEvent::Pressed(Key::N) => {
graph.put_atom(N);
state.key = "N";
update(state, graph);
}
KeyEvent::Pressed(Key::O) => {
graph.put_atom(O);
state.key = "O";
update(state, graph);
}
KeyEvent::Pressed(Key::F5) => {
graph.clear_all();
state.key = "F5";
update(state, graph);
}
KeyEvent::Pressed(Key::F7) => {
state.macros_enabled = !state.macros_enabled;
state.key = "F7";
}
KeyEvent::Pressed(Key::F12) => {
state.debug = match debug_branches(graph) {
Ok(it) => it.to_string(),
Err(it) => it.to_string(),
}
};
state.key = "F12";
}
KeyEvent::Pressed(Key::Num1) => {
graph.put_bond(Single);
state.key = "1";
update(state, graph);
}
KeyEvent::Pressed(Key::Num2) => {
graph.put_bond(Double);
state.key = "2";
update(state, graph);
}
KeyEvent::Pressed(Key::Num3) => {
graph.put_bond(Triple);
state.key = "3";
update(state, graph);
}
KeyEvent::Pressed(Key::Backspace) => {
graph.clear_cell();
state.key = "Backspace";
update(state, graph);
}
KeyEvent::Pressed(Key::Right) => {
graph.move_cursor(Direction::Right);
Expand Down Expand Up @@ -100,8 +124,23 @@ pub(crate) fn input_view_mode(app_state: &State, state: &mut AppState) {
for key_event in app_state.keyboard().last_key_events() {
match key_event {
KeyEvent::Pressed(Key::Esc) => app_state.stop(),
KeyEvent::Pressed(Key::F8) => state.mode = Mode::Insert,
KeyEvent::Pressed(Key::F8) => {
state.mode = Mode::Insert;
state.name = "".to_string()
}
_ => (),
}
}
}

//noinspection RsBorrowChecker
pub(crate) fn update(state: &mut AppState, graph: &mut GridState) {
if state.macros_enabled {
invoke_macro(graph);
}

(state.name, state.err) = match name_molecule(graph) {
Ok(it) => (it, "".to_string()),
Err(it) => ("unidentified".to_string(), it.to_string()),
};
}
39 changes: 39 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,42 @@
//!
//! Not to be confused with Rust's `macro_rules!` declarations, the `macros` module contains
//! common actions that should be automatically performed for the user when they make an input.
use crate::molecule::BondOrder::Single;
use crate::molecule::ComponentType;
use crate::molecule::Element::{C, H};
use crate::pointer::Pointer;
use crate::spatial::{EnumAll, GridState, ToVec2};
use ruscii::spatial::Direction;

pub fn invoke_macro(graph: &mut GridState) {
match graph
.current_cell()
.expect("cell should be within bounds")
.comp()
{
ComponentType::Element(C) => fill_hydrogen(graph),
ComponentType::None => {} // just to appease Clippy
_ => {}
}
}

pub fn fill_hydrogen(graph: &mut GridState) {
for direction in Direction::all() {
hydrogen_arm(graph, direction);
}
}

fn hydrogen_arm(graph: &mut GridState, direction: Direction) {
let mut ptr = Pointer::new(graph, graph.cursor);
let first_neighbor = ptr.move_ptr(direction) && !ptr.borrow().unwrap().is_empty();
let second_neighbor = ptr.move_ptr(direction) && !ptr.borrow().unwrap().is_empty();
let pos = ptr.pos;

if first_neighbor && second_neighbor {
graph.put(pos, ComponentType::Element(H));
graph.put(pos - direction.to_vec2(), ComponentType::Order(Single));
} else if first_neighbor && !second_neighbor {
graph.put(pos, ComponentType::Element(H));
}
}
19 changes: 12 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
use crate::input::{input_insert_mode, input_view_mode};
use crate::molecule::BondOrder::{Double, Single, Triple};
use crate::molecule::Cell;
use crate::naming::name_molecule;
use crate::spatial::{GridState, Invert};
use crate::Mode::Insert;
use ruscii::app::{App, State};
use ruscii::drawing::Pencil;
use ruscii::spatial::Vec2;
use ruscii::terminal::Color::{Cyan, Red, White};
use ruscii::terminal::Window;
use ruscii::terminal::{Color, Window};

mod chain;
mod groups;
Expand All @@ -34,13 +34,9 @@ fn main() {

app.run(|app_state: &mut State, window: &mut Window| {
match state.mode {
Mode::Insert => {
Insert => {
input_insert_mode(app_state, &mut state, &mut graph);

(state.name, state.err) = match name_molecule(&graph) {
Ok(it) => (it, "".to_string()),
Err(it) => ("unidentified".to_string(), it.to_string()),
};
state.pos = format!("({}, {})", graph.cursor.x, graph.cursor.y);
state.sym = match graph
.current_cell()
Expand Down Expand Up @@ -166,10 +162,17 @@ fn main() {
.set_foreground(Red)
.draw_text(&state.err.to_string(), Vec2::xy(graph.size.x * 3 + 3, 7))
.draw_text(&state.debug.to_string(), Vec2::xy(graph.size.x * 3 + 3, 8));

if state.macros_enabled && state.mode == Insert {
pencil
.set_foreground(Color::Yellow)
.draw_text("Macro mode enabled.", Vec2::xy(graph.size.x * 3 + 3, 8));
}
});
}

/// Represents the mode the app is in at a given time.
#[derive(Copy, Clone, Debug, PartialEq)]
enum Mode {
Insert,
Normal,
Expand All @@ -185,6 +188,7 @@ struct AppState {
key: &'static str,
err: String,
debug: String,
macros_enabled: bool,
}

impl Default for AppState {
Expand All @@ -197,6 +201,7 @@ impl Default for AppState {
key: "", // Overridden in Start mode to retain &str type
err: "".to_string(),
debug: "".to_string(),
macros_enabled: false,
}
}
}
Expand Down
32 changes: 29 additions & 3 deletions src/molecule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::fmt::{Display, Formatter};
use std::str::FromStr;

/// Represents a type of functional group on a molecule.
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Group {
Alkane,
Alkene,
Expand Down Expand Up @@ -114,9 +114,15 @@ impl Display for Group {
}
}

impl PartialOrd for Group {
impl PartialOrd<Self> for Group {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
todo!()
let out = match (self.priority(), other.priority()) {
(Some(a), Some(b)) => a.cmp(&b),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
};
Some(out)
}
}

Expand Down Expand Up @@ -176,9 +182,29 @@ impl Cell {
}
}

/// Returns the component type of the atom.
pub fn comp(&self) -> ComponentType {
match self {
Cell::Atom(it) => ComponentType::Element(it.element),
Cell::Bond(it) => ComponentType::Order(it.order),
Cell::None(_) => ComponentType::None,
}
}

pub fn is_atom(&self) -> bool {
matches!(self, Cell::Atom(_))
}

pub fn is_empty(&self) -> bool {
!matches!(self, Cell::None(_))
}
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ComponentType {
Element(Element),
Order(BondOrder),
None,
}

#[derive(Clone, Debug, PartialEq)]
Expand Down
34 changes: 25 additions & 9 deletions src/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! [`GridState`] and molecular structures on it.
use crate::groups::InvalidGraphError;
use crate::groups::InvalidGraphError::IncompleteBond;
use crate::groups::InvalidGraphError::{IncompleteBond, InconsistentBond};
use crate::molecule::Element::C;
use crate::molecule::{Atom, BondOrder, BondOrientation, Cell};
use crate::spatial::{EnumAll, GridState, ToVec2};
Expand Down Expand Up @@ -36,16 +36,19 @@ impl<'a> Pointer<'a> {
self.graph.get(self.pos)
}

/// Returns a [`Vec`] of references to the non-empty [`Cell`]s adjacent to the [`Cell`]
/// currently pointed to.
/// Returns a [`Vec`] of references to the non-empty [`Cell`]s adjacent (or bonded if it is a
/// bond) to the [`Cell`] currently pointed to.
pub fn connected(&self) -> Vec<&Cell> {
let mut out = vec![];

for direction in Direction::all() {
if let Ok(result) = self.graph.get(self.pos + direction.to_vec2()) {
match result {
Cell::Atom(_) | Cell::Bond(_) => out.push(result),
Cell::None(_) => {}
Cell::Atom(_) => out.push(result),
Cell::Bond(it) if it.orient == BondOrientation::from(direction) => {
out.push(result)
}
_ => {}
}
}
}
Expand All @@ -61,8 +64,11 @@ impl<'a> Pointer<'a> {
for direction in Direction::all() {
if let Ok(result) = self.graph.get(self.pos + direction.to_vec2()) {
match result {
Cell::Atom(_) | Cell::Bond(_) => out.push(direction),
Cell::None(_) => {}
Cell::Atom(_) => out.push(direction),
Cell::Bond(it) if it.orient == BondOrientation::from(direction) => {
out.push(direction)
}
_ => {}
}
}
}
Expand Down Expand Up @@ -160,20 +166,30 @@ impl<'a> Pointer<'a> {
/// ## Errors
///
/// If the [`Pointer`] created to traverse the bond encounters a [`Cell::Bond`] of the incorrect
/// orientation or a [`Cell::None`], an [`IncompleteBond`] is returned.
/// orientation or a [`Cell::None`], an [`IncompleteBond`] is returned. If the bond does not
/// have one consistent order, an [`InvalidGraphError::InconsistentBond`] is returned.
pub fn traverse_bond(&self, direction: Direction) -> Result<Atom, InvalidGraphError> {
if let Direction::None = direction {
panic!("Cannot pass Direction::None to traverse_bond");
}

let mut traversal_ptr = self.clone();
let mut current_order = None;

loop {
traversal_ptr.move_ptr(direction);
if !traversal_ptr.move_ptr(direction) {
break Err(IncompleteBond(traversal_ptr.pos));
}
match traversal_ptr.borrow() {
Ok(Cell::Atom(it)) => break Ok(it.to_owned()),
Ok(Cell::Bond(it)) => {
if let Some(order) = current_order {
if order != it.order {
break Err(InconsistentBond(traversal_ptr.pos));
}
}
if it.orient == BondOrientation::from(direction) {
current_order = Some(it.order);
continue;
} else {
break Err(IncompleteBond(traversal_ptr.pos));
Expand Down
Loading

0 comments on commit c81faa1

Please sign in to comment.