|
|
@ -1,4 +1,4 @@ |
|
|
|
#![recursion_limit = "1024"]
|
|
|
|
#![recursion_limit = "10240"]
|
|
|
|
|
|
|
|
#[global_allocator]
|
|
|
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
|
|
@ -15,6 +15,8 @@ use components::wood::WoodComponent; |
|
|
|
mod calculator;
|
|
|
|
use calculator::{find_best_cuts, CutRow, Req1D, Wood1D};
|
|
|
|
|
|
|
|
mod util;
|
|
|
|
|
|
|
|
struct AvailableRow {
|
|
|
|
name: String,
|
|
|
|
length: f64, // mm
|
|
|
@ -32,9 +34,9 @@ impl AvailableRow { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn name(&self) -> String {
|
|
|
|
fn name(&self, metric: bool) -> String {
|
|
|
|
if self.name.is_empty() {
|
|
|
|
format!("[Unnamed] {:.2} mm", self.length)
|
|
|
|
format!("[Unnamed] {}", util::format_dimension(self.length, metric))
|
|
|
|
} else {
|
|
|
|
self.name.clone()
|
|
|
|
}
|
|
|
@ -58,9 +60,9 @@ impl RequiredRow { |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn name(&self) -> String {
|
|
|
|
fn name(&self, metric: bool) -> String {
|
|
|
|
if self.name.is_empty() {
|
|
|
|
format!("[Unnamed] {:.2} mm", self.length)
|
|
|
|
format!("[Unnamed] {}", util::format_dimension(self.length, metric))
|
|
|
|
} else {
|
|
|
|
self.name.clone()
|
|
|
|
}
|
|
|
@ -81,7 +83,8 @@ enum Msg { |
|
|
|
DeleteRequired(usize),
|
|
|
|
|
|
|
|
ChangeBladeWidth(String),
|
|
|
|
ForceUpdate,
|
|
|
|
ChangeMetric,
|
|
|
|
ChangeImperial,
|
|
|
|
|
|
|
|
Calculate,
|
|
|
|
}
|
|
|
@ -99,12 +102,14 @@ struct Model { |
|
|
|
impl Model {
|
|
|
|
// convert into metric/imperial depending on user's settings
|
|
|
|
fn get_blade_width_string(&self) -> String {
|
|
|
|
format!("{:.3}", if self.metric {
|
|
|
|
self.blade_width
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self.blade_width / 25.4
|
|
|
|
})
|
|
|
|
format!(
|
|
|
|
"{:.3}",
|
|
|
|
if self.metric {
|
|
|
|
self.blade_width
|
|
|
|
} else {
|
|
|
|
self.blade_width / 25.4
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_blade_width(&mut self, w: f64) {
|
|
|
@ -116,7 +121,11 @@ impl Model { |
|
|
|
}
|
|
|
|
|
|
|
|
fn blade_width_units(&self) -> &'static str {
|
|
|
|
if self.metric { "mm" } else { "\"" }
|
|
|
|
if self.metric {
|
|
|
|
"mm"
|
|
|
|
} else {
|
|
|
|
"\""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
@ -150,6 +159,7 @@ impl Component for Model { |
|
|
|
|
|
|
|
Msg::ChangeAvailableCost(id, cost) => {
|
|
|
|
self.available[id].cost = cost;
|
|
|
|
change = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Msg::DeleteAvailable(id) => {
|
|
|
@ -186,12 +196,18 @@ impl Component for Model { |
|
|
|
change = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Msg::ForceUpdate => {}
|
|
|
|
Msg::ChangeMetric => {
|
|
|
|
self.metric = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Msg::ChangeImperial => {
|
|
|
|
self.metric = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Msg::Calculate => {
|
|
|
|
// convert available/required wood to the expected types
|
|
|
|
let available_names: Vec<String> =
|
|
|
|
self.available.iter().map(|x| x.name()).collect();
|
|
|
|
self.available.iter().map(|x| x.name(self.metric)).collect();
|
|
|
|
let available: Vec<Wood1D> = self
|
|
|
|
.available
|
|
|
|
.iter()
|
|
|
@ -200,7 +216,8 @@ impl Component for Model { |
|
|
|
.map(|(i, x)| Wood1D::new(&available_names[i], x.length, x.cost))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let required_names: Vec<String> = self.required.iter().map(|x| x.name()).collect();
|
|
|
|
let required_names: Vec<String> =
|
|
|
|
self.required.iter().map(|x| x.name(self.metric)).collect();
|
|
|
|
let mut required = Vec::new();
|
|
|
|
for (i, req) in self.required.iter().enumerate().filter(|(_, x)| !x.deleted) {
|
|
|
|
for _ in 0..req.amount {
|
|
|
@ -227,9 +244,6 @@ impl Component for Model { |
|
|
|
}
|
|
|
|
|
|
|
|
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
|
|
|
|
// Should only return "true" if new properties are different to
|
|
|
|
// previously received properties.
|
|
|
|
// This component has no properties so we will always return "false".
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
@ -243,6 +257,54 @@ impl Component for Model { |
|
|
|
<div class="container">
|
|
|
|
<div class="row">
|
|
|
|
<div class="col-sm">
|
|
|
|
<div>
|
|
|
|
<h4>{ "General Settings" }</h4>
|
|
|
|
|
|
|
|
//Metric radio
|
|
|
|
|
|
|
|
<div class="form-check">
|
|
|
|
<input
|
|
|
|
class="form-check-input"
|
|
|
|
type="radio"
|
|
|
|
name="unitRadio"
|
|
|
|
id="metricRadio"
|
|
|
|
onclick=self.link.callback(|_| Msg::ChangeMetric)
|
|
|
|
checked=self.metric
|
|
|
|
/>
|
|
|
|
<label class="form-check-label" for="metricRadio"> { "Metric" } </label>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
// Imperial radio
|
|
|
|
|
|
|
|
<div class="form-check">
|
|
|
|
<input
|
|
|
|
class="form-check-input"
|
|
|
|
type="radio"
|
|
|
|
name="unitRadio"
|
|
|
|
id="imperialRadio"
|
|
|
|
onclick=self.link.callback(|_| Msg::ChangeImperial)
|
|
|
|
checked=!self.metric
|
|
|
|
/>
|
|
|
|
<label class="form-check-label" for="imperialRadio"> { "Imperial" } </label>
|
|
|
|
</div>
|
|
|
|
<table class="bottom-margin">
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
{ "Sawblade Width" }
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<form novalidate=true>
|
|
|
|
<input type="number" min="0" step="0.125" value={self.get_blade_width_string()}
|
|
|
|
oninput=self.link.callback(|e: InputData| Msg::ChangeBladeWidth(e.value))
|
|
|
|
/>
|
|
|
|
</form>
|
|
|
|
{ self.blade_width_units() }
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="col-sm">
|
|
|
|
<h2>
|
|
|
|
<button
|
|
|
|
class="btn btn-info right-margin"
|
|
|
@ -258,7 +320,8 @@ impl Component for Model { |
|
|
|
<div class="wood">
|
|
|
|
<table>
|
|
|
|
<AvailableComponent
|
|
|
|
length=wood.length metric=false
|
|
|
|
metric=self.metric
|
|
|
|
length=wood.length
|
|
|
|
length_callback=self.link.callback(move |v| Msg::ChangeAvailableLength(idx, v))
|
|
|
|
cost=wood.cost
|
|
|
|
cost_callback=self.link.callback(move |v| Msg::ChangeAvailableCost(idx, v))
|
|
|
@ -278,9 +341,8 @@ impl Component for Model { |
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
<div class="col-sm">
|
|
|
|
<h2>
|
|
|
|
|
|
|
|
<h2 class="top-margin">
|
|
|
|
<button
|
|
|
|
class="btn btn-info right-margin"
|
|
|
|
onclick=self.link.callback(|_| Msg::AddRequired)>{ "+" }</button>
|
|
|
@ -295,6 +357,7 @@ impl Component for Model { |
|
|
|
<div class="wood">
|
|
|
|
<table>
|
|
|
|
<WoodComponent
|
|
|
|
metric=self.metric
|
|
|
|
length=wood.length
|
|
|
|
length_callback=self.link.callback(move |v| Msg::ChangeRequiredLength(idx, v))
|
|
|
|
name=wood.name.clone()
|
|
|
@ -322,22 +385,6 @@ impl Component for Model { |
|
|
|
}
|
|
|
|
</div>
|
|
|
|
<div class="col-sm">
|
|
|
|
<div>
|
|
|
|
<h4>{ "General Settings" }</h4>
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
{ "Sawblade Width" }
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<input type="number" min="0" step="0.125" value={self.get_blade_width_string()}
|
|
|
|
oninput=self.link.callback(|e: InputData| Msg::ChangeBladeWidth(e.value))
|
|
|
|
/>
|
|
|
|
{ self.blade_width_units() }
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
<button
|
|
|
|
class="btn btn-primary"
|
|
|
|
onclick=self.link.callback(|_| Msg::Calculate)>{ "Calculate" }</button>
|
|
|
@ -351,6 +398,7 @@ impl Component for Model { |
|
|
|
for self.output.iter().map(|row| {
|
|
|
|
html! {
|
|
|
|
<CutlistComponent
|
|
|
|
metric=self.metric
|
|
|
|
name=&row.name
|
|
|
|
cost=row.cost
|
|
|
|
cuts=&row.pieces
|
|
|
|