Allow Imaginate layers to work with rotation and skew (#806)

* Fix AI layer rotation

* Transform things by matricies

* Clean up size calculation

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube 2022-10-22 20:01:38 +01:00 committed by Keavon Chambers
parent b10d7a93b6
commit 05169935ad
2 changed files with 28 additions and 19 deletions

View File

@ -328,8 +328,9 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
} }
.unwrap_or_default(); .unwrap_or_default();
let size = bounds[1] - bounds[0]; let size = bounds[1] - bounds[0];
let transform = DAffine2::from_scale_angle_translation(1. / size, 0., -bounds[0]);
let document = self.render_document(bounds, persistent_data, DocumentRenderMode::Root); let document = self.render_document(size, transform, persistent_data, DocumentRenderMode::Root);
let file_suffix = &format!(".{file_type:?}").to_lowercase(); let file_suffix = &format!(".{file_type:?}").to_lowercase();
let name = match file_name.ends_with(FILE_SAVE_SUFFIX) { let name = match file_name.ends_with(FILE_SAVE_SUFFIX) {
@ -912,6 +913,7 @@ impl DocumentMessageHandler {
// Prepare the Imaginate parameters and base image // Prepare the Imaginate parameters and base image
let transform = self.graphene_document.root.transform.inverse() * self.graphene_document.multiply_transforms(&layer_path).unwrap();
let layer = self.graphene_document.layer(&layer_path).unwrap(); let layer = self.graphene_document.layer(&layer_path).unwrap();
let imaginate_layer = layer.as_imaginate().unwrap(); let imaginate_layer = layer.as_imaginate().unwrap();
@ -928,11 +930,10 @@ impl DocumentMessageHandler {
tiling: imaginate_layer.tiling, tiling: imaginate_layer.tiling,
}; };
let base_image = if imaginate_layer.use_img2img { let base_image = if imaginate_layer.use_img2img {
// Calculate the bounding box of the region to be exported // Calculate the size of the region to be exported
let bounds = layer.aabb(&persistent_data.font_cache).unwrap_or_default(); let size = transform.transform_point2(DVec2::ONE) - transform.transform_point2(DVec2::ZERO);
let size = bounds[1] - bounds[0];
let svg = self.render_document(bounds, persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path)); let svg = self.render_document(size, transform.inverse(), persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path));
Some(ImaginateBaseImage { svg, size }) Some(ImaginateBaseImage { svg, size })
} else { } else {
@ -952,8 +953,9 @@ impl DocumentMessageHandler {
) )
} }
pub fn render_document(&mut self, bounds: [DVec2; 2], persistent_data: &PersistentData, render_mode: DocumentRenderMode) -> String { pub fn render_document(&mut self, size: DVec2, transform: DAffine2, persistent_data: &PersistentData, render_mode: DocumentRenderMode) -> String {
// Remove the artwork and artboard pan/tilt/zoom to render it without the user's viewport navigation, and save it to be restored at the end // Remove the artwork and artboard pan/tilt/zoom to render it without the user's viewport navigation, and save it to be restored at the end
info!("Transform {transform}");
let old_artwork_transform = self.graphene_document.root.transform; let old_artwork_transform = self.graphene_document.root.transform;
self.graphene_document.root.transform = DAffine2::IDENTITY; self.graphene_document.root.transform = DAffine2::IDENTITY;
@ -965,7 +967,6 @@ impl DocumentMessageHandler {
// Render the document SVG code // Render the document SVG code
let size = bounds[1] - bounds[0];
let render_data = RenderData::new(ViewMode::Normal, &persistent_data.font_cache, None); let render_data = RenderData::new(ViewMode::Normal, &persistent_data.font_cache, None);
let artwork = match render_mode { let artwork = match render_mode {
@ -974,10 +975,15 @@ impl DocumentMessageHandler {
}; };
let artboards = self.artboard_message_handler.artboards_graphene_document.render_root(render_data); let artboards = self.artboard_message_handler.artboards_graphene_document.render_root(render_data);
let outside_artboards_color = if self.artboard_message_handler.artboard_ids.is_empty() { "#ffffff" } else { "#000000" }; let outside_artboards_color = if self.artboard_message_handler.artboard_ids.is_empty() { "#ffffff" } else { "#000000" };
let outside_artboards = format!(r#"<rect x="{}" y="{}" width="100%" height="100%" fill="{}" />"#, bounds[0].x, bounds[0].y, outside_artboards_color); let outside_artboards = format!(r#"<rect x="0" y="0" width="100%" height="100%" fill="{}" />"#, outside_artboards_color);
let matrix = transform
.to_cols_array()
.iter()
.enumerate()
.fold(String::new(), |accum, (i, entry)| accum + &(entry.to_string() + if i == 5 { "" } else { "," }));
let svg = format!( let svg = format!(
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="{} {} {} {}" width="{}" height="{}">{}{}{}{}</svg>"#, r#"<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 1 1" width="{}" height="{}">{}{}<g transform="matrix({})">{}{}</g></svg>"#,
bounds[0].x, bounds[0].y, size.x, size.y, size.x, size.y, "\n", outside_artboards, artboards, artwork size.x, size.y, "\n", outside_artboards, matrix, artboards, artwork
); );
// Transform the artwork and artboard back to their original scales // Transform the artwork and artboard back to their original scales

View File

@ -188,8 +188,7 @@ impl LayerData for ImaginateLayer {
let transform = self.transform(transforms, render_data.view_mode); let transform = self.transform(transforms, render_data.view_mode);
let inverse = transform.inverse(); let inverse = transform.inverse();
let matrix_values = transform.matrix2.to_cols_array(); let (width, height) = (transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length());
let (width, height) = (matrix_values[0], matrix_values[3]);
if !inverse.is_finite() { if !inverse.is_finite() {
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->"); let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
@ -205,25 +204,29 @@ impl LayerData for ImaginateLayer {
if let Some(blob_url) = &self.blob_url { if let Some(blob_url) = &self.blob_url {
let _ = write!( let _ = write!(
svg, svg,
r#"<image width="{}" height="{}" transform="matrix(1,0,0,1,{},{})" preserveAspectRatio="none" href="{}"/>"#, r#"<image width="{}" height="{}" preserveAspectRatio="none" href="{}" transform="matrix("#,
width.abs(), width.abs(),
height.abs(), height.abs(),
if width >= 0. { transform.translation.x } else { transform.translation.x + width },
if height >= 0. { transform.translation.y } else { transform.translation.y + height },
blob_url blob_url
); );
} else { } else {
let _ = write!( let _ = write!(
svg, svg,
r#"<rect width="{}" height="{}" transform="matrix(1,0,0,1,{},{})" fill="none" stroke="var(--color-data-raster)" stroke-width="3" stroke-dasharray="8"/>"#, r#"<rect width="{}" height="{}" fill="none" stroke="var(--color-data-raster)" stroke-width="3" stroke-dasharray="8" transform="matrix("#,
width.abs(), width.abs(),
height.abs(), height.abs(),
if width >= 0. { transform.translation.x } else { transform.translation.x + width },
if height >= 0. { transform.translation.y } else { transform.translation.y + height },
); );
} }
let _ = svg.write_str("</g>"); (transform * DAffine2::from_scale((width, height).into()).inverse())
.to_cols_array()
.iter()
.enumerate()
.for_each(|(i, entry)| {
let _ = svg.write_str(&(entry.to_string() + if i == 5 { "" } else { "," }));
});
let _ = svg.write_str(r#")" /> </g>"#);
} }
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> { fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {