cubes 5.2.1

Applications for determining how to put a cube together.
Documentation
use iced::widget::{button, column, radio, row, text_input};
use iced::widget::{Column, Row};
use iced::{window, Alignment, Element};

use std::fs::{self, File};
use std::io::Write;

use cubes::{project_dir_cubes, Puzzle};

pub fn main() -> iced::Result {
    iced::application(
        PolycubePieces::default,
        PolycubePieces::update,
        PolycubePieces::view,
    )
    .title("Polycube Pieces")
    .window(window::Settings {
        size: iced::Size::new(210.0, 450.0),
        ..Default::default()
    })
    .run()
}

#[derive(Clone, Debug)]
struct PolycubePieces {
    name: String,
    cube: [[[bool; 3]; 3]; 3],
    pieces: Puzzle,
}

impl PolycubePieces {
    fn sum_cubes(&self) -> i32 {
        let mut sum = 0;
        for x in 0..3 {
            for y in 0..3 {
                for z in 0..3 {
                    if self.cube[x][y][z] {
                        sum += 1;
                    }
                }
            }
        }
        sum
    }

    fn sum_pieces(&self) -> i32 {
        let mut sum = 0;
        for piece in &self.pieces.data {
            for _cube in piece {
                sum += 1;
            }
        }
        sum
    }
}

impl Default for PolycubePieces {
    fn default() -> Self {
        Self {
            name: String::new(),
            cube: [[[false; 3]; 3]; 3],
            pieces: Puzzle { data: Vec::new() },
        }
    }
}

#[derive(Clone, Debug)]
enum Message {
    NameChanged(String),
    SelectedPiece(usize),
    SavePiecePressed,
    SaveAllPressed,
}

impl PolycubePieces {
    fn update(&mut self, message: Message) {
        match message {
            Message::NameChanged(name) => {
                self.name = name;
            }
            Message::SelectedPiece(i) => {
                let x = i / 9;
                let y = (i / 3) % 3;
                let z = i % 3;
                self.cube[x][y][z] = !self.cube[x][y][z];
            }
            Message::SavePiecePressed => {
                self.pieces.data.push(Vec::new());
                let len = self.pieces.data.len() - 1;

                for x in 0..3 {
                    for y in 0..3 {
                        for z in 0..3 {
                            if self.cube[x][y][z] {
                                println!("{x} {y} {z}");
                                self.pieces.data[len].push([
                                    i32::try_from(x).unwrap(),
                                    i32::try_from(y).unwrap(),
                                    i32::try_from(z).unwrap(),
                                ]);
                            }
                        }
                    }
                }
                println!();
            }
            Message::SaveAllPressed => {
                let proj_dirs = project_dir_cubes().expect("expected a cubes directory");
                let dir = proj_dirs.data_dir();
                let mut path = dir.join("puzzles");
                fs::create_dir_all(&path).unwrap();

                path = path.join(&self.name);
                path.set_extension("ron");
                let mut buffer = File::create(&path).unwrap();
                let encoded =
                    ron::ser::to_string_pretty(&self.pieces, ron::ser::PrettyConfig::default())
                        .unwrap();
                buffer.write_all(encoded.as_bytes()).unwrap();
                println!("saved {}", path.display());
            }
        }
    }

    fn view(&self) -> Element<'_, Message> {
        let text_input = text_input("", &self.name).on_input(Message::NameChanged);
        let name = row!["Name: ", text_input]
            .padding(10)
            .align_y(Alignment::Start);

        let mut pieces_matrix = Vec::new();
        for x in 0..3 {
            if x > 0 {
                pieces_matrix.push(Row::new().padding(10).into());
            }
            for y in 0..3 {
                let mut row = Vec::new();
                for z in 0..3 {
                    let i = 9 * x + 3 * y + z;
                    let mut cube_selected = None;
                    if self.cube[x][y][z] {
                        cube_selected = Some(i);
                    }
                    row.push(radio("", i, cube_selected, Message::SelectedPiece).into());
                }
                pieces_matrix.push(Row::with_children(row).into());
            }
        }
        let pieces_col = Column::with_children(pieces_matrix).padding(10).spacing(10);

        let save_piece = if self.sum_pieces() >= 27
            || self.sum_cubes() < 1
            || self.sum_cubes() + self.sum_pieces() > 27
        {
            button("Save Piece")
        } else {
            button("Save Piece").on_press(Message::SavePiecePressed)
        };
        let save_all = if self.sum_pieces() != 27 || self.name.is_empty() {
            button("Save All")
        } else {
            button("Save All").on_press(Message::SaveAllPressed)
        };
        let buttons = row![save_piece, save_all]
            .padding(10)
            .spacing(10)
            .align_y(Alignment::Start);

        column![name, pieces_col, buttons]
            .align_x(Alignment::Start)
            .into()
    }
}