mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Construct value nodes as native record sources
This commit is contained in:
@@ -11,7 +11,7 @@ use core_types::node::Node;
|
||||
use core_types::registry::{EdgeHandle, edge_type};
|
||||
use core_types::transform::Footprint;
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::value::value_edge;
|
||||
use core_types::value::record_value_edge;
|
||||
use core_types::{CacheHash, Color, ContextModification, MemoHash, Type, TypeDescriptor};
|
||||
use dyn_any::DynAny;
|
||||
pub use dyn_any::StaticType;
|
||||
@@ -290,13 +290,13 @@ macro_rules! tagged_value {
|
||||
// ===============
|
||||
// MANUAL VARIANTS
|
||||
// ===============
|
||||
Self::None => Ok(value_edge(())),
|
||||
Self::None => Ok(record_value_edge(())),
|
||||
Self::TypeDefault(td) => {
|
||||
// Same direct-construction path as `to_dynany` for the same reason as in `to_dynany`.
|
||||
let name = td.name.as_ref();
|
||||
macro_rules! check {
|
||||
($type_default:ty) => {
|
||||
if name == std::any::type_name::<$type_default>() { return Ok(value_edge(<$type_default>::default())); }
|
||||
if name == std::any::type_name::<$type_default>() { return Ok(record_value_edge(<$type_default>::default())); }
|
||||
};
|
||||
}
|
||||
for_each_type_default!(check);
|
||||
@@ -304,33 +304,33 @@ macro_rules! tagged_value {
|
||||
}
|
||||
Self::F64Array(values) => {
|
||||
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
||||
Ok(value_edge(list))
|
||||
Ok(record_value_edge(list))
|
||||
}
|
||||
Self::Color(color) => {
|
||||
let list: List<Color> = color.into_iter().map(core_types::list::Item::new_from_element).collect();
|
||||
Ok(value_edge(list))
|
||||
Ok(record_value_edge(list))
|
||||
}
|
||||
Self::Gradient(stops) => Ok(value_edge(List::<GradientStops>::new_from_element(stops))),
|
||||
Self::Gradient(stops) => Ok(record_value_edge(List::<GradientStops>::new_from_element(stops))),
|
||||
Self::BrushStrokes(strokes) => {
|
||||
let list: List<BrushStroke> = strokes.into_iter().map(core_types::list::Item::new_from_element).collect();
|
||||
Ok(value_edge(list))
|
||||
Ok(record_value_edge(list))
|
||||
}
|
||||
// =======================
|
||||
// AUTO-GENERATED VARIANTS
|
||||
// =======================
|
||||
$( Self::$identifier(x) => Ok(value_edge(x)), )*
|
||||
$( Self::$identifier(x) => Ok(record_value_edge(x)), )*
|
||||
// =======================
|
||||
// NON-SERIALIZED VARIANTS
|
||||
// =======================
|
||||
Self::RenderOutput(x) => Ok(value_edge(x)),
|
||||
Self::RenderOutput(x) => Ok(record_value_edge(x)),
|
||||
Self::NodeIdPath(path) => {
|
||||
let list: List<NodeId> = path.into_iter().map(core_types::list::Item::new_from_element).collect();
|
||||
Ok(value_edge(list))
|
||||
Ok(record_value_edge(list))
|
||||
}
|
||||
Self::DocumentNode(node) => Ok(value_edge(node)),
|
||||
Self::ContextModification(modification) => Ok(value_edge(modification)),
|
||||
Self::EditorApi(x) => Ok(value_edge(x)),
|
||||
Self::ResourceHash(x) => Ok(value_edge(x)),
|
||||
Self::DocumentNode(node) => Ok(record_value_edge(node)),
|
||||
Self::ContextModification(modification) => Ok(record_value_edge(modification)),
|
||||
Self::EditorApi(x) => Ok(record_value_edge(x)),
|
||||
Self::ResourceHash(x) => Ok(record_value_edge(x)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -752,10 +752,10 @@ impl TypingContext {
|
||||
}
|
||||
|
||||
let inputs = match node.construction_args {
|
||||
// If the node has a value input we can infer the return type from it
|
||||
// A value node is a native record source, so it types as a record
|
||||
// of its value.
|
||||
ConstructionArgs::Value(ref v) => {
|
||||
// TODO: This should return a reference to the value
|
||||
let types = NodeIOTypes::new(concrete!(Context), v.ty(), vec![]);
|
||||
let types = NodeIOTypes::new(concrete!(Context), Type::Record(Box::new(v.ty())), vec![]);
|
||||
self.inferred.insert(node_id, types.clone());
|
||||
return Ok(types);
|
||||
}
|
||||
@@ -1327,9 +1327,9 @@ mod ref_adapter_test {
|
||||
],
|
||||
),
|
||||
(
|
||||
ProtoNodeIdentifier::new("graphene_core::memo::LendNode"),
|
||||
ProtoNodeIdentifier::new("core_types::record::RecordExtractLendNode"),
|
||||
vec![RegistryEntry {
|
||||
io: NodeIOTypes::new(concrete!(Context), ref_type::<String>(), vec![edge_type::<String>()]),
|
||||
io: NodeIOTypes::new(concrete!(Context), ref_type::<String>(), vec![record_edge_type::<String>()]),
|
||||
constructor: unused,
|
||||
}],
|
||||
),
|
||||
@@ -1366,7 +1366,7 @@ mod ref_adapter_test {
|
||||
|
||||
assert_eq!(network.nodes.len(), 3);
|
||||
let (adapter_id, adapter) = &network.nodes[1];
|
||||
assert_eq!(adapter.identifier.as_str(), "graphene_core::memo::LendNode");
|
||||
assert_eq!(adapter.identifier.as_str(), "core_types::record::RecordExtractLendNode");
|
||||
assert_eq!(adapter.unwrap_construction_nodes(), vec![NodeId(0)]);
|
||||
assert_eq!(network.nodes[2].1.unwrap_construction_nodes(), vec![*adapter_id]);
|
||||
assert_eq!(typing.type_of(*adapter_id).unwrap().return_value, ref_type::<String>());
|
||||
@@ -1392,7 +1392,7 @@ mod ref_adapter_test {
|
||||
let adapters: Vec<NodeId> = network
|
||||
.nodes
|
||||
.iter()
|
||||
.filter(|(_, node)| node.identifier.as_str() == "graphene_core::memo::LendNode")
|
||||
.filter(|(_, node)| node.identifier.as_str() == "core_types::record::RecordExtractLendNode")
|
||||
.map(|(id, _)| *id)
|
||||
.collect();
|
||||
let [adapter_id] = adapters.as_slice() else {
|
||||
|
||||
@@ -555,14 +555,18 @@ mod test {
|
||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32).into()), vec![]);
|
||||
let context = TypingContext::default();
|
||||
tree.push_node(NodeId(0), val_1_protonode, &context).unwrap();
|
||||
let _node = tree.get(NodeId(0)).unwrap();
|
||||
let handle = tree.get(NodeId(0)).unwrap();
|
||||
let layout = handle.layout().unwrap().clone();
|
||||
let edge = handle.duplicate().downcast_record::<u32>().unwrap();
|
||||
|
||||
let arena = Arena::new(64).unwrap();
|
||||
let generations = [];
|
||||
let scope = EvalScope::new(None, None, None, &generations, &arena);
|
||||
let ctx = ContextImpl::root(&scope);
|
||||
let result: Option<GPoll<u32>> = tree.eval(NodeId(0), &ctx);
|
||||
assert_eq!(result, Some(GPoll::Final(2)));
|
||||
let GPoll::Final(value) = edge.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
assert_eq!(unsafe { core_types::record::read_element::<u32>(layout.rec(&value)) }, 2);
|
||||
}
|
||||
|
||||
fn proto_node(identifier: &'static str, args: Vec<NodeId>) -> ProtoNode {
|
||||
|
||||
@@ -13,6 +13,13 @@ pub fn value_edge<T: Clone + crate::WasmNotSend + crate::WasmNotSync + 'static>(
|
||||
crate::registry::EdgeHandle::new(std::sync::Arc::new(ClonedNode(value)) as std::sync::Arc<crate::registry::ErasedNode<T>>)
|
||||
}
|
||||
|
||||
/// The native record edge of a constant: the element lifts onto the record
|
||||
/// wire per evaluation, so value sources need no spliced adapter.
|
||||
pub fn record_value_edge<T: Clone + Send + Sync + 'static>(value: T) -> crate::registry::EdgeHandle {
|
||||
let node = crate::record::RecordLift::<T, _>::new(ClonedNode(value));
|
||||
crate::registry::EdgeHandle::new_record::<T>(std::sync::Arc::new(node) as std::sync::Arc<crate::registry::ErasedRecordNode>)
|
||||
}
|
||||
|
||||
impl<T: Clone> ClonedNode<T> {
|
||||
pub const fn new(value: T) -> ClonedNode<T> {
|
||||
ClonedNode(value)
|
||||
|
||||
Reference in New Issue
Block a user