Skip to content

Commit

Permalink
Add some placeholder data for better demo'in
Browse files Browse the repository at this point in the history
  • Loading branch information
RyanMeulenkamp committed Dec 30, 2020
1 parent fbbd690 commit 51cee3b
Show file tree
Hide file tree
Showing 14 changed files with 379 additions and 83 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2018"
crate-type = ["cdylib", "rlib"]

[dependencies]
druid = { git = "https://github.com/linebender/druid.git", branch = "value-formatter", features = ["im", "image"] }
druid = { git = "https://github.com/linebender/druid.git", features = ["im", "image"] }
wasm-bindgen = "0.2.69"
console_error_panic_hook = "0.1.6"
log = "0.4.11"
Expand Down
46 changes: 21 additions & 25 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
html, body, canvas {
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<noscript>This page contains WebAssembly and JavaScript content, please enable JavaScript in your browser.</noscript>
<canvas id="canvas"></canvas>
<script type="module">
import init, { app } from './pkg/solplan.js';
<head>
<meta charset="UTF-8">
<title>Intro</title>
</head>
<body>

<h1>Disclaimer</h1>

<p>
This is a wasm demo build of <a href="https://github.com/RyanMeulenkamp/solplan-rust">Solplan</a>, a
little application that I wrote to layout solar panels on a roof. This was more of a Rust practise then
an actual usable application.
</p>

<p>
It is garbage-in = garbage-out. The input data is just there for convenience/as a placeholder.
If you actually want to use the application, please check the inputs for yourself, especially
the panel types.
</p>

async function run() {
await init();
app();
}
<h1><a href="solplan.html">Go ahead</a></h1>

run();
</script>
</body>
</body>
</html>
30 changes: 30 additions & 0 deletions solplan.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
html, body, canvas {
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<noscript>This page contains WebAssembly and JavaScript content, please enable JavaScript in your browser.</noscript>
<canvas id="canvas"></canvas>
<script type="module">
import init, { app } from './pkg/solplan.js';

async function run() {
await init();
app();
}

run();
</script>
</body>
</html>
155 changes: 155 additions & 0 deletions src/format/currencyformatter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Shameless copy of the formatter example https://github.com/linebender/druid/pull/1377

use druid::text::format::{Validation, ValidationError, Formatter};
use druid::text::Selection;

pub struct CurrencyFormatter {
currency_symbol: char,
thousands_separator: char,
decimal_separator: char,
}

#[derive(Debug, Clone)]
pub enum CurrencyValidationError {
Parse(std::num::ParseFloatError),
InvalidChar(char),
TooManyCharsAfterDecimal,
}

impl std::fmt::Display for CurrencyValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
CurrencyValidationError::InvalidChar(c) => write!(f, "Invalid character '{}'", c),
CurrencyValidationError::Parse(err) => write!(f, "Parse failed: {}", err),
CurrencyValidationError::TooManyCharsAfterDecimal => {
write!(f, "Too many characters after decimal")
}
}
}
}

impl std::error::Error for CurrencyValidationError {

}

impl CurrencyFormatter {
/// A formatter for USD.
pub const DOLLARS: CurrencyFormatter = CurrencyFormatter {
currency_symbol: '$',
thousands_separator: ',',
decimal_separator: '.',
};

/// A formatter for euros.
pub const EUROS: CurrencyFormatter = CurrencyFormatter {
currency_symbol: '€',
thousands_separator: '.',
decimal_separator: ',',
};

/// A formatter for british pounds.
pub const GBP: CurrencyFormatter = CurrencyFormatter {
currency_symbol: '£',
thousands_separator: '.',
decimal_separator: ',',
};
}

