Initial commit
@@ -0,0 +1,7 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Generated by Tauri
|
||||
# will have schema files for capabilities auto-completion
|
||||
/gen/schemas
|
||||
@@ -0,0 +1,39 @@
|
||||
[package]
|
||||
name = "map-editor"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
# The `_lib` suffix may seem redundant but it is necessary
|
||||
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||
name = "map_editor_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
rand_xoshiro = "0.8.1"
|
||||
# bounded_macro = { path = "bounded_macro" }
|
||||
|
||||
# Read the optimization guideline for more details: https://tauri.app/concept/size/#cargo-configuration
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
opt-level = 3
|
||||
panic = "abort"
|
||||
strip = true
|
||||
|
||||
[dev-dependencies]
|
||||
color-eyre = "0.6.5"
|
||||
crossterm = "0.29.0"
|
||||
ratatui = "0.30.2"
|
||||
@@ -0,0 +1,46 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bounded_macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "bounded_macro"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.46"
|
||||
syn = { version = "2.0.118", features = ["full"] }
|
||||
@@ -0,0 +1,81 @@
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn bounded_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let ast: (syn::MetaList, syn::ItemImpl) = (
|
||||
syn::parse(attr).expect("Attr"),
|
||||
syn::parse(item).expect("Item"),
|
||||
);
|
||||
impl_bounded_macro(&ast.0, &ast.1)
|
||||
}
|
||||
|
||||
fn impl_bounded_macro(attr: &syn::MetaList, item: &syn::ItemImpl) -> TokenStream {
|
||||
let picked_max = item.items.iter()
|
||||
.map_while(|x| {
|
||||
if let syn::ImplItem::Const(x) = x {
|
||||
return Some(x);
|
||||
}
|
||||
None
|
||||
})
|
||||
.find(|x| {
|
||||
x.attrs.iter().any(|x| {
|
||||
x.meta.require_path_only().map_or(false, |x| {
|
||||
x.get_ident().map_or(false, |x| {
|
||||
x == "max"
|
||||
})
|
||||
})
|
||||
})
|
||||
}).expect("");
|
||||
let picked_min = item.items.iter()
|
||||
.map_while(|x| {
|
||||
if let syn::ImplItem::Const(x) = x {
|
||||
return Some(x);
|
||||
}
|
||||
None
|
||||
})
|
||||
.find(|x| {
|
||||
x.attrs.iter().any(|x| {
|
||||
x.meta.require_path_only().map_or(false, |x| {
|
||||
x.get_ident().map_or(false, |x| {
|
||||
x == "min"
|
||||
})
|
||||
})
|
||||
})
|
||||
}).expect("");
|
||||
|
||||
let syn::Expr::Lit(max) = &picked_max.expr else {
|
||||
panic!()
|
||||
};
|
||||
let syn::Expr::Lit(min) = &picked_min.expr else {
|
||||
panic!()
|
||||
};
|
||||
let name = match item.self_ty.as_ref() {
|
||||
syn::Type::Path(p) => p,
|
||||
_ => panic!()
|
||||
};
|
||||
quote::quote! {
|
||||
impl Add<Self> for #name {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self {
|
||||
degree: clip_range(self.degree + rhs.degree, #max, #min),
|
||||
minute: self.minute + rhs.minute,
|
||||
second: self.second + rhs.second,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<i64> for #name {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: i64) -> Self {
|
||||
Self {
|
||||
degree: clip_range(rhs * self.degree, Self::DEGREE_MAX, Self::DEGREE_MIN),
|
||||
minute: clip_range(rhs * self.minute, Self::MINUTE_MAX, Self::MINUTE_MIN),
|
||||
second: clip_range(rhs * self.second, Self::SECOND_MAX, Self::SECOND_MIN),
|
||||
}
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 567 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 536 B |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 777 B |
|
After Width: | Height: | Size: 1020 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 848 B |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,20 @@
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![greet])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
pub mod util;
|
||||
pub mod map;
|
||||
pub mod noise {
|
||||
use rand_xoshiro::Xoshiro256StarStar as RndEngine;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
map_editor_lib::run()
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
mod coordinate;
|
||||
|
||||
use crate::util::BezierCurve;
|
||||
|
||||
use coordinate::Coordinate;
|
||||
use coordinate::Latitude;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BaseFactor {
|
||||
seed: i64,
|
||||
outline: Outline,
|
||||
|
||||
tropic: Latitude,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Outline {
|
||||
curve: BezierCurve<Coordinate>,
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
mod latitude;
|
||||
mod longitude;
|
||||
|
||||
use std::ops::{Add, Sub, Mul, Rem};
|
||||
|
||||
pub use latitude::Latitude;
|
||||
pub use longitude::Longitude;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Coordinate {
|
||||
x: Latitude,
|
||||
y: Longitude,
|
||||
}
|
||||
|
||||
impl Add for Coordinate {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for Coordinate {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: f64) -> Self {
|
||||
Self {
|
||||
x: self.x * rhs,
|
||||
y: self.y * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Coordinate> for f64 {
|
||||
type Output = Coordinate;
|
||||
|
||||
fn mul(self, rhs: Coordinate) -> Coordinate {
|
||||
rhs * self
|
||||
}
|
||||
}
|
||||
|
||||
fn clip_range<T>(x: T, upper: T, lower: T) -> T
|
||||
where
|
||||
T: Copy + Add<T, Output = T> + Sub<T, Output = T> + Rem<T, Output = T> + PartialOrd<T>
|
||||
{
|
||||
if upper < lower {
|
||||
return x;
|
||||
}
|
||||
let len = upper - lower;
|
||||
if x > lower {
|
||||
(x - lower) % len + lower
|
||||
} else {
|
||||
len - (lower - x) % len + lower
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
use super::clip_range;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Latitude {
|
||||
degree: i64,
|
||||
minute: i64,
|
||||
second: i64,
|
||||
}
|
||||
|
||||
impl Latitude {
|
||||
// const DEGREE_MAX: i64 = 90;
|
||||
// const DEGREE_MIN: i64 = 0;
|
||||
// const MINUTE_MAX: i64 = 60;
|
||||
// const MINUTE_MIN: i64 = 0;
|
||||
// const SECOND_MAX: i64 = 60;
|
||||
// const SECOND_MIN: i64 = 0;
|
||||
|
||||
pub fn default() -> Self {
|
||||
Self { degree: 0, minute: 0, second: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Latitude {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
// Self {
|
||||
// degree: clip_range(self.degree + rhs.degree, Self::DEGREE_MAX, Self::DEGREE_MIN),
|
||||
// minute: clip_range(self.minute + rhs.minute, Self::MINUTE_MAX, Self::MINUTE_MIN),
|
||||
// second: clip_range(self.second + rhs.second, Self::SECOND_MAX, Self::SECOND_MIN),
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for Latitude {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: f64) -> Self {
|
||||
// Self {
|
||||
// degree: clip_range(rhs * self.degree, Self::DEGREE_MAX, Self::DEGREE_MIN),
|
||||
// minute: clip_range(rhs * self.minute, Self::MINUTE_MAX, Self::MINUTE_MIN),
|
||||
// second: clip_range(rhs * self.second, Self::SECOND_MAX, Self::SECOND_MIN),
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Latitude> for f64 {
|
||||
type Output = Latitude;
|
||||
|
||||
fn mul(self, rhs: Latitude) -> Latitude {
|
||||
rhs * self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
use super::clip_range;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Longitude {
|
||||
degree: i64,
|
||||
minute: i64,
|
||||
second: i64,
|
||||
}
|
||||
|
||||
impl Longitude {
|
||||
// const DEGREE_MAX: i64 = 180;
|
||||
// const DEGREE_MIN: i64 = -180;
|
||||
// const MINUTE_MAX: i64 = 60;
|
||||
// const MINUTE_MIN: i64 = 0;
|
||||
// const SECOND_MAX: i64 = 60;
|
||||
// const SECOND_MIN: i64 = 0;
|
||||
|
||||
pub fn default() -> Self {
|
||||
Self { degree: 0, minute: 0, second: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Longitude {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
// Self {
|
||||
// degree: clip_range(self.degree + rhs.degree, Self::DEGREE_MAX, Self::DEGREE_MIN),
|
||||
// minute: clip_range(self.minute + rhs.minute, Self::MINUTE_MAX, Self::MINUTE_MIN),
|
||||
// second: clip_range(self.second + rhs.second, Self::SECOND_MAX, Self::SECOND_MIN),
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for Longitude {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: f64) -> Self {
|
||||
// Self {
|
||||
// degree: clip_range(rhs * self.degree, Self::DEGREE_MAX, Self::DEGREE_MIN),
|
||||
// minute: clip_range(rhs * self.minute, Self::MINUTE_MAX, Self::MINUTE_MIN),
|
||||
// second: clip_range(rhs * self.second, Self::SECOND_MAX, Self::SECOND_MIN),
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Longitude> for f64 {
|
||||
type Output = Longitude;
|
||||
|
||||
fn mul(self, rhs: Longitude) -> Longitude {
|
||||
rhs * self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
mod basic;
|
||||
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
use basic::*;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct BezierCurve<T>
|
||||
where T: Add<T> + Mul<f64>
|
||||
{
|
||||
points: Vec<T>
|
||||
}
|
||||
|
||||
impl<T> BezierCurve<T>
|
||||
where T: Add<T, Output = T> + Mul<f64, Output = T>
|
||||
{
|
||||
pub fn new(ps: Vec<T>) -> Self {
|
||||
Self { points: ps }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> BezierCurve<T>
|
||||
where T: Add<T, Output = T> + Mul<f64, Output = T> + Copy
|
||||
{
|
||||
pub fn fetch_point(&self, mut t: f64) -> Option<T> {
|
||||
if t < 0.0 {
|
||||
t = 0.0;
|
||||
} else if t > 1.0 {
|
||||
t = 1.0;
|
||||
}
|
||||
if self.points.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let len: i32 = (self.points.len() - 1).try_into().ok()?;
|
||||
self.points.iter().enumerate()
|
||||
.map(|(i, p)| -> Option<T> {
|
||||
let i: i32 = i.try_into().ok()?;
|
||||
|
||||
let tn: f64 = t.powi(i) * (1.0 - t).powi(len - i);
|
||||
let cn: f64 = combination(len, i).try_into().ok()?;
|
||||
|
||||
Some(*p * tn * cn)
|
||||
})
|
||||
.reduce(|left, right| -> Option<T> {
|
||||
left.zip(right).map(|(left, right)| left + right)
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const EPSILON: f64 = 1e-6;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Point {
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
|
||||
impl Point {
|
||||
pub fn new(x: f64, y: f64) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Point {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self { x: self.x + rhs.x, y: self.y + rhs.y }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for Point {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: f64) -> Self {
|
||||
Self { x: self.x * rhs, y: self.y * rhs }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn curve_test() {
|
||||
let test_cur = vec![
|
||||
BezierCurve::new(vec![
|
||||
Point::new(-50.0, -50.0),
|
||||
Point::new(-50.0, 50.0),
|
||||
Point::new(50.0, 50.0),
|
||||
]),
|
||||
];
|
||||
let test_val = vec![
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
];
|
||||
test_cur.iter()
|
||||
.map(|cur| {
|
||||
print!("Curve between ", );
|
||||
cur.points.iter().for_each(|p| {
|
||||
print!("{}:{}, ", p.x, p.y);
|
||||
});
|
||||
println!("");
|
||||
test_val.iter()
|
||||
.map(|x| {
|
||||
cur.fetch_point(*x).inspect(|p| {
|
||||
println!("t = {}, point = {:.3}:{:.3}", x, p.x, p.y);
|
||||
});
|
||||
})
|
||||
.count();
|
||||
})
|
||||
.count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
pub fn level(mut x: i32) -> i32 {
|
||||
if x <= 1 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let mut res = 1;
|
||||
while x > 0 {
|
||||
res = res * x;
|
||||
x = x - 1;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn arrangement(n: i32, m: i32) -> i32 {
|
||||
if m < 0 || n <= 0 {
|
||||
return 0;
|
||||
}
|
||||
if m > n {
|
||||
return 0;
|
||||
}
|
||||
|
||||
level(n) / level(n - m)
|
||||
}
|
||||
|
||||
pub fn combination(n: i32, m: i32) -> i32 {
|
||||
if m < 0 || n <= 0 {
|
||||
return 0;
|
||||
}
|
||||
if m > n {
|
||||
return 0;
|
||||
}
|
||||
if m == 0 {
|
||||
return 1;
|
||||
}
|
||||
if m == n {
|
||||
return 1;
|
||||
}
|
||||
|
||||
level(n) / level(n - m) / level(m)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn util_basic_numeric() {
|
||||
assert_eq!(level(0), 1);
|
||||
assert_eq!(level(1), 1);
|
||||
|
||||
assert_eq!(level(3), 1 * 2 * 3);
|
||||
assert_eq!(level(6), level(3) * 4 * 5 * 6);
|
||||
assert_eq!(level(12), level(12));
|
||||
|
||||
|
||||
|
||||
assert_eq!(arrangement(0, 0), 0);
|
||||
assert_eq!(arrangement(1, 0), 0);
|
||||
assert_eq!(arrangement(0, 1), 0);
|
||||
|
||||
assert_eq!(arrangement(1, 3), 0);
|
||||
|
||||
assert_eq!(arrangement(5, 5), level(5));
|
||||
assert_eq!(arrangement(7, 5), level(7) / level(7 - 5));
|
||||
|
||||
|
||||
|
||||
assert_eq!(combination(0, 0), 0);
|
||||
assert_eq!(combination(1, 0), 0);
|
||||
assert_eq!(combination(0, 1), 0);
|
||||
|
||||
assert_eq!(combination(1, 3), 0);
|
||||
|
||||
assert_eq!(combination(8, 8), 1);
|
||||
assert_eq!(combination(9, 5), level(9) / level(9 - 5) / level (5));
|
||||
|
||||
|
||||
|
||||
assert_eq!(combination(8, 3), arrangement(8, 3) / arrangement(3, 3))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "map-editor",
|
||||
"version": "0.1.0",
|
||||
"identifier": "com.hydro.map-editor",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"beforeBuildCommand": "npm run build",
|
||||
"frontendDist": "../dist"
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": true,
|
||||
"windows": [
|
||||
{
|
||||
"title": "map-editor",
|
||||
"width": 800,
|
||||
"height": 600
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
use map_editor_lib::util::BezierCurve;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event;
|
||||
use ratatui::Frame;
|
||||
use ratatui::style::{Color, Stylize};
|
||||
use ratatui::layout::{Constraint, Layout, Rect};
|
||||
use ratatui::symbols::Marker;
|
||||
use ratatui::text::{Line as TextLine, Span};
|
||||
use ratatui::widgets::canvas::{Canvas, Line, Points};
|
||||
|
||||
#[test]
|
||||
fn curve_test_full() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
ratatui::run(|terminal| loop {
|
||||
terminal.draw(render)?;
|
||||
if event::read()?.is_key_press() {
|
||||
break Ok(());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Render the UI with a canvas widget.
|
||||
fn render(frame: &mut Frame) {
|
||||
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1);
|
||||
let horizontal = Layout::horizontal([Constraint::Percentage(100)]).spacing(1);
|
||||
let [top, main] = frame.area().layout(&vertical);
|
||||
let [area] = main.layout(&horizontal);
|
||||
|
||||
let title = TextLine::from_iter([
|
||||
Span::from("Canvas Widget").bold(),
|
||||
Span::from(" (Press 'q' to quit)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_canvas(frame, area);
|
||||
}
|
||||
|
||||
/// Renders the canvas widget with various shapes and a map.
|
||||
pub fn render_canvas(frame: &mut Frame, area: Rect) {
|
||||
let sample_t = vec![
|
||||
0.0,
|
||||
0.2,
|
||||
0.4,
|
||||
0.6,
|
||||
0.8,
|
||||
1.0,
|
||||
];
|
||||
let curve = BezierCurve::new(vec![
|
||||
Point::new(-50.0, -50.0),
|
||||
Point::new(-50.0, 50.0),
|
||||
Point::new(50.0, 50.0),
|
||||
]);
|
||||
let canvas = Canvas::default()
|
||||
.x_bounds([-180.0, 180.0])
|
||||
.y_bounds([-90.0, 90.0])
|
||||
.marker(Marker::Braille)
|
||||
.paint(|ctx| {
|
||||
ctx.draw(&Points::new(&[
|
||||
(-50.0, -50.0),
|
||||
(-50.0, 50.0),
|
||||
(50.0, 50.0),
|
||||
], Color::Black));
|
||||
|
||||
// sample_t.windows(2)
|
||||
// .map_while(|a| {
|
||||
// curve.fetch_point(a[0]).zip(curve.fetch_point(a[1]))
|
||||
// })
|
||||
// .for_each(|(x, y)| {
|
||||
// ctx.draw(&Line::new(x.x, x.y, y.x, y.y, Color::Blue));
|
||||
// });
|
||||
});
|
||||
|
||||
frame.render_widget(canvas, area);
|
||||
}
|
||||
|
||||
const EPSILON: f64 = 1e-6;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Point {
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
|
||||
impl Point {
|
||||
pub fn new(x: f64, y: f64) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Point {
|
||||
type Output = Self;
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self { x: self.x + rhs.x, y: self.y + rhs.y }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for Point {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: f64) -> Self {
|
||||
Self { x: self.x * rhs, y: self.y * rhs }
|
||||
}
|
||||
}
|
||||