summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlittleblack111 <littleblack11111@gmail.com>2025-10-01 23:20:02 +0800
committerlittleblack111 <littleblack11111@gmail.com>2025-10-08 16:30:13 +0800
commitcff4074fb49ef7543fe157a991087d6b24839991 (patch)
treed4679c5d254747e25dd95ec22b1bd4641ded975e
parent33bb133620177883b63f045444a471e4a4d56413 (diff)
refactor(image): move into struct
-rw-r--r--src/image.rs473
-rw-r--r--src/lib.rs1
2 files changed, 237 insertions, 237 deletions
diff --git a/src/image.rs b/src/image.rs
index 4cd18fc..6a69bc4 100644
--- a/src/image.rs
+++ b/src/image.rs
@@ -6,54 +6,49 @@ use imageproc::{
point::Point,
rect::Rect,
};
-use std::{
- fs, io,
- path::Path,
- sync::{Mutex, MutexGuard, OnceLock},
-};
+use std::{fs, io, path::Path};
use crate::ast::DepthExpr;
-type Offset = (u32, u32, u32); // x, y or width, height center
-type NCOffset = (u32, u32);
-
-// TODO: based on how much text
-const COMPONENT_TEXT_PADDING: u32 = 1 * RESOLUTION_MULTIPLIER;
+/*
+offset based mutation model, to avoid overflowing on previous image
+*/
-const TEXT_SCALE: f32 = 12.0 * RESOLUTION_MULTIPLIER as f32;
-const TEXT_LEN_WRAP: usize = 10;
+struct FlowChart {
+ img: DynamicImage,
+ offset: Self::Offset,
+ font: FontArc,
+}
-// TODO: LENGTH based on where to where
-const DIRECTION_LINE_THICKNESS: u32 = 2 * RESOLUTION_MULTIPLIER / 2;
-const DIRECTION_LINE_LENGTH: u32 = 13 * RESOLUTION_MULTIPLIER;
-const DIRECTION_LINE_ARROW_OFFSET: u32 = 5 * RESOLUTION_MULTIPLIER / 2;
+impl FlowChart {
+ type Offset = (u32, u32, u32); // x, y or width, height center
+ type NCOffset = (u32, u32);
+ // TODO: based on how much text
+ const COMPONENT_TEXT_PADDING: u32 = 1 * Self::RESOLUTION_MULTIPLIER;
-const RESOLUTION_MULTIPLIER: u32 = 5;
+ const TEXT_SCALE: f32 = 12.0 * Self::RESOLUTION_MULTIPLIER as f32;
+ const TEXT_LEN_WRAP: usize = 10;
-static IMAGE: OnceLock<Mutex<DynamicImage>> = OnceLock::new();
-static OFFSET: OnceLock<Mutex<Offset>> = OnceLock::new();
-static FONT: OnceLock<FontArc> = OnceLock::new();
+ // TODO: LENGTH based on where to where
+ const DIRECTION_LINE_THICKNESS: u32 = 2 * Self::RESOLUTION_MULTIPLIER / 2;
+ const DIRECTION_LINE_LENGTH: u32 = 13 * Self::RESOLUTION_MULTIPLIER;
+ const DIRECTION_LINE_ARROW_OFFSET: u32 = 5 * Self::RESOLUTION_MULTIPLIER / 2;
-fn get_image() -> MutexGuard<'static, DynamicImage> {
- IMAGE
- .get_or_init(|| Mutex::new(DynamicImage::new(0, 0, ColorType::Rgba8)))
- .lock()
- .unwrap()
-}
+ const RESOLUTION_MULTIPLIER: u32 = 5;
-fn get_offset() -> MutexGuard<'static, Offset> {
- OFFSET
- .get_or_init(|| Mutex::new((0, 0, 0)))
- .lock()
- .unwrap()
-}
+ fn new() -> Self {
+ Self {
+ img: DynamicImage::new(0, 0, ColorType::Rgba8),
+ offset: (0, 0, 0),
+ font: Self::get_system_font(),
+ }
+ }
-fn get_font() -> &'static FontArc {
- FONT.get_or_init(|| {
+ fn get_system_font() -> FontArc {
let mut db = Database::new();
db.load_system_fonts();
- let font_result = match &db
+ match &db
.face(
db.query(&Query {
families: &[Family::SansSerif],
@@ -81,14 +76,210 @@ fn get_font() -> &'static FontArc {
)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "parse font failed")),
_ => Err(io::Error::new(io::ErrorKind::Unsupported, "unsupported font source")),
+ }
+ .expect("failed to load font")
+ }
+ fn wrap_str(s: &str) -> Vec<String> {
+ s.chars()
+ .collect::<Vec<_>>()
+ .chunks(Self::TEXT_LEN_WRAP)
+ .map(|c| {
+ c.iter()
+ .collect()
+ })
+ .collect()
+ }
+
+ fn ext(&mut self, (curw, curh): &mut Self::NCOffset, (extw, exth): &mut Self::NCOffset) {
+ let mut img = &mut self.img;
+ let (w, h) = img.dimensions();
+ let extedw = *extw + *curw;
+ let extedh = *exth + *curh;
+ if extedw <= w && extedh <= h {
+ return;
+ }
+ let mut resized = ImageBuffer::from_pixel(w.max(extedw), h.max(extedh), colors::BG);
+ imageops::overlay(&mut resized, img, 0, 0);
+ *img = image::DynamicImage::ImageRgba8(resized);
+ }
+
+ // TODO: make draw_rect func
+ // TODO: Accept all directions
+ fn draw_process(&mut self, txt: &str, config: ComponentConfig<HDirection, HDirection>) {
+ let curh = &mut self
+ .offset
+ .1;
+
+ let wrapped = Self::wrap_str(txt);
+
+ let pxscale = PxScale::from(Self::TEXT_SCALE / 2_f32);
+ let (text_w, _) = text_size(pxscale, &self.font, wrapped[0].as_str());
+ let w = text_w + (2 * Self::COMPONENT_TEXT_PADDING);
+ let h = Self::TEXT_SCALE as u32 / 2 * wrapped.len() as u32 + (2 * Self::COMPONENT_TEXT_PADDING); // text_size's height is weird and incorrect
+ if config.dst_direction == HDirection::Up {
+ *curh = curh
+ .checked_sub(h)
+ .unwrap_or(0);
+ }
+ drop(curh);
+ let (_, curh, c) = self.offset;
+ self.ext(&mut (c, curh), &mut (w, h));
+ let (curw, curh, c) = &mut self.offset;
+ let img = &mut self.img;
+ let cw = c
+ .checked_sub(w / 2)
+ .unwrap_or(0);
+ draw_filled_rect_mut(img, Rect::at(cw as i32, *curh as i32).of_size(w, h), colors::PROCESS);
+ // TODO: move to draw_text()
+ for s in wrapped {
+ draw_text_mut(img, colors::FG, cw as i32 + Self::COMPONENT_TEXT_PADDING as i32, *curh as i32 + Self::COMPONENT_TEXT_PADDING as i32, pxscale, &self.font, s.as_str());
+ *curh += Self::TEXT_SCALE as u32 / 2;
+ }
+ if *c == 0 {
+ *c = *curw + (w / 2);
+ }
+ *curh += 2 * Self::COMPONENT_TEXT_PADDING;
+ }
+
+ fn draw_direction(&mut self, dst: Option<&Self::NCOffset>) {
+ let (_, orih, c) = self.offset;
+
+ let srcx = c as i32;
+ let srcy = orih as i32;
+
+ // perpendicular(thickness)
+ let (dstx, dsty) = match dst {
+ Some(&(x, y)) => {
+ let dx = if x == 0 {
+ srcx
+ } else {
+ x as i32
+ };
+ (dx, y as i32)
+ }
+ None => (srcx, srcy + (Self::DIRECTION_LINE_LENGTH - Self::DIRECTION_LINE_ARROW_OFFSET) as i32),
};
- font_result.expect("failed to load font")
- })
-}
-/*
-offset based mutation model, to avoid overflowing on previous image
-*/
+ let xdiff = (dstx - srcx) as f32;
+ let ydiff = (dsty - srcy) as f32;
+ let length = (xdiff * xdiff + ydiff * ydiff)
+ .sqrt()
+ .max(1_f32);
+
+ let prepx = -ydiff / length;
+ let prepy = xdiff / length;
+
+ let linemaxx = (dstx as f32 - (xdiff / length) * Self::DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32;
+ let linemaxy = (dsty as f32 - (ydiff / length) * Self::DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32;
+
+ let line = [
+ Point::new((srcx as f32 - prepx * Self::DIRECTION_LINE_THICKNESS as f32).round() as i32, (srcy as f32 - prepy * Self::DIRECTION_LINE_THICKNESS as f32).round() as i32),
+ Point::new((srcx as f32 + prepx * Self::DIRECTION_LINE_THICKNESS as f32).round() as i32, (srcy as f32 + prepy * Self::DIRECTION_LINE_THICKNESS as f32).round() as i32),
+ Point::new((linemaxx as f32 + prepx * Self::DIRECTION_LINE_THICKNESS as f32).round() as i32, (linemaxy as f32 + prepy * Self::DIRECTION_LINE_THICKNESS as f32).round() as i32),
+ Point::new((linemaxx as f32 - prepx * Self::DIRECTION_LINE_THICKNESS as f32).round() as i32, (linemaxy as f32 - prepy * Self::DIRECTION_LINE_THICKNESS as f32).round() as i32),
+ ];
+
+ let xc = dstx as f32 - (xdiff / length) * Self::DIRECTION_LINE_ARROW_OFFSET as f32;
+ let yc = dsty as f32 - (ydiff / length) * Self::DIRECTION_LINE_ARROW_OFFSET as f32;
+
+ let left = Point::new((xc + prepx * Self::DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32, (yc + prepy * Self::DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32);
+ let right = Point::new((xc - prepx * Self::DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32, (yc - prepy * Self::DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32);
+ let tip = Point::new(dstx, dsty);
+ let center = Point::new(xc.round() as i32, yc.round() as i32);
+
+ let mut maxx = srcx.max(dstx);
+ let mut maxy = srcy.max(dsty);
+ for p in line
+ .iter()
+ .copied()
+ .chain([
+ tip, center, left, right,
+ ])
+ {
+ if p.x > maxx {
+ maxx = p.x;
+ }
+ if p.y > maxy {
+ maxy = p.y;
+ }
+ }
+ self.ext(&mut (c, orih), &mut (((maxx - c as i32).max(0) as u32), ((maxy - orih as i32).max(0) as u32)));
+ let img = &mut self.img;
+
+ draw_polygon_mut(img, &line, colors::DIRECT);
+ draw_polygon_mut(
+ img,
+ &[
+ tip, left, center,
+ ],
+ colors::DIRECT,
+ );
+ draw_polygon_mut(
+ img,
+ &[
+ tip, right, center,
+ ],
+ colors::DIRECT,
+ );
+
+ // TODO: move to struct, this look like shit
+ self.offset
+ .2 = dstx as u32;
+ self.offset
+ .1 = dsty as u32;
+ }
+
+ fn build(&mut self, ast: &[DepthExpr]) {
+ self.draw_process(
+ "abcdefghijklmnospa",
+ ComponentConfig {
+ ori_direction: HDirection::Down,
+ dst_direction: HDirection::Down,
+ },
+ );
+ self.draw_direction(None);
+ self.draw_process(
+ "ab",
+ ComponentConfig {
+ ori_direction: HDirection::Down,
+ dst_direction: HDirection::Down,
+ },
+ );
+ self.draw_direction(None);
+ self.draw_process(
+ "a",
+ ComponentConfig {
+ ori_direction: HDirection::Down,
+ dst_direction: HDirection::Down,
+ },
+ );
+ let (a, b, c) = self.offset;
+ self.draw_direction(None);
+ self.draw_process(
+ "a",
+ ComponentConfig {
+ ori_direction: HDirection::Down,
+ dst_direction: HDirection::Down,
+ },
+ );
+ self.draw_direction(None);
+ self.draw_process(
+ "a",
+ ComponentConfig {
+ ori_direction: HDirection::Down,
+ dst_direction: HDirection::Down,
+ },
+ );
+ self.draw_direction(Some(&(a + 100, b)));
+ self.draw_process(
+ "a",
+ ComponentConfig {
+ ori_direction: HDirection::Down,
+ dst_direction: HDirection::Up,
+ },
+ );
+ }
+}
enum VHDirection {
Horizontal(HDirection),
@@ -138,203 +329,11 @@ mod colors {
]);
}
-fn wrap_str(s: &str) -> Vec<String> {
- s.chars()
- .collect::<Vec<_>>()
- .chunks(TEXT_LEN_WRAP)
- .map(|c| {
- c.iter()
- .collect()
- })
- .collect()
-}
-
-fn ext((curw, curh): &mut NCOffset, (extw, exth): &mut NCOffset) {
- let img = &mut *get_image();
- let (w, h) = img.dimensions();
- let extedw = *extw + *curw;
- let extedh = *exth + *curh;
- if extedw <= w && extedh <= h {
- return;
- }
- let mut resized = ImageBuffer::from_pixel(w.max(extedw), h.max(extedh), colors::BG);
- imageops::overlay(&mut resized, img, 0, 0);
- *img = image::DynamicImage::ImageRgba8(resized);
-}
-
-// TODO: make draw_rect func
-// TODO: Accept all directions
-fn draw_process(txt: &str, config: ComponentConfig<HDirection, HDirection>) {
- let (curw, curh, c) = &mut *get_offset();
-
- let wrapped = wrap_str(txt);
-
- let pxscale = PxScale::from(TEXT_SCALE / 2_f32);
- let font = &get_font();
- let (text_w, _) = text_size(pxscale, font, wrapped[0].as_str());
- let w = text_w + (2 * COMPONENT_TEXT_PADDING);
- let h = TEXT_SCALE as u32 / 2 * wrapped.len() as u32 + (2 * COMPONENT_TEXT_PADDING); // text_size's height is weird and incorrect
- if config.dst_direction == HDirection::Up {
- *curh = curh
- .checked_sub(h)
- .unwrap_or(0);
- }
- ext(&mut (*c, *curh), &mut (w, h));
- let img = &mut *get_image();
- let cw = c
- .checked_sub(w / 2)
- .unwrap_or(0);
- draw_filled_rect_mut(img, Rect::at(cw as i32, *curh as i32).of_size(w, h), colors::PROCESS);
- // TODO: move to draw_text()
- for s in wrapped {
- draw_text_mut(img, colors::FG, cw as i32 + COMPONENT_TEXT_PADDING as i32, *curh as i32 + COMPONENT_TEXT_PADDING as i32, pxscale, font, s.as_str());
- *curh += TEXT_SCALE as u32 / 2;
- }
- if *c == 0 {
- *c = *curw + (w / 2);
- }
- *curh += 2 * COMPONENT_TEXT_PADDING;
-}
-
-fn draw_direction(dst: Option<&NCOffset>) {
- let (_, orih, c) = &mut *get_offset();
-
- let srcx = *c as i32;
- let srcy = *orih as i32;
-
- // perpendicular(thickness)
- let (dstx, dsty) = match dst {
- Some(&(x, y)) => {
- let dx = if x == 0 {
- srcx
- } else {
- x as i32
- };
- (dx, y as i32)
- }
- None => (srcx, srcy + (DIRECTION_LINE_LENGTH - DIRECTION_LINE_ARROW_OFFSET) as i32),
- };
-
- let xdiff = (dstx - srcx) as f32;
- let ydiff = (dsty - srcy) as f32;
- let length = (xdiff * xdiff + ydiff * ydiff)
- .sqrt()
- .max(1_f32);
-
- let prepx = -ydiff / length;
- let prepy = xdiff / length;
-
- let linemaxx = (dstx as f32 - (xdiff / length) * DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32;
- let linemaxy = (dsty as f32 - (ydiff / length) * DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32;
-
- let line = [
- Point::new((srcx as f32 - prepx * DIRECTION_LINE_THICKNESS as f32).round() as i32, (srcy as f32 - prepy * DIRECTION_LINE_THICKNESS as f32).round() as i32),
- Point::new((srcx as f32 + prepx * DIRECTION_LINE_THICKNESS as f32).round() as i32, (srcy as f32 + prepy * DIRECTION_LINE_THICKNESS as f32).round() as i32),
- Point::new((linemaxx as f32 + prepx * DIRECTION_LINE_THICKNESS as f32).round() as i32, (linemaxy as f32 + prepy * DIRECTION_LINE_THICKNESS as f32).round() as i32),
- Point::new((linemaxx as f32 - prepx * DIRECTION_LINE_THICKNESS as f32).round() as i32, (linemaxy as f32 - prepy * DIRECTION_LINE_THICKNESS as f32).round() as i32),
- ];
-
- let xc = dstx as f32 - (xdiff / length) * DIRECTION_LINE_ARROW_OFFSET as f32;
- let yc = dsty as f32 - (ydiff / length) * DIRECTION_LINE_ARROW_OFFSET as f32;
-
- let left = Point::new((xc + prepx * DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32, (yc + prepy * DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32);
- let right = Point::new((xc - prepx * DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32, (yc - prepy * DIRECTION_LINE_ARROW_OFFSET as f32).round() as i32);
- let tip = Point::new(dstx, dsty);
- let center = Point::new(xc.round() as i32, yc.round() as i32);
-
- let mut maxx = srcx.max(dstx);
- let mut maxy = srcy.max(dsty);
- for p in line
- .iter()
- .copied()
- .chain([
- tip, center, left, right,
- ])
- {
- if p.x > maxx {
- maxx = p.x;
- }
- if p.y > maxy {
- maxy = p.y;
- }
- }
- ext(&mut (*c, *orih), &mut (((maxx - *c as i32).max(0) as u32), ((maxy - *orih as i32).max(0) as u32)));
- let img = &mut *get_image();
-
- draw_polygon_mut(img, &line, colors::DIRECT);
- draw_polygon_mut(
- img,
- &[
- tip, left, center,
- ],
- colors::DIRECT,
- );
- draw_polygon_mut(
- img,
- &[
- tip, right, center,
- ],
- colors::DIRECT,
- );
-
- *c = dstx as u32;
- *orih = dsty as u32;
-}
-
-fn build(ast: &[DepthExpr]) {
- draw_process(
- "abcdefghijklmnospa",
- ComponentConfig {
- ori_direction: HDirection::Down,
- dst_direction: HDirection::Down,
- },
- );
- draw_direction(None);
- draw_process(
- "a",
- ComponentConfig {
- ori_direction: HDirection::Down,
- dst_direction: HDirection::Down,
- },
- );
- draw_direction(None);
- draw_process(
- "a",
- ComponentConfig {
- ori_direction: HDirection::Down,
- dst_direction: HDirection::Down,
- },
- );
- let (a, b, c) = *get_offset();
- draw_direction(None);
- draw_process(
- "a",
- ComponentConfig {
- ori_direction: HDirection::Down,
- dst_direction: HDirection::Down,
- },
- );
- draw_direction(None);
- draw_process(
- "a",
- ComponentConfig {
- ori_direction: HDirection::Down,
- dst_direction: HDirection::Down,
- },
- );
- draw_direction(Some(&(a + 100, b)));
- draw_process(
- "a",
- ComponentConfig {
- ori_direction: HDirection::Down,
- dst_direction: HDirection::Up,
- },
- );
-}
-
pub fn create(ast: &[DepthExpr], path: &Path) {
- build(ast);
- get_image()
+ let mut chart = FlowChart::new();
+ chart.build(ast);
+ chart
+ .img
.save(path)
.unwrap();
}
diff --git a/src/lib.rs b/src/lib.rs
index 05c7525..3103d56 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,4 @@
+#![feature(inherent_associated_types)]
pub mod ast;
pub mod image;
pub mod parser;