impl Formatter<f64> for CurrencyFormatter {
fn format(&self, value: &f64) -> String {
if !value.is_normal() {
return format!("{}0{}00", self.currency_symbol, self.decimal_separator);
}

let mut components = Vec::new();
let mut major_part = value.abs().trunc() as usize;
let minor_part = (value.abs().fract() * 100.0).round() as usize;

let bonus_rounding_dollar = minor_part / 100;

components.push(format!("{}{:02}", self.decimal_separator, minor_part % 100));
if major_part == 0 {
components.push('0'.to_string());
}

while major_part > 0 {
let remain = major_part % 1000;
major_part /= 1000;
if major_part > 0 {
components.push(format!("{}{:03}", self.thousands_separator, remain));
} else {
components.push((remain + bonus_rounding_dollar).to_string());
}
}
if value.is_sign_negative() {
components.push(format!("-{}", self.currency_symbol));
} else {
components.push(self.currency_symbol.to_string());
}

components.iter().rev().flat_map(|s| s.chars()).collect()
}

fn format_for_editing(&self, value: &f64) -> String {
self.format(value)
.chars()
.filter(|c| *c != self.currency_symbol)
.collect()
}

fn value(&self, input: &str) -> Result<f64, ValidationError> {
// we need to convert from our naive localized representation back into
// rust's float representation
let decimal_pos = input
.bytes()
.rposition(|b| b as char == self.decimal_separator);
let (major, minor) = input.split_at(decimal_pos.unwrap_or_else(|| input.len()));
let canonical: String = major
.chars()
.filter(|c| *c != self.thousands_separator)
.chain(Some('.'))
.chain(minor.chars().skip(1))
.collect();
canonical
.parse()
.map_err(|err| ValidationError::new(CurrencyValidationError::Parse(err)))
}

fn validate_partial_input(&self, input: &str, _sel: &Selection) -> Validation {
if input.is_empty() {
return Validation::success();
}

let mut char_iter = input.chars();
if let Some(c) = char_iter.next() {
if !(c.is_ascii_digit() || c == '-') {
return Validation::failure(CurrencyValidationError::InvalidChar(c));
}
}
let mut char_iter =
char_iter.skip_while(|c| c.is_ascii_digit() || *c == self.thousands_separator);
match char_iter.next() {
None => return Validation::success(),
Some(c) if c == self.decimal_separator => (),
Some(c) => return Validation::failure(CurrencyValidationError::InvalidChar(c)),
};

// we're after the decimal, allow up to 2 digits
let (d1, d2, d3) = (char_iter.next(), char_iter.next(), char_iter.next());
match (d1, d2, d3) {
(_, _, Some(_)) => {
Validation::failure(CurrencyValidationError::TooManyCharsAfterDecimal)
}
(Some(c), None, _) if c.is_ascii_digit() => Validation::success(),
(None, None, _) => Validation::success(),
(Some(c1), Some(c2), _) if c1.is_ascii_digit() && c2.is_ascii_digit() => {
Validation::success()
}
(Some(c1), Some(other), _) => {
let bad_char = if c1.is_ascii_digit() { other } else { c1 };
Validation::failure(CurrencyValidationError::InvalidChar(bad_char))
}
_ => unreachable!(),
}
}
}
1 change: 1 addition & 0 deletions src/format/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod siformatter;
pub mod currencyformatter;
88 changes: 72 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::model::state::State;
use crate::widget::{boundarywidget::create_boundary_widget, roofwidget::create_roof_widget};
use crate::widget::clearancewidget::create_clearance_widget;
use crate::widget::constraintswidget::create_constraints_widget;
use crate::widget::panelwidget::{create_add_remove_widget, create_panels_widget};
use crate::widget::panelwidget::{create_controls_widget, create_panels_widget};
use crate::widget::planwidget::create_plan_widget;

