Skip to content

Commit

Permalink
Big image export refactor
Browse files Browse the repository at this point in the history
* Unify actions
* Enable image export for 0D and 1D diagrams
* Enable multiple image export for other formats
* Use the geometry samples and subdivision depth settings in STL
  • Loading branch information
calintat committed Jun 14, 2024
1 parent a0f6074 commit bc1f724
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 152 deletions.
36 changes: 29 additions & 7 deletions homotopy-graphics/src/manim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use homotopy_core::{
};
use itertools::Itertools;
use lyon_path::{Event, Path};
use serde::Serialize;

use crate::{
path_util::simplify_graphic,
Expand All @@ -21,6 +22,12 @@ use crate::{

const INDENT: &str = " ";

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize)]
pub struct ManimOptions {
/// Whether to use the OpenGL renderer.
pub use_opengl: bool,
}

pub fn stylesheet(styles: &impl SignatureStyleData) -> String {
let mut stylesheet = String::new();

Expand Down Expand Up @@ -77,15 +84,30 @@ fn name(generator: Generator, c: usize, orientation: Orientation) -> String {
}

pub fn render(
diagram: &Diagram,
dimension: u8,
signature_styles: &impl SignatureStyleData,
stylesheet: &str,
options: ManimOptions,
) -> Result<String, DimensionError> {
match dimension {
0 => render_generic::<0>(diagram, signature_styles, stylesheet, options),
1 => render_generic::<1>(diagram, signature_styles, stylesheet, options),
2 => render_generic::<2>(diagram, signature_styles, stylesheet, options),
_ => Err(DimensionError),
}
}

fn render_generic<const N: usize>(
diagram: &Diagram,
signature_styles: &impl SignatureStyleData,
stylesheet: &str,
use_opengl: bool,
options: ManimOptions,
) -> Result<String, DimensionError> {
let layout = Layout::<2>::new(diagram)?;
let layout = Layout::<N>::new(diagram)?;
let complex = make_complex(diagram);
let depths = Depths::<2>::new(diagram)?;
let projection = Projection::<2>::new(diagram, &layout, &depths)?;
let depths = Depths::<N>::new(diagram)?;
let projection = Projection::<N>::new(diagram, &layout, &depths)?;
let graphic = simplify_graphic(&GraphicElement::build(
&complex,
&layout,
Expand Down Expand Up @@ -117,7 +139,7 @@ pub fn render(
}

let mut manim = String::new();
if use_opengl {
if options.use_opengl {
manim.push_str(
"# Render with 'manim --format mp4 --renderer=opengl homotopy_io_export.py'\n",
);
Expand All @@ -128,7 +150,7 @@ pub fn render(
}
manim.push_str("import numpy as np\n");
manim.push_str("from manim import *\n");
if use_opengl {
if options.use_opengl {
manim.push_str(
"from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject\n",
);
Expand All @@ -149,7 +171,7 @@ pub fn render(
)
.unwrap();

let vmobj = if use_opengl {
let vmobj = if options.use_opengl {
"OpenGLVMobject"
} else {
"VMobject"
Expand Down
12 changes: 10 additions & 2 deletions homotopy-graphics/src/stl.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
use std::fmt::Write;

use homotopy_core::{common::DimensionError, Diagram};
use serde::Serialize;

use crate::{
geom::{CubicalGeometry, SimplicialGeometry},
style::SignatureStyleData,
};

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize)]
pub struct StlOptions {
pub geometry_samples: u8,
pub subdivision_depth: u8,
}

pub fn render(
diagram: &Diagram,
signature_styles: &impl SignatureStyleData,
options: StlOptions,
) -> Result<String, DimensionError> {
let mut output = String::new();

let mut cubical = CubicalGeometry::new::<3>(diagram, false)?;
cubical.subdivide(false, 3);
cubical.subdivide(false, options.subdivision_depth);

let mut simplicial = SimplicialGeometry::from(cubical);
simplicial.inflate_3d(3, signature_styles);
simplicial.inflate_3d(options.geometry_samples, signature_styles);

writeln!(output, "solid assoc").unwrap();

Expand Down
80 changes: 48 additions & 32 deletions homotopy-graphics/src/tikz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@ use homotopy_core::{
};
use itertools::Itertools;
use lyon_path::{Event, Path};
use serde::Serialize;

use crate::{
path_util::simplify_graphic,
style::{Color, GeneratorRepresentation, GeneratorStyle, SignatureStyleData, VertexShape},
svg::render::GraphicElement,
};

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize)]
pub struct TikzOptions {
/// Whether to render the diagrams from left to right.
pub left_to_right: bool,

/// Whether to use masking to render braidings.
pub show_braidings: bool,
}

#[allow(clippy::implicit_hasher)]
pub fn stylesheet(
styles: &impl SignatureStyleData,
Expand Down Expand Up @@ -92,16 +102,29 @@ fn rgb(color: Color) -> String {
}

pub fn render(
diagram: &Diagram,
dimension: u8,
signature_styles: &impl SignatureStyleData,
options: TikzOptions,
) -> Result<String, DimensionError> {
match dimension {
0 => render_generic::<0>(diagram, signature_styles, options),
1 => render_generic::<1>(diagram, signature_styles, options),
2 => render_generic::<2>(diagram, signature_styles, options),
_ => Err(DimensionError),
}
}

fn render_generic<const N: usize>(
diagram: &Diagram,
signature_styles: &impl SignatureStyleData,
leftright_mode: bool,
show_braids: bool,
options: TikzOptions,
) -> Result<String, DimensionError> {
let dimension = diagram.dimension();
let layout = Layout::<2>::new(diagram)?;
let layout = Layout::<N>::new(diagram)?;
let complex = make_complex(diagram);
let depths = Depths::<2>::new(diagram)?;
let projection = Projection::<2>::new(diagram, &layout, &depths)?;
let depths = Depths::<N>::new(diagram)?;
let projection = Projection::<N>::new(diagram, &layout, &depths)?;
let graphic = simplify_graphic(&GraphicElement::build(
&complex,
&layout,
Expand Down Expand Up @@ -134,20 +157,14 @@ pub fn render(
writeln!(tikz, "\\begin{{tikzpicture}}").unwrap();
tikz.push_str(&stylesheet(signature_styles, dimension, &diagrams));

tikz.push_str(&render_inner(
&surfaces,
wires,
leftright_mode,
show_braids,
dimension,
));
tikz.push_str(&render_inner(&surfaces, wires, options, dimension));

// Points are unchanged
for (d, point) in points {
let vertex = render_vertex(
signature_styles.generator_style(d.generator).unwrap(),
point,
leftright_mode,
options,
);
writeln!(
tikz,
Expand Down Expand Up @@ -179,13 +196,12 @@ const MAGIC_MACRO: &str = "\n\\newcommand{\\wire}[2]{
fn render_inner(
surfaces: &[(Diagram0, Path)],
wires: FastHashMap<usize, Vec<(Diagram0, Path)>>,
leftright_mode: bool,
show_braids: bool,
options: TikzOptions,
diagram_dimension: usize,
) -> String {
let mut tikz = String::new();

let needs_masking = wires.len() > 1 && show_braids;
let needs_masking = wires.len() > 1 && options.show_braidings;

if needs_masking {
tikz.push_str(MAGIC_MACRO);
Expand All @@ -205,7 +221,7 @@ fn render_inner(
tikz,
"\\fill[{color}] {path};",
color = name_from_diagram_dim(*g, diagram_dimension, GeneratorRepresentation::Surface),
path = &render_path(path, leftright_mode)
path = &render_path(path, options)
)
.unwrap();
}
Expand All @@ -220,7 +236,7 @@ fn render_inner(
" \\clipped{{{color}}}{{#1}}{{{path}}}",
color =
name_from_diagram_dim(*g, diagram_dimension, GeneratorRepresentation::Surface),
path = &render_path(path, leftright_mode)
path = &render_path(path, options)
)
.unwrap();
}
Expand Down Expand Up @@ -256,7 +272,7 @@ fn render_inner(
"\\wire{{{color}}}{{{path}}};",
color =
name_from_diagram_dim(*g, diagram_dimension, GeneratorRepresentation::Wire),
path = &render_path(path, leftright_mode)
path = &render_path(path, options)
)
.unwrap();
} else {
Expand All @@ -265,7 +281,7 @@ fn render_inner(
"\\draw[color={color}, line width=5pt]{path};",
color =
name_from_diagram_dim(*g, diagram_dimension, GeneratorRepresentation::Wire),
path = &render_path(path, leftright_mode)
path = &render_path(path, options)
)
.unwrap();
}
Expand All @@ -280,10 +296,10 @@ fn render_inner(
tikz
}

fn render_point(point: Point2D<f32>, leftright_mode: bool) -> String {
fn render_point(point: Point2D<f32>, options: TikzOptions) -> String {
let x = (point.x * 100.0).round() / 100.0;
let y = (point.y * 100.0).round() / 100.0;
if leftright_mode {
if options.left_to_right {
format!("({y},{})", -x)
} else {
format!("({x},{y})")
Expand All @@ -293,7 +309,7 @@ fn render_point(point: Point2D<f32>, leftright_mode: bool) -> String {
fn render_vertex(
generator_style: &impl GeneratorStyle,
point: Point2D<f32>,
leftright_mode: bool,
options: TikzOptions,
) -> String {
use VertexShape::{Circle, Square};

Expand All @@ -320,36 +336,36 @@ fn render_vertex(
.collect::<Vec<String>>()
.join(", ");

if leftright_mode {
if options.left_to_right {
format!("({y1},{}) {shape_str} ({sz});", -x1)
} else {
format!("({x1},{y1}) {shape_str} ({sz});")
}
}

fn render_path(path: &Path, leftright_mode: bool) -> String {
fn render_path(path: &Path, options: TikzOptions) -> String {
let mut result = String::new();
for event in path {
match event {
Event::Begin { at } => result.push_str(&render_point(at, leftright_mode)),
Event::Begin { at } => result.push_str(&render_point(at, options)),
Event::Line { to, .. } => {
write!(result, " -- {}", render_point(to, leftright_mode)).unwrap();
write!(result, " -- {}", render_point(to, options)).unwrap();
}
Event::Quadratic { ctrl, to, .. } => write!(
result,
" .. controls {} .. {}",
render_point(ctrl, leftright_mode),
render_point(to, leftright_mode)
render_point(ctrl, options),
render_point(to, options)
)
.unwrap(),
Event::Cubic {
ctrl1, ctrl2, to, ..
} => write!(
result,
" .. controls {} and {} .. {}",
render_point(ctrl1, leftright_mode),
render_point(ctrl2, leftright_mode),
render_point(to, leftright_mode),
render_point(ctrl1, options),
render_point(ctrl2, options),
render_point(to, options),
)
.unwrap(),
Event::End { close, .. } => {
Expand Down
Loading

0 comments on commit bc1f724

Please sign in to comment.