Skip to content

Commit 9148ad8

Browse files
committed
wip: first poc
Approximate Coons patch by creating bilinear gradient in parallelograms. Each subgrid's color does not have C1 continuity, so we can see steps of colors in the strongly skewed scene. This could be solved by using bicubic interpolation.
1 parent 48fb945 commit 9148ad8

3 files changed

Lines changed: 134 additions & 48 deletions

File tree

node-graph/libraries/no-std-types/src/color/color_types.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,20 @@ impl Color {
11011101
}
11021102
}
11031103

1104+
pub fn bilerp_color(c00: Color, c10: Color, c01: Color, c11: Color, u: f64, v: f64) -> Color {
1105+
let (u, v) = (u as f32, v as f32);
1106+
let w00 = (1. - u) * (1. - v);
1107+
let w10 = u * (1. - v);
1108+
let w01 = (1. - u) * v;
1109+
let w11 = u * v;
1110+
Color::from_rgbaf32_unchecked(
1111+
w00 * c00.r() + w10 * c10.r() + w01 * c01.r() + w11 * c11.r(),
1112+
w00 * c00.g() + w10 * c10.g() + w01 * c01.g() + w11 * c11.g(),
1113+
w00 * c00.b() + w10 * c10.b() + w01 * c01.b() + w11 * c11.b(),
1114+
w00 * c00.a() + w10 * c10.a() + w01 * c01.a() + w11 * c11.a(),
1115+
)
1116+
}
1117+
11041118
#[cfg(test)]
11051119
mod tests {
11061120
use super::*;

node-graph/libraries/rendering/src/renderer.rs

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::render_ext::RenderExt;
22
use crate::to_peniko::BlendModeExt;
33
use core_types::blending::BlendMode;
44
use core_types::bounds::{BoundingBox, RenderBoundingBox};
5-
use core_types::color::{Alpha, Color};
5+
use core_types::color::{Alpha, Color, bilerp_color};
66
use core_types::math::quad::Quad;
77
use core_types::render_complexity::RenderComplexity;
88
use core_types::table::{Table, TableRow};
@@ -17,14 +17,15 @@ use graphic_types::vector_types::subpath::Subpath;
1717
use graphic_types::vector_types::vector::click_target::{ClickTarget, FreePoint};
1818
use graphic_types::vector_types::vector::style::{Fill, PaintOrder, RenderMode, Stroke, StrokeAlign};
1919
use graphic_types::{Artboard, Graphic};
20-
use kurbo::{Affine, Cap, Join, Shape};
20+
use kurbo::{Affine, Cap, Join, ParamCurve, Shape};
2121
use num_traits::Zero;
2222
use std::collections::{HashMap, HashSet};
2323
use std::fmt::Write;
2424
use std::hash::{Hash, Hasher};
2525
use std::ops::Deref;
2626
use std::sync::{Arc, LazyLock};
2727
use vector_types::gradient::{self, GradientSpreadMethod};
28+
use vector_types::vector::misc::point_to_dvec2;
2829
use vello::*;
2930

3031
/// Cached 16x16 transparency checkerboard image data (two 8x8 cells of #ffffff and #cccccc).
@@ -972,7 +973,123 @@ impl Render for Table<Vector> {
972973
Fill::Gradient(g) if g.gradient_type == GradientType::Mesh
973974
);
974975
if is_mesh_fill {
975-
log::debug!("hoge");
976+
// Split the mesh into 8 x 8 subgrids
977+
let grid_num = 8;
978+
let grid_stride_uv = 1. / grid_num as f64;
979+
980+
let segments: Vec<_> = row.element.segment_iter().map(|(_, path_seg, _, _)| path_seg).collect();
981+
let points = row.element.point_domain.positions();
982+
983+
let top_seg = segments[0];
984+
let right_seg = segments[1];
985+
let bottom_seg = segments[2];
986+
let left_seg = segments[3];
987+
988+
let top_left_color = Color::RED;
989+
let top_right_color = Color::BLUE;
990+
let bottom_left_color = Color::GREEN;
991+
let bottom_right_color = Color::YELLOW;
992+
993+
let bilerp = |u: f64, v: f64| points[3] * (1. - u) * (1. - v) + points[2] * u * (1. - v) + points[0] * (1. - u) * v + points[1] * u * v;
994+
995+
// Create the position and color info of the grid that is splitted from the original mesh
996+
let mut grid_info: Vec<Vec<(DVec2, Color)>> = vec![];
997+
998+
for i in 0..=grid_num {
999+
let u = i as f64 * grid_stride_uv;
1000+
let mut grid_info_row: Vec<(DVec2, Color)> = vec![];
1001+
1002+
for j in 0..=grid_num {
1003+
let v = j as f64 * grid_stride_uv;
1004+
let c1_u = point_to_dvec2(bottom_seg.eval(1. - u));
1005+
let c2_u = point_to_dvec2(top_seg.eval(u));
1006+
let d1_v = point_to_dvec2(left_seg.eval(v));
1007+
let d2_v = point_to_dvec2(right_seg.eval(1. - v));
1008+
let lc = (1. - v) * c1_u + v * c2_u;
1009+
let ld = (1. - u) * d1_v + u * d2_v;
1010+
1011+
let pos = lc + ld - bilerp(u, v);
1012+
let color = bilerp_color(bottom_left_color, bottom_right_color, top_left_color, top_right_color, u, v);
1013+
grid_info_row.push((pos, color));
1014+
}
1015+
grid_info.push(grid_info_row);
1016+
}
1017+
1018+
// Create poloygons using the vertex position data
1019+
let mut idx = generate_uuid();
1020+
1021+
for i in 0..grid_num {
1022+
for j in 0..grid_num {
1023+
let bl = grid_info[i][j];
1024+
let tl = grid_info[i][j + 1];
1025+
let br = grid_info[i + 1][j];
1026+
let tr = grid_info[i + 1][j + 1];
1027+
1028+
// linear gradient for the bottom line
1029+
write!(
1030+
&mut render.svg_defs,
1031+
r##"<linearGradient id="gb{idx}" x1="{}" y1="{}" x2="{}" y2="{}" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#{}"/><stop offset="1" stop-color="#{}"/></linearGradient>"##,
1032+
bl.0.x,
1033+
bl.0.y,
1034+
br.0.x,
1035+
br.0.y,
1036+
bl.1.to_rgb_hex_srgb_from_gamma(),
1037+
br.1.to_rgb_hex_srgb_from_gamma(),
1038+
)
1039+
.unwrap();
1040+
1041+
// linear gradient for the top line
1042+
write!(
1043+
&mut render.svg_defs,
1044+
r##"<linearGradient id="gt{idx}" x1="{}" y1="{}" x2="{}" y2="{}" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#{}"/><stop offset="1" stop-color="#{}"/></linearGradient>"##,
1045+
tl.0.x,
1046+
tl.0.y,
1047+
tr.0.x,
1048+
tr.0.y,
1049+
tl.1.to_rgb_hex_srgb_from_gamma(),
1050+
tr.1.to_rgb_hex_srgb_from_gamma(),
1051+
)
1052+
.unwrap();
1053+
1054+
// top mask gradient
1055+
write!(
1056+
&mut render.svg_defs,
1057+
r##"<linearGradient id="gm{idx}" x1="{}" y1="{}" x2="{}" y2="{}" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="black"/><stop offset="1" stop-color="white"/></linearGradient>"##,
1058+
(bl.0.x + br.0.x) / 2.,
1059+
(bl.0.y + br.0.y) / 2.,
1060+
(tl.0.x + tr.0.x) / 2.,
1061+
(tl.0.y + tr.0.y) / 2.,
1062+
)
1063+
.unwrap();
1064+
1065+
// mask
1066+
write!(
1067+
&mut render.svg_defs,
1068+
r##"<mask id="m{idx}" maskUnits="userSpaceOnUse"><polygon points="{},{} {},{} {},{} {},{}" fill="url(#gm{idx})"/></mask>"##,
1069+
bl.0.x, bl.0.y, br.0.x, br.0.y, tr.0.x, tr.0.y, tl.0.x, tl.0.y,
1070+
)
1071+
.unwrap();
1072+
1073+
render.leaf_tag("polygon", |attributes| {
1074+
attributes.push("points", format!("{},{} {},{} {},{} {},{}", bl.0.x, bl.0.y, br.0.x, br.0.y, tr.0.x, tr.0.y, tl.0.x, tl.0.y));
1075+
attributes.push("fill", format!("url(#gb{idx})"));
1076+
});
1077+
1078+
render.leaf_tag("polygon", |attributes| {
1079+
attributes.push("points", format!("{},{} {},{} {},{} {},{}", bl.0.x, bl.0.y, br.0.x, br.0.y, tr.0.x, tr.0.y, tl.0.x, tl.0.y));
1080+
attributes.push("fill", format!("url(#gt{idx})"));
1081+
attributes.push("mask", format!("url(#m{idx})"));
1082+
});
1083+
1084+
idx += 1;
1085+
}
1086+
}
1087+
1088+
// Create a linear gradients, bottom-left -> bottom-right
1089+
1090+
// Create a linear gradient mask from top to bottom
1091+
1092+
// Create a linear gradient, top->left -> top->right with applying the mask
9761093
return;
9771094
}
9781095

node-graph/nodes/vector/src/vector_nodes.rs

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -109,51 +109,6 @@ where
109109
content
110110
}
111111