pub mod format;
Expand Down Expand Up @@ -59,7 +59,7 @@ fn build_app() -> impl Widget<State> {
.expand_width()
)
.with_child(
create_add_remove_widget()
create_controls_widget()
.border(theme::PLACEHOLDER_COLOR, 1.5),
)
.with_flex_child(
Expand Down Expand Up @@ -96,20 +96,76 @@ fn build_app() -> impl Widget<State> {
#[wasm_bindgen]
pub fn app() {
console_error_panic_hook::set_once();
let window = WindowDesc::new(build_app).title(LocalizedString::new("Solplan"));

let roof = Roof::new(9625.0, 13500.0, 7320.0);
let boundary = Boundary::new(30.0, 200.0, 30.0, 30.0);
let panels = vector![
Panel::new("JinkoSolar JKM-335M-60H", 1684.0, 1002.0, 335.0),
Panel::new("Sunpower Maxeon 3", 1690.0, 1046.0, 400.0),
];
let clearance = Clearance::new(10.0, 10.0);
let constraints = Constraints::new(true, true, false);
let state = State::new(roof, boundary, panels, clearance, constraints);

AppLauncher::with_window(window)

AppLauncher::with_window(WindowDesc::new(build_app).title(LocalizedString::new("Solplan")))
.use_simple_logger()
.launch(state)
.launch(State::new(
Roof::new(9625.0, 13500.0, 7320.0),
Boundary::new(30.0, 200.0, 30.0, 30.0),
vector![
Panel::new("JinkoSolar JKM320M-60HB", 1692.0, 1029.0, 315.0, 315.0 / 3.0),
Panel::new("JinkoSolar JKM325M-60H(B)", 1684.0, 1002.0, 325.0, 325.0 / 3.0),
Panel::new("JinkoSolar JKM330M-60H(B)", 1684.0, 1002.0, 330.0, 330.0 / 3.0),
Panel::new("JinkoSolar JKM335M-60H(B)", 1684.0, 1002.0, 335.0, 335.0 / 3.0),
Panel::new("JinkoSolar JKM340M-60H(B)", 1684.0, 1002.0, 340.0, 340.0 / 3.0),
Panel::new("JinkoSolar JKM345M-60H", 1684.0, 1002.0, 345.0, 345.0 / 3.0),

Panel::new("JinkoSolar JKM345M-66HB", 1841.0, 1002.0, 345.0, 345.0 / 3.0),
Panel::new("JinkoSolar JKM350M-66HB", 1841.0, 1002.0, 350.0, 350.0 / 3.0),
Panel::new("JinkoSolar JKM355M-66H(B)", 1841.0, 1002.0, 355.0, 355.0 / 3.0),
Panel::new("JinkoSolar JKM360M-66H(B)", 1841.0, 1002.0, 360.0, 360.0 / 3.0),
Panel::new("JinkoSolar JKM365M-66H(B)", 1841.0, 1002.0, 365.0, 365.0 / 3.0),
Panel::new("JinkoSolar JKM370M-66H", 1841.0, 1002.0, 370.0, 370.0 / 3.0),
Panel::new("JinkoSolar JKM375M-66H", 1841.0, 1002.0, 375.0, 375.0 / 3.0),

Panel::new("JinkoSolar JKM390M-72H", 2008.0, 1002.0, 390.0, 390.0 / 3.0),
Panel::new("JinkoSolar JKM395M-72H", 2008.0, 1002.0, 395.0, 395.0 / 3.0),
Panel::new("JinkoSolar JKM400M-72H", 2008.0, 1002.0, 400.0, 400.0 / 3.0),
Panel::new("JinkoSolar JKM405M-72H", 2008.0, 1002.0, 405.0, 405.0 / 3.0),
Panel::new("JinkoSolar JKM410M-72H", 2008.0, 1002.0, 410.0, 410.0 / 3.0),

Panel::new("JinkoSolar JKM425M-78H", 2166.0, 1002.0, 425.0, 425.0 / 3.0),
Panel::new("JinkoSolar JKM430M-78H", 2166.0, 1002.0, 430.0, 430.0 / 3.0),
Panel::new("JinkoSolar JKM435M-78H", 2166.0, 1002.0, 435.0, 435.0 / 3.0),
Panel::new("JinkoSolar JKM440M-78H", 2166.0, 1002.0, 440.0, 440.0 / 3.0),
Panel::new("JinkoSolar JKM445M-78H", 2166.0, 1002.0, 445.0, 445.0 / 3.0),

Panel::new("JinkoSolar JKM375N-6RL3", 1855.0, 1029.0, 375.0, 375.0 / 3.0),
Panel::new("JinkoSolar JKM370N-6RL3", 1855.0, 1029.0, 370.0, 370.0 / 3.0),
Panel::new("JinkoSolar JKM385N-6RL3", 1855.0, 1029.0, 385.0, 385.0 / 3.0),
Panel::new("JinkoSolar JKM380N-6RL3", 1855.0, 1029.0, 380.0, 380.0 / 3.0),
Panel::new("JinkoSolar JKM395N-6RL3", 1855.0, 1029.0, 395.0, 395.0 / 3.0),

Panel::new("JinkoSolar JKM450N-7RL3", 2182.0, 1029.0, 450.0, 450.0 / 3.0),
Panel::new("JinkoSolar JKM455N-7RL3", 2182.0, 1029.0, 455.0, 455.0 / 3.0),
Panel::new("JinkoSolar JKM460N-7RL3", 2182.0, 1029.0, 460.0, 460.0 / 3.0),
Panel::new("JinkoSolar JKM465N-7RL3", 2182.0, 1029.0, 465.0, 465.0 / 3.0),
Panel::new("JinkoSolar JKM470N-7RL3", 2182.0, 1029.0, 470.0, 470.0 / 3.0),

Panel::new("Sunpower X22-345", 1559.0, 1046.0, 345.0, 345.0 / 3.0),
Panel::new("Sunpower X22-360", 1559.0, 1046.0, 360.0, 360.0 / 3.0),
Panel::new("Sunpower X22-370", 1559.0, 1046.0, 370.0, 370.0 / 3.0),

Panel::new("Sunpower MAX2-340", 1690.0, 1046.0, 340.0, 340.0 / 3.0),
Panel::new("Sunpower MAX2-350", 1690.0, 1046.0, 350.0, 350.0 / 3.0),
Panel::new("Sunpower MAX2-360", 1690.0, 1046.0, 360.0, 360.0 / 3.0),

Panel::new("Sunpower MAX3-355", 1690.0, 1046.0, 355.0, 390.0 / 3.0),
Panel::new("Sunpower MAX3-370", 1690.0, 1046.0, 370.0, 395.0 / 3.0),
Panel::new("Sunpower MAX3-375", 1690.0, 1046.0, 375.0, 400.0 / 3.0),

Panel::new("Sunpower MAX3-390", 1690.0, 1046.0, 390.0, 390.0 / 3.0),
Panel::new("Sunpower MAX3-395", 1690.0, 1046.0, 395.0, 395.0 / 3.0),
Panel::new("Sunpower MAX3-400", 1690.0, 1046.0, 400.0, 400.0 / 3.0),

Panel::new("Sunpower P3-315", 1690.0, 1046.0, 315.0, 315.0 / 3.0),
Panel::new("Sunpower P3-320", 1690.0, 1046.0, 320.0, 320.0 / 3.0),
Panel::new("Sunpower P3-325", 1690.0, 1046.0, 325.0, 325.0 / 3.0),
Panel::new("Sunpower P3-330", 1690.0, 1046.0, 330.0, 330.0 / 3.0),
Panel::new("Sunpower P3-335", 1690.0, 1046.0, 335.0, 335.0 / 3.0),
],
Clearance::new(10.0, 10.0),
Constraints::new(true, true, false, 30000.0),
))
.expect("launch failed");
}
Loading

0 comments on commit 51cee3b

Please sign in to comment.