mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 01:38:11 +08:00
Restore pre-flip color parity: rng replay, typed wrap, boolean marker paints
This commit is contained in:
@@ -159,9 +159,10 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
|||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_graphic::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_graphic::IDENTIFIER),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Collection of the coerced content (node 6) into the layer's group
|
// Collection of the content into the layer's group; the wrap keeps
|
||||||
|
// the content level's element type for the legacy boundary.
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
inputs: vec![NodeInput::node(NodeId(6), 0)],
|
inputs: vec![NodeInput::import(generic!(T), 1)],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::wrap_graphic::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::wrap_graphic::IDENTIFIER),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -191,12 +192,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
|||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Secondary (left) input type coercion
|
|
||||||
DocumentNode {
|
|
||||||
inputs: vec![NodeInput::import(generic!(T), 1)],
|
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::to_graphic::IDENTIFIER),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -262,14 +257,6 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
|||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// 6: to_graphic (secondary)
|
|
||||||
DocumentNodeMetadata {
|
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-28, -1)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use crate::markers::{ATTR_FILL, ATTR_STROKE};
|
||||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||||
use core_types::graphene_hash::CacheHash;
|
use core_types::graphene_hash::CacheHash;
|
||||||
use crate::markers::{ATTR_FILL, ATTR_STROKE};
|
|
||||||
use core_types::list::{AttributeValueDyn, Item, ItemAttributeValues, List};
|
use core_types::list::{AttributeValueDyn, Item, ItemAttributeValues, List};
|
||||||
use core_types::ops::{FromAnchorPosition, ListConvert};
|
use core_types::ops::{FromAnchorPosition, ListConvert};
|
||||||
use core_types::render_complexity::RenderComplexity;
|
use core_types::render_complexity::RenderComplexity;
|
||||||
@@ -258,6 +258,8 @@ pub fn bake_paint_transforms(attributes: &mut ItemAttributeValues, transform: DA
|
|||||||
for paint_key in [ATTR_FILL, ATTR_STROKE] {
|
for paint_key in [ATTR_FILL, ATTR_STROKE] {
|
||||||
if let Some(graphics) = attributes.get_mut::<List<Graphic>>(paint_key) {
|
if let Some(graphics) = attributes.get_mut::<List<Graphic>>(paint_key) {
|
||||||
bake_graphic_paint_transform(graphics, transform);
|
bake_graphic_paint_transform(graphics, transform);
|
||||||
|
} else if let Some(Some(graphics)) = attributes.get_mut::<Option<List<Graphic>>>(paint_key) {
|
||||||
|
bake_graphic_paint_transform(graphics, transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -604,9 +606,7 @@ fn group_is_fully_transparent(group: &core_types::record::Group) -> bool {
|
|||||||
core_types::record::GroupContent::Run(item) => {
|
core_types::record::GroupContent::Run(item) => {
|
||||||
let attrs = RunAttrs::of(item);
|
let attrs = RunAttrs::of(item);
|
||||||
let lanes = item.typed_lanes::<Graphic>();
|
let lanes = item.typed_lanes::<Graphic>();
|
||||||
(0..item.len()).all(|lane| {
|
(0..item.len()).all(|lane| RunAttrs::read_or(item, attrs.opacity, lane, 1.) <= 0. || lanes.as_ref().is_some_and(|lanes| lanes.element_ref(lane).is_fully_transparent()))
|
||||||
RunAttrs::read_or(item, attrs.opacity, lane, 1.) <= 0. || lanes.as_ref().is_some_and(|lanes| lanes.element_ref(lane).is_fully_transparent())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
core_types::record::GroupContent::Stack(children) => children.iter().all(group_is_fully_transparent),
|
core_types::record::GroupContent::Stack(children) => children.iter().all(group_is_fully_transparent),
|
||||||
}
|
}
|
||||||
@@ -719,7 +719,9 @@ pub fn run_to_render_list<T: Clone + Send + Sync + 'static>(item: &core_types::r
|
|||||||
fn push_lane_paint_into_interiors(list: &mut List<Graphic>) {
|
fn push_lane_paint_into_interiors(list: &mut List<Graphic>) {
|
||||||
for index in 0..list.len() {
|
for index in 0..list.len() {
|
||||||
for key in [ATTR_FILL, ATTR_STROKE] {
|
for key in [ATTR_FILL, ATTR_STROKE] {
|
||||||
let Some(paint) = paint_at(list, index, key).filter(|paint| is_paint_present(paint)).cloned() else { continue };
|
let Some(paint) = paint_at(list, index, key).filter(|paint| is_paint_present(paint)).cloned() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let Some(element) = list.element_mut(index) else { continue };
|
let Some(element) = list.element_mut(index) else { continue };
|
||||||
let fill_list = |inner: &mut List<Vector>| {
|
let fill_list = |inner: &mut List<Vector>| {
|
||||||
for item in 0..inner.len() {
|
for item in 0..inner.len() {
|
||||||
@@ -767,10 +769,10 @@ const _: () = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The graphic with every `Group` converted to its legacy list form.
|
/// The graphic with every `Group` converted to its legacy form.
|
||||||
pub fn map_groups_to_legacy(graphic: &Graphic) -> Graphic {
|
pub fn map_groups_to_legacy(graphic: &Graphic) -> Graphic {
|
||||||
match graphic {
|
match graphic {
|
||||||
Graphic::Group(group) => Graphic::Graphic(group_to_legacy_list(group)),
|
Graphic::Group(group) => group_to_legacy_graphic(group),
|
||||||
Graphic::Graphic(children) => {
|
Graphic::Graphic(children) => {
|
||||||
let mut children = children.clone();
|
let mut children = children.clone();
|
||||||
for child in children.iter_element_values_mut() {
|
for child in children.iter_element_values_mut() {
|
||||||
@@ -782,6 +784,27 @@ pub fn map_groups_to_legacy(graphic: &Graphic) -> Graphic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The group as one legacy graphic. A bare (row-less) wrap of a single typed
|
||||||
|
/// run keeps the run's typed variant, matching the `Into<Graphic>` the
|
||||||
|
/// pre-flip wrap applied; everything else becomes the legacy group list.
|
||||||
|
pub fn group_to_legacy_graphic(group: &core_types::record::Group) -> Graphic {
|
||||||
|
if group.row.is_none()
|
||||||
|
&& let core_types::record::GroupContent::Run(item) = &group.content
|
||||||
|
{
|
||||||
|
let typed = None
|
||||||
|
.or_else(|| run_to_legacy_list::<Vector>(item).map(Graphic::Vector))
|
||||||
|
.or_else(|| run_to_legacy_list::<Raster<CPU>>(item).map(Graphic::RasterCPU))
|
||||||
|
.or_else(|| run_to_legacy_list::<Raster<GPU>>(item).map(Graphic::RasterGPU))
|
||||||
|
.or_else(|| run_to_legacy_list::<Color>(item).map(Graphic::Color))
|
||||||
|
.or_else(|| run_to_legacy_list::<GradientStops>(item).map(Graphic::Gradient))
|
||||||
|
.or_else(|| run_to_legacy_list::<String>(item).map(Graphic::Text));
|
||||||
|
if let Some(typed) = typed {
|
||||||
|
return typed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Graphic::Graphic(group_to_legacy_list(group))
|
||||||
|
}
|
||||||
|
|
||||||
/// The group as a legacy `List<Graphic>`: a `Graphic` run becomes the items,
|
/// The group as a legacy `List<Graphic>`: a `Graphic` run becomes the items,
|
||||||
/// another typed run becomes one item holding its typed list, and stack
|
/// another typed run becomes one item holding its typed list, and stack
|
||||||
/// segments become one item each with the segment's row attributes.
|
/// segments become one item each with the segment's row attributes.
|
||||||
@@ -810,7 +833,7 @@ pub fn group_to_legacy_list(group: &core_types::record::Group) -> List<Graphic>
|
|||||||
core_types::record::GroupContent::Stack(children) => {
|
core_types::record::GroupContent::Stack(children) => {
|
||||||
let mut list = List::new();
|
let mut list = List::new();
|
||||||
for child in children {
|
for child in children {
|
||||||
list.push(Item::new_from_element(Graphic::Graphic(group_to_legacy_list(child))));
|
list.push(Item::new_from_element(group_to_legacy_graphic(child)));
|
||||||
let index = list.len() - 1;
|
let index = list.len() - 1;
|
||||||
if let Some(row) = &child.row {
|
if let Some(row) = &child.row {
|
||||||
if !row.is_empty() {
|
if !row.is_empty() {
|
||||||
|
|||||||
@@ -468,9 +468,14 @@ pub fn legacy_layer_extend<T: Send + Clone>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Nests the input graphical content in a wrapper graphic. This essentially "groups" the input.
|
/// Nests the input graphical content in a wrapper graphic. This essentially "groups" the input.
|
||||||
|
/// The wrapped run keeps the level's element type, so the legacy boundary can
|
||||||
|
/// lower a wrapped vector level to the bare typed graphic the pre-flip wrap made.
|
||||||
/// The inverse of this node is 'Flatten Graphic'.
|
/// The inverse of this node is 'Flatten Graphic'.
|
||||||
#[node_macro::node(category("General"), extent(wrap_graphic_extent))]
|
#[node_macro::node(category("General"), extent(wrap_graphic_extent))]
|
||||||
pub fn wrap_graphic(_: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IList<Graphic>) -> Result<IList<Graphic>, Interrupt> {
|
pub fn wrap_graphic<T: Clone + Send + Sync + core_types::CacheHash + 'static>(
|
||||||
|
_: impl Ctx + ExtractIndex + InjectIndex + Copy,
|
||||||
|
#[implementations(Graphic, Vector, Raster<CPU>, Raster<GPU>, Color, GradientStops, String)] content: IList<T>,
|
||||||
|
) -> Result<IList<Graphic>, Interrupt> {
|
||||||
// SAFETY: a materialized input's frames are arena-resident.
|
// SAFETY: a materialized input's frames are arena-resident.
|
||||||
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) };
|
||||||
Ok(Graphic::Group(core_types::record::Group {
|
Ok(Graphic::Group(core_types::record::Group {
|
||||||
@@ -480,7 +485,7 @@ pub fn wrap_graphic(_: impl Ctx + ExtractIndex + InjectIndex + Copy, content: IL
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The collected group is the level's single lane.
|
/// The collected group is the level's single lane.
|
||||||
fn wrap_graphic_extent(_content: ListIn<'_, Graphic>, _level: LevelIn) -> GPoll<Extent> {
|
fn wrap_graphic_extent<T>(_content: ListIn<'_, T>, _level: LevelIn) -> GPoll<Extent> {
|
||||||
GPoll::Final(Extent::Exactly(1))
|
GPoll::Final(Extent::Exactly(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
|
use core_types::attribute::{Attr, BlendMode as BlendModeAttr, ClippingMask, EditorLayerPath, Opacity, OpacityFill, Transform as TransformAttr};
|
||||||
use core_types::list::{Item, List};
|
use core_types::list::{Item, List};
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
use core_types::attribute::{Attr, BlendMode as BlendModeAttr, ClippingMask, EditorLayerPath, Opacity, OpacityFill, Transform as TransformAttr};
|
|
||||||
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, BlendMode, Color, Ctx};
|
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, BlendMode, Color, Ctx};
|
||||||
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke};
|
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphic_types::graphic::{bake_paint_transforms, set_paint_attribute};
|
use graphic_types::graphic::{bake_paint_transforms, set_paint_attribute};
|
||||||
|
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke};
|
||||||
use graphic_types::vector_types::gradient::{GradientSpreadMethod, GradientType};
|
use graphic_types::vector_types::gradient::{GradientSpreadMethod, GradientType};
|
||||||
use graphic_types::vector_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD};
|
|
||||||
use graphic_types::vector_types::subpath::{ManipulatorGroup, Subpath};
|
use graphic_types::vector_types::subpath::{ManipulatorGroup, Subpath};
|
||||||
use graphic_types::vector_types::vector::PointId;
|
use graphic_types::vector_types::vector::PointId;
|
||||||
use graphic_types::vector_types::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
use graphic_types::vector_types::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
||||||
|
use graphic_types::vector_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD};
|
||||||
use graphic_types::{ATTR_FILL, Graphic, IntoGraphicList, Vector};
|
use graphic_types::{ATTR_FILL, Graphic, IntoGraphicList, Vector};
|
||||||
use linesweeper::topology::Topology;
|
use linesweeper::topology::Topology;
|
||||||
use linesweeper::{BinaryOp, FillRule, binary_op};
|
use linesweeper::{BinaryOp, FillRule, binary_op};
|
||||||
@@ -21,7 +21,11 @@ pub use vector_types::vector::misc::BooleanOperation;
|
|||||||
// TODO: since before we used a Vec of single-item `List`s and now we use a single `List`
|
// TODO: since before we used a Vec of single-item `List`s and now we use a single `List`
|
||||||
// TODO: with multiple items while still assuming a single item for the boolean operations.
|
// TODO: with multiple items while still assuming a single item for the boolean operations.
|
||||||
|
|
||||||
fn boolean_core<'e>(arena: &'e core_types::arena::Arena, content: List<Graphic>, operation: BooleanOperation) -> Result<
|
fn boolean_core<'e>(
|
||||||
|
arena: &'e core_types::arena::Arena,
|
||||||
|
content: List<Graphic>,
|
||||||
|
operation: BooleanOperation,
|
||||||
|
) -> Result<
|
||||||
(
|
(
|
||||||
Vector,
|
Vector,
|
||||||
Attr<'e, TransformAttr>,
|
Attr<'e, TransformAttr>,
|
||||||
@@ -54,10 +58,12 @@ fn boolean_core<'e>(arena: &'e core_types::arena::Arena, content: List<Graphic>,
|
|||||||
result_vector_list.element_mut(0).unwrap().merge_by_distance_spatial(merge_transform, 0.0001);
|
result_vector_list.element_mut(0).unwrap().merge_by_distance_spatial(merge_transform, 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
let exhausted = || core_types::gpoll::Interrupt::from(core_types::gpoll::GraphError {
|
let exhausted = || {
|
||||||
kind: core_types::gpoll::ErrorKind::ArenaExhausted,
|
core_types::gpoll::Interrupt::from(core_types::gpoll::GraphError {
|
||||||
trace: Vec::new(),
|
kind: core_types::gpoll::ErrorKind::ArenaExhausted,
|
||||||
});
|
trace: Vec::new(),
|
||||||
|
})
|
||||||
|
};
|
||||||
let park_paint = |paint: Option<List<Graphic>>| -> Result<Option<&'e List<Graphic>>, core_types::gpoll::Interrupt> {
|
let park_paint = |paint: Option<List<Graphic>>| -> Result<Option<&'e List<Graphic>>, core_types::gpoll::Interrupt> {
|
||||||
match paint {
|
match paint {
|
||||||
Some(list) => Ok(Some(arena.alloc(list).ok_or_else(exhausted)?.0)),
|
Some(list) => Ok(Some(arena.alloc(list).ok_or_else(exhausted)?.0)),
|
||||||
@@ -66,8 +72,8 @@ fn boolean_core<'e>(arena: &'e core_types::arena::Arena, content: List<Graphic>,
|
|||||||
};
|
};
|
||||||
|
|
||||||
let element = result_vector_list.element(0).cloned().unwrap_or_default();
|
let element = result_vector_list.element(0).cloned().unwrap_or_default();
|
||||||
let fill = park_paint(result_vector_list.attribute::<List<Graphic>>(graphic_types::ATTR_FILL, 0).cloned())?;
|
let fill = park_paint(graphic_types::graphic::graphic_list_at(&result_vector_list, 0, graphic_types::ATTR_FILL).map(|paint| paint.into_owned()))?;
|
||||||
let stroke = park_paint(result_vector_list.attribute::<List<Graphic>>(graphic_types::ATTR_STROKE, 0).cloned())?;
|
let stroke = park_paint(graphic_types::graphic::graphic_list_at(&result_vector_list, 0, graphic_types::ATTR_STROKE).map(|paint| paint.into_owned()))?;
|
||||||
let layer_path: Vec<NodeId> = result_vector_list
|
let layer_path: Vec<NodeId> = result_vector_list
|
||||||
.attribute::<List<NodeId>>(ATTR_EDITOR_LAYER_PATH, 0)
|
.attribute::<List<NodeId>>(ATTR_EDITOR_LAYER_PATH, 0)
|
||||||
.map(|path| path.iter_element_values().copied().collect())
|
.map(|path| path.iter_element_values().copied().collect())
|
||||||
|
|||||||
@@ -58,7 +58,11 @@ fn assign_color_at(gradient: &GradientStops, position: usize, length: usize, ran
|
|||||||
let factor = match randomize {
|
let factor = match randomize {
|
||||||
true => {
|
true => {
|
||||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
||||||
(0..=position).map(|_| rng.random::<f64>()).next_back().unwrap_or_default()
|
let mut draw = 0.;
|
||||||
|
for _ in 0..=position {
|
||||||
|
draw = rng.random::<f64>();
|
||||||
|
}
|
||||||
|
draw
|
||||||
}
|
}
|
||||||
false => match repeat_every {
|
false => match repeat_every {
|
||||||
0 => position as f64 / (length - 1).max(1) as f64,
|
0 => position as f64 / (length - 1).max(1) as f64,
|
||||||
@@ -171,7 +175,7 @@ fn assign_colors_graphic<'e>(
|
|||||||
if lane >= content.len() {
|
if lane >= content.len() {
|
||||||
return Err(GraphError::past_end().into());
|
return Err(GraphError::past_end().into());
|
||||||
}
|
}
|
||||||
let mut element = content.element_ref(lane).clone();
|
let mut element = graphic_types::graphic::map_groups_to_legacy(content.element_ref(lane));
|
||||||
let (transform, layer_path) = carried_lane_attrs(ctx.arena(), content.lane(lane))?;
|
let (transform, layer_path) = carried_lane_attrs(ctx.arena(), content.lane(lane))?;
|
||||||
|
|
||||||
if gradient.len() == 0 {
|
if gradient.len() == 0 {
|
||||||
@@ -187,9 +191,11 @@ fn assign_colors_graphic<'e>(
|
|||||||
false => gradient_element,
|
false => gradient_element,
|
||||||
};
|
};
|
||||||
|
|
||||||
let interior_count = |graphic: &Graphic| graphic.as_vector().map_or(0, |list| list.len());
|
// The interiors the pre-flip node reached: only a lane's DIRECT vector
|
||||||
let length: usize = (0..content.len()).map(|row| interior_count(content.element_ref(row))).sum();
|
// list, so wrapped groups keep their own styling and consume no position.
|
||||||
let mut position: usize = (0..lane).map(|row| interior_count(content.element_ref(row))).sum();
|
let count_lane = |row: usize| graphic_types::graphic::map_groups_to_legacy(content.element_ref(row)).as_vector().map_or(0, |list| list.len());
|
||||||
|
let length: usize = (0..content.len()).map(count_lane).sum();
|
||||||
|
let mut position: usize = (0..lane).map(count_lane).sum();
|
||||||
|
|
||||||
if let Some(vector_list) = element.as_vector_mut() {
|
if let Some(vector_list) = element.as_vector_mut() {
|
||||||
for index in 0..vector_list.len() {
|
for index in 0..vector_list.len() {
|
||||||
|
|||||||
Reference in New Issue
Block a user