112-
// #[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
113-
// async fn mesh_gradient(
114-
// _: impl Ctx,
115-
// /// The content with vector paths to apply the mesh gradient to.
116-
// mut content: Table<Vector>,
117-
// /// The top-left color of the gradient.
118-
// #[default(Color::BLACK)]
119-
// top_left_color: Table<Color>,
120-
// /// The top-right color of the gradient.
121-
// #[default(Color::BLACK)]
122-
// top_right_color: Table<Color>,
123-
// /// The bottom-left color of the gradient.
124-
// #[default(Color::BLACK)]
125-
// bottom_left_color: Table<Color>,
126-
// /// The bottom-right color of the gradient.
127-
// #[default(Color::BLACK)]
128-
// bottom_right_color: Table<Color>,
129-
// _backup_color: Table<Color>,
130-
// _backup_gradient: Gradient,
131-
// ) -> Table<Vector> {
132-
// for vector in content.vector_iter_mut() {
133-
// let grid_num = 8;
134-
// let grid_stride_uv = 1. / grid_num as f64;
135-
136-
// let segments: Vec<_> = vector.element.segment_iter().collect();
137-
// let points = vector.element.point_domain.positions();
138-
// let top_seg = segments.get(0).unwrap_throw().1;
139-
// let right_seg = segments.get(1).unwrap_throw().1;
140-
// let bottom_seg = segments.get(2).unwrap_throw().1;
141-
// let left_seg = segments.get(3).unwrap_throw().1;
142-
143-
// let bilerp = |u: f64, v: f64| points[0] * (1. - u) * (1. - v) + points[1] * u * (1. - v) + points[2] * (1. - u) * v + points[3] * u * v;
144-
145-
// for i in 0..=grid_num {
146-
// for j in 0..=grid_num {
147-
// let u = i as f64 * grid_stride_uv;
148-
// let v = j as f64 * grid_stride_uv;
149-
// let pos = point_to_dvec2(top_seg.eval(u)) + point_to_dvec2(bottom_seg.eval(1. - u)) - bilerp(u, v);
150-
// }
151-
// }
152-
// }
153-
154-
// content
155-
// }
156-
157112
/// Applies a fill style to the vector content, giving an appearance to the area within the interior of the geometry.
158113
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
159114
async fn fill<F: Into<Fill> + 'n + Send, V: VectorTableIterMut + 'n + Send>(

0 commit comments

Comments
 (0)