This commit is contained in:
elvis
2025-11-12 17:35:00 +01:00
parent f06991d2ff
commit 41ceea2bcc
5 changed files with 290 additions and 169 deletions

View File

@ -1134,7 +1134,8 @@ where
let max_connections = self.graph[*param]
.max_connections
.map(NonZeroU32::get)
.unwrap_or(u32::MAX) as usize;
.unwrap_or(u32::MAX)
as usize;
draw_port(
pan_zoom,
ui,
@ -1209,8 +1210,12 @@ where
vec2(outer_rect.width(), outer_rect.height() - titlebar_height),
);
let body = Shape::Rect(
RectShape::filled(body_rect, CornerRadius::ZERO, background_color)
.with_texture(Default::default(), Rect::ZERO),
RectShape::filled(
body_rect,
CornerRadius::ZERO,
background_color,
)
.with_texture(Default::default(), Rect::ZERO),
);
let bottom_body_rect = Rect::from_min_size(
@ -1230,8 +1235,9 @@ where
RectShape::filled(
node_rect.expand(1.0 * pan_zoom.zoom),
rounding,
Color32::WHITE.lighten(0.8))
.with_texture(Default::default(), Rect::ZERO)
Color32::WHITE.lighten(0.8),
)
.with_texture(Default::default(), Rect::ZERO),
)
} else {
Shape::Noop

View File

@ -79,8 +79,8 @@ where
let mut query_submit = resp.lost_focus()
&& ui.input(|i| i.key_pressed(Key::Enter));
let max_height =
ui.input(|i| f32::max(i.content_rect().height() * 0.5, 200.));
let max_height = ui
.input(|i| f32::max(i.content_rect().height() * 0.5, 200.));
let scroll_area_width = resp.rect.width();
let all_kinds = all_kinds.all_kinds();

View File

@ -494,7 +494,7 @@ impl NodeInstruction {
],
| Self::Sleep => vec![("seconds", PositiveInt)],
| Self::StringToSvg => vec![("value", String)],
| Self::SaveSvg => vec![("path", Path), ("value", Svg)]
| Self::SaveSvg => vec![("path", Path), ("value", Svg)],
}
.into_iter()
.map(|e| (e.0.to_string(), e.1))
@ -695,10 +695,7 @@ impl NodeInstruction {
PositiveGroupFunction,
assert::positive_grouping::PositiveAssert::default()
),
| BasicDataType::Svg => helper!(
Svg,
super::svg::Svg::default()
)
| BasicDataType::Svg => helper!(Svg, super::svg::Svg::default()),
}
}
@ -754,8 +751,7 @@ impl NodeInstruction {
helper!(PositiveAssertFunction),
| BasicDataType::PositiveGroupFunction =>
helper!(PositiveGroupFunction),
| BasicDataType::Svg =>
helper!(Svg),
| BasicDataType::Svg => helper!(Svg),
}
}
}
@ -796,7 +792,9 @@ pub(crate) struct OutputsCache {
impl Clone for OutputsCache {
fn clone(&self) -> Self {
Self { internals: Arc::clone(&self.internals) }
Self {
internals: Arc::clone(&self.internals),
}
}
}
@ -952,8 +950,7 @@ impl DataTypeTrait<GlobalState> for BasicDataType {
egui::Color32::from_rgb(200, 150, 120),
| Self::PositiveGroupFunction =>
egui::Color32::from_rgb(150, 120, 200),
| Self::Svg =>
egui::Color32::from_rgb(200, 200, 240),
| Self::Svg => egui::Color32::from_rgb(200, 200, 240),
}
}
@ -1172,9 +1169,8 @@ impl NodeTemplateTrait for NodeInstruction {
| Self::PositiveBisimilarityPaigeTarjanNoLabels
| Self::PositiveBisimilarityPaigeTarjan =>
vec!["Positive Graph", "Positive Bisimilarity"],
| Self::Sleep
| Self::StringToSvg
| Self::SaveSvg => vec!["General"],
| Self::Sleep | Self::StringToSvg | Self::SaveSvg =>
vec!["General"],
}
}
@ -1423,7 +1419,7 @@ impl WidgetValueTrait for BasicValue {
},
| BasicValue::Svg { value: _ } => {
ui.label(param_name);
}
},
}
responses
@ -1467,13 +1463,12 @@ impl NodeDataTrait for NodeData {
));
}
},
| (_, NodeInstruction::SaveSvg) => {
| (_, NodeInstruction::SaveSvg) =>
if ui.button("Write").clicked() {
responses.push(NodeResponse::User(
CustomResponse::SaveToFile(node_id),
));
}
}
},
| (true, NodeInstruction::ReadPath) => {
// since no filewatcher we simply give the option to reload the
// file
@ -1597,7 +1592,13 @@ impl AppHandle {
.unwrap_or_default();
let user_state = Arc::new(RwLock::new(GlobalState::default()));
Self { state, user_state, cache, translator, ..Default::default() }
Self {
state,
user_state,
cache,
translator,
..Default::default()
}
}
}
@ -1607,10 +1608,10 @@ fn write_state(
state: &str,
translator: &str,
cache: &str,
path: &std::path::PathBuf
path: &std::path::PathBuf,
) -> std::io::Result<()> {
use std::io::{Write, BufWriter};
use std::fs::File;
use std::io::{BufWriter, Write};
let f = File::create(path)?;
let mut writer = BufWriter::new(f);
@ -1630,10 +1631,14 @@ fn write_state(
#[cfg(feature = "persistence")]
#[cfg(not(target_arch = "wasm32"))]
fn read_state(
path: &std::path::PathBuf
) -> Result<(EditorState, rsprocess::translator::Translator, OutputsCache), String> {
use std::io::Read;
path: &std::path::PathBuf,
) -> Result<
(EditorState, rsprocess::translator::Translator, OutputsCache),
String,
> {
use std::fs::File;
use std::io::Read;
use rsprocess::translator::Translator;
let mut f = File::open(path).map_err(|e| format!("{e}"))?;
@ -1666,25 +1671,37 @@ fn read_state(
let string_state = {
let mut s = Vec::new();
s.reserve_exact(len_state as usize);
f.by_ref().take(len_state).read_to_end(&mut s).map_err(|e| format!("{e}"))?;
f.by_ref()
.take(len_state)
.read_to_end(&mut s)
.map_err(|e| format!("{e}"))?;
String::from_utf8(s).map_err(|e| format!("{e}"))?
};
let string_translator = {
let mut s = Vec::new();
s.reserve_exact(len_translator as usize);
f.by_ref().take(len_translator).read_to_end(&mut s).map_err(|e| format!("{e}"))?;
f.by_ref()
.take(len_translator)
.read_to_end(&mut s)
.map_err(|e| format!("{e}"))?;
String::from_utf8(s).map_err(|e| format!("{e}"))?
};
let string_cache = {
let mut s = Vec::new();
s.reserve_exact(len_cache as usize);
f.by_ref().take(len_cache).read_to_end(&mut s).map_err(|e| format!("{e}"))?;
f.by_ref()
.take(len_cache)
.read_to_end(&mut s)
.map_err(|e| format!("{e}"))?;
String::from_utf8(s).map_err(|e| format!("{e}"))?
};
let state = ron::from_str::<EditorState>(&string_state).map_err(|e| format!("{e}"))?;
let translator = ron::from_str::<Translator>(&string_translator).map_err(|e| format!("{e}"))?;
let cache = ron::from_str::<OutputsCache>(&string_cache).map_err(|e| format!("{e}"))?;
let state = ron::from_str::<EditorState>(&string_state)
.map_err(|e| format!("{e}"))?;
let translator = ron::from_str::<Translator>(&string_translator)
.map_err(|e| format!("{e}"))?;
let cache = ron::from_str::<OutputsCache>(&string_cache)
.map_err(|e| format!("{e}"))?;
Ok((state, translator, cache))
}
@ -1747,7 +1764,7 @@ impl eframe::App for AppHandle {
.pick_file()
{
match read_state(&path) {
Ok((state, translator, cache)) => {
| Ok((state, translator, cache)) => {
eframe::set_value(
_frame
.storage_mut()
@ -1761,18 +1778,23 @@ impl eframe::App for AppHandle {
_frame
.storage_mut()
.expect("no storage found"),
TRANSLATOR_KEY, &translator,
TRANSLATOR_KEY,
&translator,
);
self.translator = Arc::new(Mutex::new(translator));
self.translator =
Arc::new(Mutex::new(translator));
eframe::set_value(
_frame
.storage_mut()
.expect("no storage found"),
CACHE_KEY, &cache);
CACHE_KEY,
&cache,
);
self.cache = cache;
},
Err(e) => println!("Error reading file: {e}"),
| Err(e) =>
println!("Error reading file: {e}"),
}
ui.close();
}
@ -1790,7 +1812,8 @@ impl eframe::App for AppHandle {
},
};
let translator =
match ron::ser::to_string(&self.translator) {
match ron::ser::to_string(&self.translator)
{
| Ok(value) => value,
| Err(e) => {
println!("Error serializing: {e}");
@ -1805,9 +1828,15 @@ impl eframe::App for AppHandle {
panic!()
},
};
match write_state(&state, &translator, &cache, &path) {
Ok(_) => {},
Err(e) => println!("Could not save file: {e}"),
match write_state(
&state,
&translator,
&cache,
&path,
) {
| Ok(_) => {},
| Err(e) =>
println!("Could not save file: {e}"),
}
ui.close();
@ -1878,8 +1907,7 @@ impl eframe::App for AppHandle {
self.cached_last_value = None;
},
| NodeResponse::User(CustomResponse::FieldModified(node)) => {
self.cache
.invalidate_outputs(&self.state.graph, *node);
self.cache.invalidate_outputs(&self.state.graph, *node);
self.cache.invalidate_last_state();
self.cached_last_value = None;
},
@ -1912,7 +1940,8 @@ impl eframe::App for AppHandle {
if let Some(l_v) = &self.cached_last_value {
content = l_v.clone();
} else {
#[cfg(not(target_arch = "wasm32"))] {
#[cfg(not(target_arch = "wasm32"))]
{
// wasm does not support threads :-(
// ---------------------------------------------------------
// did we already start a thread?
@ -1929,41 +1958,60 @@ impl eframe::App for AppHandle {
graph,
&cache,
arc_translator,
&ctx
&ctx,
)
})
};
self.app_logic_thread = Some(thread_join_handle);
}
if self.app_logic_thread.as_ref()
if self
.app_logic_thread
.as_ref()
.map(|handle| handle.is_finished())
.unwrap_or(false)
{
let handle = std::mem::take(&mut self.app_logic_thread);
let err = handle.unwrap().join()
let err = handle
.unwrap()
.join()
.expect("Could not join thread");
if let Err(e) = err {
let text = get_layout(Err(e), &self.translator.lock().unwrap(), ctx);
let text = get_layout(
Err(e),
&self.translator.lock().unwrap(),
ctx,
);
self.cached_last_value = Some(text);
} else if let Some(l_b_v) = self.cache.get_last_state() {
if let BasicValue::SaveBytes { path, value } = &l_b_v {
} else if let Some(l_b_v) = self.cache.get_last_state()
{
if let BasicValue::SaveBytes { path, value } =
&l_b_v
{
use std::io::Write;
let mut f = match std::fs::File::create(path) {
Ok(f) => f,
Err(e) => {
println!("Error creating file {path}: {e}");
| Ok(f) => f,
| Err(e) => {
println!(
"Error creating file {path}: {e}"
);
return;
}
},
};
if let Err(e) = f.write_all(value) {
println!("Error writing to file {path}: {e}");
println!(
"Error writing to file {path}: {e}"
);
return;
}
}
content = get_layout(Ok(l_b_v), &self.translator.lock().unwrap(), ctx);
content = get_layout(
Ok(l_b_v),
&self.translator.lock().unwrap(),
ctx,
);
self.cached_last_value = Some(content.clone());
}
} else {
@ -1971,33 +2019,42 @@ impl eframe::App for AppHandle {
}
}
#[cfg(target_arch = "wasm32")] {
#[cfg(target_arch = "wasm32")]
{
let err = create_output(
Arc::clone(&self.user_state),
self.state.graph.clone(),
&self.cache,
Arc::clone(&self.translator),
&ctx
&ctx,
);
if let Err(e) = err {
let text = get_layout(Err(e), &self.translator.lock().unwrap(), ctx);
let text = get_layout(
Err(e),
&self.translator.lock().unwrap(),
ctx,
);
self.cached_last_value = Some(text);
} else if let Some(l_b_v) = self.cache.get_last_state() {
if let BasicValue::SaveBytes { path, value } = &l_b_v {
use std::io::Write;
let mut f = match std::fs::File::create(path) {
Ok(f) => f,
Err(e) => {
| Ok(f) => f,
| Err(e) => {
println!("Error creating file {path}: {e}");
return;
}
},
};
if let Err(e) = f.write_all(value) {
println!("Error writing to file {path}: {e}");
return;
}
}
content = get_layout(Ok(l_b_v), &self.translator.lock().unwrap(), ctx);
content = get_layout(
Ok(l_b_v),
&self.translator.lock().unwrap(),
ctx,
);
self.cached_last_value = Some(content.clone());
}
spin = false;
@ -2035,7 +2092,7 @@ fn create_output(
graph: Graph<NodeData, BasicDataType, BasicValue>,
cache: &OutputsCache,
translator: Arc<Mutex<rsprocess::translator::Translator>>,
ctx: &egui::Context
ctx: &egui::Context,
) -> anyhow::Result<()> {
let (save_node, active_node) = {
let user_state = user_state.read().unwrap();
@ -2092,17 +2149,16 @@ impl egui::Widget for WidgetLayout {
match self {
| Self::LayoutJob(lj) => {
let mut response = None;
egui::ScrollArea::vertical().auto_shrink([false, false]).show(ui, |ui| {
response = Some(egui::Label::new(lj).ui(ui));
});
egui::ScrollArea::vertical()
.auto_shrink([false, false])
.show(ui, |ui| {
response = Some(egui::Label::new(lj).ui(ui));
});
response.unwrap()
},
| Self::Empty => {
egui::Label::new("").ui(ui)
},
| Self::Image(i) => {
egui::Image::new(&i).max_size(ui.available_size()).ui(ui)
}
| Self::Empty => egui::Label::new("").ui(ui),
| Self::Image(i) =>
egui::Image::new(&i).max_size(ui.available_size()).ui(ui),
}
}
}
@ -2118,7 +2174,8 @@ fn get_layout(
| Ok(value) => match value {
| BasicValue::SaveBytes { path, value: _ } => text.append(
&format!("Saving to file \"{}\"", path),
0., Default::default(),
0.,
Default::default(),
),
| BasicValue::Error { value } => {
text = value;
@ -2130,7 +2187,8 @@ fn get_layout(
text.append(&value, 0., Default::default()),
| BasicValue::System { value } => text.append(
&format!("{}", Formatter::from(translator, &value)),
0., Default::default(),
0.,
Default::default(),
),
| BasicValue::PositiveInt { value } =>
text.append(&format!("{value}"), 0., Default::default()),

View File

@ -55,7 +55,7 @@ pub fn evaluate_node(
| None => {},
| Some(val) => {
outputs_cache.set_last_state(val);
return Ok(())
return Ok(());
},
}
}
@ -109,21 +109,20 @@ fn generate_to_evaluate(
let mut input_hashes = vec![];
match graph[n_id].user_data.template {
NodeInstruction::SaveString | NodeInstruction::SaveSvg => {
| NodeInstruction::SaveString | NodeInstruction::SaveSvg => {
res.push(n_id);
invalid_ids.insert(n_id);
outputs_cache.invalidate_outputs(graph, n_id);
continue;
},
_ => {}
| _ => {},
}
let first_output =
if let Some(o) = graph[n_id].output_ids().next() {
o
} else {
continue;
};
let first_output = if let Some(o) = graph[n_id].output_ids().next() {
o
} else {
continue;
};
let hashes =
if let Some(hashes) = outputs_cache.input_hashes(&first_output) {
hashes
@ -442,7 +441,10 @@ fn process_template(
value: format!(
"After {} steps arrived at state {}",
limit.0,
Formatter::from(&translator.lock().unwrap(), &limit.1)
Formatter::from(
&translator.lock().unwrap(),
&limit.1
)
),
};
set_cache_output!((
@ -694,12 +696,14 @@ fn process_template(
BasicValue::AssertFunction { value: grouping },
) => {
use execution::data::MapEdges;
let graph_1 = match graph_1.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_1 = match graph_1
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_2 = match graph_2
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
@ -762,12 +766,14 @@ fn process_template(
BasicValue::AssertFunction { value: grouping },
) => {
use execution::data::MapEdges;
let graph_1 = match graph_1.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_1 = match graph_1
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_2 = match graph_2
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
@ -797,12 +803,14 @@ fn process_template(
BasicValue::AssertFunction { value: grouping },
) => {
use execution::data::MapEdges;
let graph_1 = match graph_1.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_1 = match graph_1
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_2 = match graph_2
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
@ -851,7 +859,10 @@ fn process_template(
BasicValue::Path { value: path },
BasicValue::String { value },
) => {
*to_ret = Some(BasicValue::SaveBytes { path, value: value.into() });
*to_ret = Some(BasicValue::SaveBytes {
path,
value: value.into(),
});
},
| (BasicValue::Path { .. }, _) => {
anyhow::bail!("Not a string");
@ -899,13 +910,18 @@ fn process_template(
BasicValue::ColorNode { value: color_node },
BasicValue::ColorEdge { value: color_edge },
) => {
let current_translator: rsprocess::translator::Translator = translator.lock().unwrap().clone();
let current_translator: rsprocess::translator::Translator =
translator.lock().unwrap().clone();
let arc_translator = Arc::new(current_translator);
let modified_graph = input_graph.map(
display_node
.generate(Arc::clone(&arc_translator), &input_graph),
display_edge
.generate(Arc::clone(&arc_translator), &input_graph),
display_node.generate(
Arc::clone(&arc_translator),
&input_graph,
),
display_edge.generate(
Arc::clone(&arc_translator),
&input_graph,
),
);
let input_graph = Arc::new(input_graph.to_owned());
@ -1076,8 +1092,10 @@ fn process_template(
let current_translator = translator.lock().unwrap().clone();
let arc_translator = Arc::new(current_translator);
let modified_graph = input_graph.map(
display_node
.generate(Arc::clone(&arc_translator), &input_graph),
display_node.generate(
Arc::clone(&arc_translator),
&input_graph,
),
display_edge.generate(arc_translator, &input_graph),
);
@ -1303,7 +1321,10 @@ fn process_template(
value: format!(
"After {} steps arrived at state {}",
limit.0,
Formatter::from(&translator.lock().unwrap(), &limit.1)
Formatter::from(
&translator.lock().unwrap(),
&limit.1
)
),
};
set_cache_output!((
@ -1788,7 +1809,10 @@ fn process_template(
if let BasicValue::Trace { value } = trace {
let res = BasicValue::String {
value: format!("{}", Formatter::from(&translator.lock().unwrap(), &value)),
value: format!(
"{}",
Formatter::from(&translator.lock().unwrap(), &value)
),
};
set_cache_output!((
output_names.first().unwrap(),
@ -1805,7 +1829,10 @@ fn process_template(
if let BasicValue::PositiveTrace { value } = trace {
let res = BasicValue::String {
value: format!("{}", Formatter::from(&translator.lock().unwrap(), &value)),
value: format!(
"{}",
Formatter::from(&translator.lock().unwrap(), &value)
),
};
set_cache_output!((
output_names.first().unwrap(),
@ -2124,7 +2151,11 @@ fn process_template(
) => {
use execution::data;
let mut graph = g.clone();
match data::grouping(&mut graph, &grouping, &mut translator.lock().unwrap()) {
match data::grouping(
&mut graph,
&grouping,
&mut translator.lock().unwrap(),
) {
| Ok(_) => {},
| Err(e) => anyhow::bail!(e),
};
@ -2223,7 +2254,9 @@ fn process_template(
use execution::data;
let mut graph = g.clone();
match data::positive_grouping(
&mut graph, &grouping, &mut translator.lock().unwrap(),
&mut graph,
&grouping,
&mut translator.lock().unwrap(),
) {
| Ok(_) => {},
| Err(e) => anyhow::bail!(e),
@ -2254,12 +2287,14 @@ fn process_template(
BasicValue::PositiveAssertFunction { value: grouping },
) => {
use execution::data::PositiveMapEdges;
let graph_1 = match graph_1.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_1 = match graph_1
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_2 = match graph_2
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
@ -2289,12 +2324,14 @@ fn process_template(
BasicValue::PositiveAssertFunction { value: grouping },
) => {
use execution::data::PositiveMapEdges;
let graph_1 = match graph_1.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_1 = match graph_1
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_2 = match graph_2
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
@ -2324,12 +2361,14 @@ fn process_template(
BasicValue::PositiveAssertFunction { value: grouping },
) => {
use execution::data::PositiveMapEdges;
let graph_1 = match graph_1.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_1 = match graph_1
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
};
let graph_2 = match graph_2.map_edges(&grouping, &mut translator.lock().unwrap())
let graph_2 = match graph_2
.map_edges(&grouping, &mut translator.lock().unwrap())
{
| Ok(g) => g,
| Err(e) => anyhow::bail!(e),
@ -2476,12 +2515,16 @@ fn process_template(
}
},
| NodeInstruction::Sleep => {
#[cfg(not(target_arch = "wasm32"))] {
#[cfg(not(target_arch = "wasm32"))]
{
let input_seconds = retrieve_from_cache![1];
let hash_inputs = hash_inputs!(input_seconds);
if let BasicValue::PositiveInt { value: _value } = input_seconds {
std::thread::sleep(std::time::Duration::from_secs(_value as u64));
if let BasicValue::PositiveInt { value: _value } = input_seconds
{
std::thread::sleep(std::time::Duration::from_secs(
_value as u64,
));
set_cache_output!((
output_names.first().unwrap(),
@ -2492,7 +2535,8 @@ fn process_template(
anyhow::bail!("Not an integer");
}
}
#[cfg(target_arch = "wasm32")] {
#[cfg(target_arch = "wasm32")]
{
anyhow::bail!("Cannot sleep on wams");
}
},
@ -2502,8 +2546,8 @@ fn process_template(
if let BasicValue::String { value } = s {
let res = match super::svg::Svg::parse_dot_string(&value) {
Ok(svg) => svg,
Err(e) => anyhow::bail!(e),
| Ok(svg) => svg,
| Err(e) => anyhow::bail!(e),
};
let res = BasicValue::Svg { value: res };
@ -2529,8 +2573,8 @@ fn process_template(
path.push_str(".png");
}
let svg = match value.rasterize() {
Ok(svg) => svg,
Err(e) => anyhow::bail!(e),
| Ok(svg) => svg,
| Err(e) => anyhow::bail!(e),
};
*to_ret = Some(BasicValue::SaveBytes { path, value: svg });
},
@ -2544,7 +2588,7 @@ fn process_template(
anyhow::bail!("Values of wrong type");
},
}
}
},
}
Ok(None)
}

View File

@ -1,7 +1,10 @@
use std::{fmt::Debug, hash::Hash, sync::{Arc, Mutex}};
use std::fmt::Debug;
use std::hash::Hash;
use std::sync::{Arc, Mutex};
use layout::{backends::svg::SVGWriter, gv::{self, GraphBuilder}};
use eframe::egui;
use layout::backends::svg::SVGWriter;
use layout::gv::{self, GraphBuilder};
#[cfg_attr(
feature = "persistence",
@ -9,7 +12,7 @@ use eframe::egui;
)]
#[derive(Clone, Default)]
pub(crate) struct Svg {
image: egui::ColorImage,
image: egui::ColorImage,
/// original size of the svg
svg_size: egui::Vec2,
@ -26,9 +29,9 @@ impl Svg {
let mut parser = gv::DotParser::new(dot_str);
let g = match parser.process() {
Ok(g) => g,
Err(_) =>
// errors are printed to sdtout so we ignore them
| Ok(g) => g,
| Err(_) =>
// errors are printed to sdtout so we ignore them
return Err("Could not parse dot string.".into()),
};
@ -36,53 +39,58 @@ impl Svg {
gb.visit_graph(&g);
let mut graph = gb.get();
let mut svg = SVGWriter::new();
graph.do_it(
false,
false,
false,
&mut svg,
);
graph.do_it(false, false, false, &mut svg);
let content = svg.finalize();
let svg_tree = match resvg::usvg::Tree::from_str(
&content,
&resvg::usvg::Options {
let svg_tree =
match resvg::usvg::Tree::from_str(&content, &resvg::usvg::Options {
dpi: 92.,
font_family: "Andale Mono".into(),
fontdb: Arc::new(fontdb),
..Default::default()
}
) {
Ok(svg) => svg,
Err(err) => return Err(format!("{}", err)),
};
}) {
| Ok(svg) => svg,
| Err(err) => return Err(format!("{}", err)),
};
let svg_size = egui::vec2(svg_tree.size().width(), svg_tree.size().height());
let svg_size =
egui::vec2(svg_tree.size().width(), svg_tree.size().height());
let mut pixmap = resvg::tiny_skia::Pixmap::new(svg_size.x as _, svg_size.y as _)
.expect("Could not allocate svg");
let mut pixmap =
resvg::tiny_skia::Pixmap::new(svg_size.x as _, svg_size.y as _)
.expect("Could not allocate svg");
let pixmap_mut = &mut pixmap.as_mut();
resvg::render(&svg_tree, Default::default(), pixmap_mut);
let pixmap = pixmap_mut.to_owned();
let image = egui::ColorImage::from_rgba_unmultiplied([pixmap.width() as _, pixmap.height() as _], pixmap.data());
let image = egui::ColorImage::from_rgba_unmultiplied(
[pixmap.width() as _, pixmap.height() as _],
pixmap.data(),
);
let svg = Svg { image,
original: content,
svg_size,
svg_texture: Arc::new(Mutex::new(None)) };
let svg = Svg {
image,
original: content,
svg_size,
svg_texture: Arc::new(Mutex::new(None)),
};
Ok(svg)
}
pub(crate) fn get_texture(&self, ctx: &egui::Context) -> egui::TextureHandle {
pub(crate) fn get_texture(
&self,
ctx: &egui::Context,
) -> egui::TextureHandle {
let tx = self.svg_texture.lock().expect("Poisoned");
if tx.is_some() {
(*tx).clone().unwrap()
} else {
std::mem::drop(tx);
let svg_texture = ctx.load_texture("svg", self.image.clone(), Default::default());
*self.svg_texture.lock().expect("Poisoned") = Some(svg_texture.clone());
let svg_texture =
ctx.load_texture("svg", self.image.clone(), Default::default());
*self.svg_texture.lock().expect("Poisoned") =
Some(svg_texture.clone());
svg_texture
}
}
@ -94,34 +102,39 @@ impl Svg {
dpi: 92.,
font_family: "Andale Mono".into(),
..Default::default()
}
},
) {
Ok(svg) => svg,
Err(err) => return Err(format!("{}", err)),
| Ok(svg) => svg,
| Err(err) => return Err(format!("{}", err)),
};
let mut pixmap = resvg::tiny_skia::Pixmap::new(self.svg_size.x as _, self.svg_size.y as _)
.expect("Could not allocate svg");
let mut pixmap = resvg::tiny_skia::Pixmap::new(
self.svg_size.x as _,
self.svg_size.y as _,
)
.expect("Could not allocate svg");
let pixmap_mut = &mut pixmap.as_mut();
resvg::render(&svg_tree, Default::default(), pixmap_mut);
let pixmap = pixmap_mut.to_owned();
match pixmap.encode_png() {
Ok(png) => Ok(png),
Err(e) => Err(format!("{}", e)),
| Ok(png) => Ok(png),
| Err(e) => Err(format!("{}", e)),
}
}
}
impl Debug for Svg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[image: {:?}, svg_size: {:?}, svg_texture: {}",
self.image,
self.svg_size,
if self.svg_texture.lock().expect("Poisoned").is_some() {
"Some(...)"
} else {
"None"
}
write!(
f,
"[image: {:?}, svg_size: {:?}, svg_texture: {}",
self.image,
self.svg_size,
if self.svg_texture.lock().expect("Poisoned").is_some() {
"Some(...)"
} else {
"None"
}
)
}
}