New node: Assign Colors (#1938)
* New node: Assign Colors * Simplify the Procedural String Lights demo artwork * Add "Group" node to the node catalog * Better comment styling in profiling action
This commit is contained in:
parent
12ebc6f972
commit
fa981a0897
|
|
@ -2,7 +2,7 @@ name: Profiling
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [master]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
@ -11,90 +11,90 @@ jobs:
|
||||||
profile:
|
profile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
|
||||||
- name: Install Valgrind
|
- name: Install Valgrind
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y valgrind
|
sudo apt-get install -y valgrind
|
||||||
|
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/registry
|
~/.cargo/registry
|
||||||
~/.cargo/git
|
~/.cargo/git
|
||||||
target
|
target
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: Install iai-callgrind
|
- name: Install iai-callgrind
|
||||||
run: |
|
run: |
|
||||||
cargo install iai-callgrind-runner@0.12.3
|
cargo install iai-callgrind-runner@0.12.3
|
||||||
|
|
||||||
- name: Checkout master branch
|
- name: Checkout master branch
|
||||||
run: |
|
run: |
|
||||||
git fetch origin master:master
|
git fetch origin master:master
|
||||||
git checkout master
|
git checkout master
|
||||||
|
|
||||||
- name: Run baseline benchmarks
|
- name: Run baseline benchmarks
|
||||||
run: |
|
run: |
|
||||||
cargo bench --bench compile_demo_art --features=iai -- --save-baseline=master
|
cargo bench --bench compile_demo_art --features=iai -- --save-baseline=master
|
||||||
|
|
||||||
- name: Checkout PR branch
|
- name: Checkout PR branch
|
||||||
run: |
|
run: |
|
||||||
git checkout ${{ github.event.pull_request.head.sha }}
|
git checkout ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Run PR benchmarks
|
- name: Run PR benchmarks
|
||||||
id: benchmark
|
id: benchmark
|
||||||
run: |
|
run: |
|
||||||
BENCH_OUTPUT=$(cargo bench --bench compile_demo_art --features=iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
|
BENCH_OUTPUT=$(cargo bench --bench compile_demo_art --features=iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
|
||||||
echo "BENCHMARK_OUTPUT<<EOF" >> $GITHUB_OUTPUT
|
echo "BENCHMARK_OUTPUT<<EOF" >> $GITHUB_OUTPUT
|
||||||
echo "$BENCH_OUTPUT" >> $GITHUB_OUTPUT
|
echo "$BENCH_OUTPUT" >> $GITHUB_OUTPUT
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Comment PR
|
- name: Comment PR
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
const benchmarkOutput = JSON.parse(`${{ steps.benchmark.outputs.BENCHMARK_OUTPUT }}`);
|
const benchmarkOutput = JSON.parse(`${{ steps.benchmark.outputs.BENCHMARK_OUTPUT }}`);
|
||||||
|
|
||||||
let significantChanges = false;
|
let significantChanges = false;
|
||||||
let commentBody = "#### Performance Benchmark Results\n\n";
|
let commentBody = "#### Performance Benchmark Results\n\n";
|
||||||
|
|
||||||
for (const benchmark of benchmarkOutput) {
|
for (const benchmark of benchmarkOutput) {
|
||||||
if (benchmark.callgrind_summary && benchmark.callgrind_summary.summaries) {
|
if (benchmark.callgrind_summary && benchmark.callgrind_summary.summaries) {
|
||||||
for (const summary of benchmark.callgrind_summary.summaries) {
|
for (const summary of benchmark.callgrind_summary.summaries) {
|
||||||
for (const [eventKind, costsDiff] of Object.entries(summary.events)) {
|
for (const [eventKind, costsDiff] of Object.entries(summary.events)) {
|
||||||
if (costsDiff.diff_pct !== null && Math.abs(costsDiff.diff_pct) > 5) {
|
if (costsDiff.diff_pct !== null && Math.abs(costsDiff.diff_pct) > 5) {
|
||||||
significantChanges = true;
|
significantChanges = true;
|
||||||
const changeDirection = costsDiff.diff_pct > 0 ? "increase" : "decrease";
|
const changeDirection = costsDiff.diff_pct > 0 ? "Increase" : "Decrease";
|
||||||
const color = costsDiff.diff_pct > 0 ? "red" : "green";
|
const color = costsDiff.diff_pct > 0 ? "red" : "lime";
|
||||||
commentBody += `\`${benchmark.module_path}\` - ${eventKind}:\n`;
|
commentBody += `\`${benchmark.module_path}\` - ${eventKind}:\n`;
|
||||||
commentBody += `\\color{${color}}${changeDirection} of ${Math.abs(costsDiff.diff_pct).toFixed(2)}%\n`;
|
commentBody += `${changeDirection} of $$\\color{${color}}${Math.abs(costsDiff.diff_pct).toFixed(2)}\\\\%$$\n`;
|
||||||
commentBody += `Old: ${costsDiff.old}, New: ${costsDiff.new}\n\n`;
|
commentBody += `Old: ${costsDiff.old}, New: ${costsDiff.new}\n\n`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (significantChanges) {
|
||||||
if (significantChanges) {
|
github.rest.issues.createComment({
|
||||||
github.rest.issues.createComment({
|
issue_number: context.issue.number,
|
||||||
issue_number: context.issue.number,
|
owner: context.repo.owner,
|
||||||
owner: context.repo.owner,
|
repo: context.repo.repo,
|
||||||
repo: context.repo.repo,
|
body: commentBody
|
||||||
body: commentBody
|
});
|
||||||
});
|
} else {
|
||||||
} else {
|
console.log("No significant performance changes detected. Skipping comment.");
|
||||||
console.log("No significant performance changes detected. Skipping comment.");
|
console.log(commentBody);
|
||||||
console.log(commentBody);
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -172,6 +172,23 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||||
category: "Structural",
|
category: "Structural",
|
||||||
properties: |_document_node, _node_id, _context| node_properties::string_properties("The Monitor node stores the value of its last evaluation"),
|
properties: |_document_node, _node_id, _context| node_properties::string_properties("The Monitor node stores the value of its last evaluation"),
|
||||||
},
|
},
|
||||||
|
DocumentNodeDefinition {
|
||||||
|
identifier: "Group",
|
||||||
|
node_template: NodeTemplate {
|
||||||
|
document_node: DocumentNode {
|
||||||
|
implementation: DocumentNodeImplementation::proto("graphene_core::ToGraphicGroupNode"),
|
||||||
|
inputs: vec![NodeInput::value(TaggedValue::VectorData(VectorData::empty()), true)],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||||
|
input_names: vec!["Element".to_string()],
|
||||||
|
output_names: vec!["Graphic Group".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
category: "General",
|
||||||
|
properties: node_properties::node_no_properties,
|
||||||
|
},
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
identifier: "Merge",
|
identifier: "Merge",
|
||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
|
|
@ -235,7 +252,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||||
},
|
},
|
||||||
DocumentNodeMetadata {
|
DocumentNodeMetadata {
|
||||||
persistent_metadata: DocumentNodePersistentMetadata {
|
persistent_metadata: DocumentNodePersistentMetadata {
|
||||||
display_name: "To Graphic Group".to_string(),
|
display_name: "Group".to_string(),
|
||||||
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -3)),
|
node_type_metadata: NodeTypePersistentMetadata::node(IVec2::new(-14, -3)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
|
@ -3499,6 +3516,39 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||||
category: "Transform",
|
category: "Transform",
|
||||||
properties: node_properties::node_no_properties,
|
properties: node_properties::node_no_properties,
|
||||||
},
|
},
|
||||||
|
DocumentNodeDefinition {
|
||||||
|
identifier: "Assign Colors",
|
||||||
|
node_template: NodeTemplate {
|
||||||
|
document_node: DocumentNode {
|
||||||
|
implementation: DocumentNodeImplementation::proto("graphene_core::vector::AssignColorsNode<_, _, _, _, _, _>"),
|
||||||
|
inputs: vec![
|
||||||
|
NodeInput::value(TaggedValue::GraphicGroup(graphene_core::GraphicGroup::default()), true),
|
||||||
|
NodeInput::value(TaggedValue::Bool(true), false),
|
||||||
|
NodeInput::value(TaggedValue::Bool(false), false),
|
||||||
|
NodeInput::value(TaggedValue::GradientStops(vector::style::GradientStops::default()), false),
|
||||||
|
NodeInput::value(TaggedValue::Bool(false), false),
|
||||||
|
NodeInput::value(TaggedValue::Bool(false), false),
|
||||||
|
NodeInput::value(TaggedValue::U32(0), false),
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||||
|
input_names: vec![
|
||||||
|
"Vector Group".to_string(),
|
||||||
|
"Fill".to_string(),
|
||||||
|
"Stroke".to_string(),
|
||||||
|
"Gradient".to_string(),
|
||||||
|
"Reverse".to_string(),
|
||||||
|
"Randomize".to_string(),
|
||||||
|
"Repeat Every".to_string(),
|
||||||
|
],
|
||||||
|
output_names: vec!["Vector Group".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
category: "Vector",
|
||||||
|
properties: node_properties::assign_colors_properties,
|
||||||
|
},
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
identifier: "Fill",
|
identifier: "Fill",
|
||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,11 @@ fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||||
fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
|
||||||
if let Some(TaggedValue::String(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(TaggedValue::String(x)) = &input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
TextInput::new(x.clone())
|
TextInput::new(x.clone())
|
||||||
|
|
@ -102,7 +106,11 @@ fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
||||||
fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
|
||||||
if let Some(TaggedValue::String(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(TaggedValue::String(x)) = &input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
TextAreaInput::new(x.clone())
|
TextAreaInput::new(x.clone())
|
||||||
|
|
@ -117,7 +125,11 @@ fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
||||||
fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
|
||||||
if let Some(&TaggedValue::Bool(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::Bool(x)) = &input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
CheckboxInput::new(x)
|
CheckboxInput::new(x)
|
||||||
|
|
@ -141,7 +153,11 @@ fn footprint_widget(document_node: &DocumentNode, node_id: NodeId, index: usize)
|
||||||
add_blank_assist(&mut resolution_widgets);
|
add_blank_assist(&mut resolution_widgets);
|
||||||
resolution_widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
resolution_widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
|
|
||||||
if let Some(&TaggedValue::Footprint(footprint)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::Footprint(footprint)) = &input.as_non_exposed_value() {
|
||||||
let top_left = footprint.transform.transform_point2(DVec2::ZERO);
|
let top_left = footprint.transform.transform_point2(DVec2::ZERO);
|
||||||
let bounds = footprint.scale();
|
let bounds = footprint.scale();
|
||||||
let oversample = footprint.resolution.as_dvec2() / bounds;
|
let oversample = footprint.resolution.as_dvec2() / bounds;
|
||||||
|
|
@ -271,77 +287,86 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
||||||
|
|
||||||
assist(&mut widgets);
|
assist(&mut widgets);
|
||||||
|
|
||||||
if let Some(&TaggedValue::DVec2(dvec2)) = document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
widgets.extend_from_slice(&[
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
NumberInput::new(Some(dvec2.x))
|
};
|
||||||
.label(x)
|
match input.as_non_exposed_value() {
|
||||||
.unit(unit)
|
Some(&TaggedValue::DVec2(dvec2)) => {
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
widgets.extend_from_slice(&[
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
|
NumberInput::new(Some(dvec2.x))
|
||||||
.on_commit(commit_value)
|
.label(x)
|
||||||
.widget_holder(),
|
.unit(unit)
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
NumberInput::new(Some(dvec2.y))
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.label(y)
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
|
||||||
.unit(unit)
|
.on_commit(commit_value)
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
.widget_holder(),
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
|
NumberInput::new(Some(dvec2.y))
|
||||||
.on_commit(commit_value)
|
.label(y)
|
||||||
.widget_holder(),
|
.unit(unit)
|
||||||
]);
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
} else if let Some(&TaggedValue::IVec2(ivec2)) = document_node.inputs[index].as_non_exposed_value() {
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
let update_x = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(input.value.unwrap() as i32, ivec2.y));
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
|
||||||
let update_y = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(ivec2.x, input.value.unwrap() as i32));
|
.on_commit(commit_value)
|
||||||
widgets.extend_from_slice(&[
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
]);
|
||||||
NumberInput::new(Some(ivec2.x as f64))
|
}
|
||||||
.int()
|
Some(&TaggedValue::IVec2(ivec2)) => {
|
||||||
.label(x)
|
let update_x = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(input.value.unwrap() as i32, ivec2.y));
|
||||||
.unit(unit)
|
let update_y = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(ivec2.x, input.value.unwrap() as i32));
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
widgets.extend_from_slice(&[
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
.on_update(update_value(update_x, node_id, index))
|
NumberInput::new(Some(ivec2.x as f64))
|
||||||
.on_commit(commit_value)
|
.int()
|
||||||
.widget_holder(),
|
.label(x)
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
.unit(unit)
|
||||||
NumberInput::new(Some(ivec2.y as f64))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.int()
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.label(y)
|
.on_update(update_value(update_x, node_id, index))
|
||||||
.unit(unit)
|
.on_commit(commit_value)
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
.widget_holder(),
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
.on_update(update_value(update_y, node_id, index))
|
NumberInput::new(Some(ivec2.y as f64))
|
||||||
.on_commit(commit_value)
|
.int()
|
||||||
.widget_holder(),
|
.label(y)
|
||||||
]);
|
.unit(unit)
|
||||||
} else if let Some(&TaggedValue::UVec2(uvec2)) = document_node.inputs[index].as_non_exposed_value() {
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
let update_x = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(input.value.unwrap() as u32, uvec2.y));
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
let update_y = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(uvec2.x, input.value.unwrap() as u32));
|
.on_update(update_value(update_y, node_id, index))
|
||||||
widgets.extend_from_slice(&[
|
.on_commit(commit_value)
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
.widget_holder(),
|
||||||
NumberInput::new(Some(uvec2.x as f64))
|
]);
|
||||||
.int()
|
}
|
||||||
.label(x)
|
Some(&TaggedValue::UVec2(uvec2)) => {
|
||||||
.unit(unit)
|
let update_x = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(input.value.unwrap() as u32, uvec2.y));
|
||||||
.min(min.unwrap_or(0.))
|
let update_y = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(uvec2.x, input.value.unwrap() as u32));
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
widgets.extend_from_slice(&[
|
||||||
.on_update(update_value(update_x, node_id, index))
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
.on_commit(commit_value)
|
NumberInput::new(Some(uvec2.x as f64))
|
||||||
.widget_holder(),
|
.int()
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
.label(x)
|
||||||
NumberInput::new(Some(uvec2.y as f64))
|
.unit(unit)
|
||||||
.int()
|
.min(min.unwrap_or(0.))
|
||||||
.label(y)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
.unit(unit)
|
.on_update(update_value(update_x, node_id, index))
|
||||||
.min(min.unwrap_or(0.))
|
.on_commit(commit_value)
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
.widget_holder(),
|
||||||
.on_update(update_value(update_y, node_id, index))
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
.on_commit(commit_value)
|
NumberInput::new(Some(uvec2.y as f64))
|
||||||
.widget_holder(),
|
.int()
|
||||||
]);
|
.label(y)
|
||||||
|
.unit(unit)
|
||||||
|
.min(min.unwrap_or(0.))
|
||||||
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
|
.on_update(update_value(update_y, node_id, index))
|
||||||
|
.on_commit(commit_value)
|
||||||
|
.widget_holder(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutGroup::Row { widgets }
|
LayoutGroup::Row { widgets }
|
||||||
|
|
@ -360,7 +385,11 @@ fn vec_f64_input(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||||
.map(TaggedValue::VecF64)
|
.map(TaggedValue::VecF64)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(TaggedValue::VecF64(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(TaggedValue::VecF64(x)) = &input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
text_props
|
text_props
|
||||||
|
|
@ -385,7 +414,11 @@ fn vec_dvec2_input(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
||||||
.map(TaggedValue::VecDVec2)
|
.map(TaggedValue::VecDVec2)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(TaggedValue::VecDVec2(x)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(TaggedValue::VecDVec2(x)) = &input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
text_props
|
text_props
|
||||||
|
|
@ -403,7 +436,11 @@ fn font_inputs(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
||||||
|
|
||||||
let from_font_input = |font: &FontInput| TaggedValue::Font(Font::new(font.font_family.clone(), font.font_style.clone()));
|
let from_font_input = |font: &FontInput| TaggedValue::Font(Font::new(font.font_family.clone(), font.font_style.clone()));
|
||||||
|
|
||||||
if let Some(TaggedValue::Font(font)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return (vec![], None);
|
||||||
|
};
|
||||||
|
if let Some(TaggedValue::Font(font)) = &input.as_non_exposed_value() {
|
||||||
first_widgets.extend_from_slice(&[
|
first_widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
FontInput::new(font.font_family.clone(), font.font_style.clone())
|
FontInput::new(font.font_family.clone(), font.font_style.clone())
|
||||||
|
|
@ -439,32 +476,41 @@ fn vector_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||||
fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, number_props: NumberInput, blank_assist: bool) -> Vec<WidgetHolder> {
|
fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, number_props: NumberInput, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, blank_assist);
|
||||||
|
|
||||||
if let Some(&TaggedValue::F64(x)) = document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
widgets.extend_from_slice(&[
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
match input.as_non_exposed_value() {
|
||||||
|
Some(&TaggedValue::F64(x)) => widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
number_props
|
number_props
|
||||||
.value(Some(x))
|
.value(Some(x))
|
||||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index))
|
.on_update(update_value(move |x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
])
|
]),
|
||||||
} else if let Some(&TaggedValue::U32(x)) = document_node.inputs[index].as_non_exposed_value() {
|
Some(&TaggedValue::U32(x)) => widgets.extend_from_slice(&[
|
||||||
widgets.extend_from_slice(&[
|
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
number_props
|
number_props
|
||||||
.value(Some(x as f64))
|
.value(Some(x as f64))
|
||||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::U32((x.value.unwrap()) as u32), node_id, index))
|
.on_update(update_value(move |x: &NumberInput| TaggedValue::U32((x.value.unwrap()) as u32), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
])
|
]),
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
widgets
|
widgets
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::RedGreenBlue(mode)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::RedGreenBlue(mode)) = &input.as_non_exposed_value() {
|
||||||
let calculation_modes = [RedGreenBlue::Red, RedGreenBlue::Green, RedGreenBlue::Blue];
|
let calculation_modes = [RedGreenBlue::Red, RedGreenBlue::Green, RedGreenBlue::Blue];
|
||||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||||
for method in calculation_modes {
|
for method in calculation_modes {
|
||||||
|
|
@ -487,7 +533,11 @@ fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||||
|
|
||||||
fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::RedGreenBlueAlpha(mode)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::RedGreenBlueAlpha(mode)) = &input.as_non_exposed_value() {
|
||||||
let calculation_modes = [RedGreenBlueAlpha::Red, RedGreenBlueAlpha::Green, RedGreenBlueAlpha::Blue, RedGreenBlueAlpha::Alpha];
|
let calculation_modes = [RedGreenBlueAlpha::Red, RedGreenBlueAlpha::Green, RedGreenBlueAlpha::Blue, RedGreenBlueAlpha::Alpha];
|
||||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||||
for method in calculation_modes {
|
for method in calculation_modes {
|
||||||
|
|
@ -511,7 +561,11 @@ fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
||||||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::NoiseType(noise_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::NoiseType(noise_type)) = &input.as_non_exposed_value() {
|
||||||
let entries = NoiseType::list()
|
let entries = NoiseType::list()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|noise_type| {
|
.map(|noise_type| {
|
||||||
|
|
@ -533,7 +587,11 @@ fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name:
|
||||||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::FractalType(fractal_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::FractalType(fractal_type)) = &input.as_non_exposed_value() {
|
||||||
let entries = FractalType::list()
|
let entries = FractalType::list()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|fractal_type| {
|
.map(|fractal_type| {
|
||||||
|
|
@ -555,7 +613,11 @@ fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
||||||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::CellularDistanceFunction(cellular_distance_function)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::CellularDistanceFunction(cellular_distance_function)) = &input.as_non_exposed_value() {
|
||||||
let entries = CellularDistanceFunction::list()
|
let entries = CellularDistanceFunction::list()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|cellular_distance_function| {
|
.map(|cellular_distance_function| {
|
||||||
|
|
@ -580,7 +642,11 @@ fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, ind
|
||||||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::CellularReturnType(cellular_return_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::CellularReturnType(cellular_return_type)) = &input.as_non_exposed_value() {
|
||||||
let entries = CellularReturnType::list()
|
let entries = CellularReturnType::list()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|cellular_return_type| {
|
.map(|cellular_return_type| {
|
||||||
|
|
@ -602,7 +668,11 @@ fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index: us
|
||||||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::DomainWarpType(domain_warp_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::DomainWarpType(domain_warp_type)) = &input.as_non_exposed_value() {
|
||||||
let entries = DomainWarpType::list()
|
let entries = DomainWarpType::list()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|domain_warp_type| {
|
.map(|domain_warp_type| {
|
||||||
|
|
@ -624,7 +694,11 @@ fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
||||||
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
// TODO: Generalize this instead of using a separate function per dropdown menu enum
|
||||||
fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::BlendMode(blend_mode)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::BlendMode(blend_mode)) = &input.as_non_exposed_value() {
|
||||||
let entries = BlendMode::list_svg_subset()
|
let entries = BlendMode::list_svg_subset()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|category| {
|
.map(|category| {
|
||||||
|
|
@ -653,7 +727,11 @@ fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name:
|
||||||
// TODO: Generalize this for all dropdowns (also see blend_mode and channel_extration)
|
// TODO: Generalize this for all dropdowns (also see blend_mode and channel_extration)
|
||||||
fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::LuminanceCalculation(calculation)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::LuminanceCalculation(calculation)) = &input.as_non_exposed_value() {
|
||||||
let calculation_modes = LuminanceCalculation::list();
|
let calculation_modes = LuminanceCalculation::list();
|
||||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||||
for method in calculation_modes {
|
for method in calculation_modes {
|
||||||
|
|
@ -677,7 +755,11 @@ fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: u
|
||||||
fn boolean_operation_radio_buttons(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn boolean_operation_radio_buttons(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
|
||||||
if let Some(&TaggedValue::BooleanOperation(calculation)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::BooleanOperation(calculation)) = &input.as_non_exposed_value() {
|
||||||
let operations = BooleanOperation::list();
|
let operations = BooleanOperation::list();
|
||||||
let icons = BooleanOperation::icons();
|
let icons = BooleanOperation::icons();
|
||||||
let mut entries = Vec::with_capacity(operations.len());
|
let mut entries = Vec::with_capacity(operations.len());
|
||||||
|
|
@ -702,7 +784,11 @@ fn boolean_operation_radio_buttons(document_node: &DocumentNode, node_id: NodeId
|
||||||
|
|
||||||
fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::LineCap(line_cap)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::LineCap(line_cap)) = &input.as_non_exposed_value() {
|
||||||
let entries = [("Butt", LineCap::Butt), ("Round", LineCap::Round), ("Square", LineCap::Square)]
|
let entries = [("Butt", LineCap::Butt), ("Round", LineCap::Round), ("Square", LineCap::Square)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(name, val)| {
|
.map(|(name, val)| {
|
||||||
|
|
@ -723,7 +809,11 @@ fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
||||||
|
|
||||||
fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
if let Some(&TaggedValue::LineJoin(line_join)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::LineJoin(line_join)) = &input.as_non_exposed_value() {
|
||||||
let entries = [("Miter", LineJoin::Miter), ("Bevel", LineJoin::Bevel), ("Round", LineJoin::Round)]
|
let entries = [("Miter", LineJoin::Miter), ("Bevel", LineJoin::Bevel), ("Round", LineJoin::Round)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(name, val)| {
|
.map(|(name, val)| {
|
||||||
|
|
@ -742,7 +832,7 @@ fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
||||||
LayoutGroup::Row { widgets }
|
LayoutGroup::Row { widgets }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, color_props: ColorButton, blank_assist: bool) -> LayoutGroup {
|
fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, color_button: ColorButton, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
|
||||||
// Return early with just the label if the input is exposed to the graph, meaning we don't want to show the color picker widget in the Properties panel
|
// Return early with just the label if the input is exposed to the graph, meaning we don't want to show the color picker widget in the Properties panel
|
||||||
|
|
@ -754,14 +844,14 @@ fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
||||||
|
|
||||||
match &**tagged_value {
|
match &**tagged_value {
|
||||||
TaggedValue::Color(color) => widgets.push(
|
TaggedValue::Color(color) => widgets.push(
|
||||||
color_props
|
color_button
|
||||||
.value(FillChoice::Solid(*color))
|
.value(FillChoice::Solid(*color))
|
||||||
.on_update(update_value(|x: &ColorButton| TaggedValue::Color(x.value.as_solid().unwrap_or_default()), node_id, index))
|
.on_update(update_value(|x: &ColorButton| TaggedValue::Color(x.value.as_solid().unwrap_or_default()), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
),
|
),
|
||||||
TaggedValue::OptionalColor(color) => widgets.push(
|
TaggedValue::OptionalColor(color) => widgets.push(
|
||||||
color_props
|
color_button
|
||||||
.value(match color {
|
.value(match color {
|
||||||
Some(color) => FillChoice::Solid(*color),
|
Some(color) => FillChoice::Solid(*color),
|
||||||
None => FillChoice::None,
|
None => FillChoice::None,
|
||||||
|
|
@ -771,7 +861,7 @@ fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
),
|
),
|
||||||
TaggedValue::GradientStops(ref x) => widgets.push(
|
TaggedValue::GradientStops(ref x) => widgets.push(
|
||||||
color_props
|
color_button
|
||||||
.value(FillChoice::Gradient(x.clone()))
|
.value(FillChoice::Gradient(x.clone()))
|
||||||
.on_update(update_value(
|
.on_update(update_value(
|
||||||
|x: &ColorButton| TaggedValue::GradientStops(x.value.as_gradient().cloned().unwrap_or_default()),
|
|x: &ColorButton| TaggedValue::GradientStops(x.value.as_gradient().cloned().unwrap_or_default()),
|
||||||
|
|
@ -790,7 +880,11 @@ fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
||||||
fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
|
||||||
|
|
||||||
if let Some(TaggedValue::Curve(curve)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(TaggedValue::Curve(curve)) = &input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
CurveInput::new(curve.clone())
|
CurveInput::new(curve.clone())
|
||||||
|
|
@ -804,7 +898,11 @@ fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
||||||
|
|
||||||
fn centroid_widget(document_node: &DocumentNode, node_id: NodeId, index: usize) -> LayoutGroup {
|
fn centroid_widget(document_node: &DocumentNode, node_id: NodeId, index: usize) -> LayoutGroup {
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, "Centroid Type", FrontendGraphDataType::General, true);
|
let mut widgets = start_widgets(document_node, node_id, index, "Centroid Type", FrontendGraphDataType::General, true);
|
||||||
if let Some(&TaggedValue::CentroidType(centroid_type)) = &document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return LayoutGroup::Row { widgets: vec![] };
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::CentroidType(centroid_type)) = &input.as_non_exposed_value() {
|
||||||
let entries = vec![
|
let entries = vec![
|
||||||
RadioEntryData::new("area")
|
RadioEntryData::new("area")
|
||||||
.label("Area")
|
.label("Area")
|
||||||
|
|
@ -902,7 +1000,7 @@ pub fn color_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn load_image_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let url = text_widget(document_node, node_id, 1, "Url", true);
|
let url = text_widget(document_node, node_id, 1, "URL", true);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: url }]
|
vec![LayoutGroup::Row { widgets: url }]
|
||||||
}
|
}
|
||||||
|
|
@ -1137,40 +1235,63 @@ pub fn threshold_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gradient_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn gradient_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let gradient_input = 1;
|
let gradient_index = 1;
|
||||||
let reverse_input = 2;
|
let reverse_index = 2;
|
||||||
|
|
||||||
let gradient = if let Some(TaggedValue::GradientStops(gradient)) = &document_node.inputs[gradient_input].as_value() {
|
let gradient_row = color_widget(document_node, node_id, gradient_index, "Gradient", ColorButton::default().allow_none(false), true);
|
||||||
gradient.clone()
|
let reverse_row = bool_widget(document_node, node_id, reverse_index, "Reverse", true);
|
||||||
|
|
||||||
|
vec![gradient_row, LayoutGroup::Row { widgets: reverse_row }]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_colors_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
|
let fill_index = 1;
|
||||||
|
let stroke_index = 2;
|
||||||
|
let gradient_index = 3;
|
||||||
|
let reverse_index = 4;
|
||||||
|
let randomize_index = 5;
|
||||||
|
let repeat_every_index = 6;
|
||||||
|
|
||||||
|
let fill_row = bool_widget(document_node, node_id, fill_index, "Fill", true);
|
||||||
|
let stroke_row = bool_widget(document_node, node_id, stroke_index, "Stroke", true);
|
||||||
|
let gradient_row = color_widget(document_node, node_id, gradient_index, "Gradient", ColorButton::default().allow_none(false), true);
|
||||||
|
let reverse_row = bool_widget(document_node, node_id, reverse_index, "Reverse", true);
|
||||||
|
let randomize_row = bool_widget(document_node, node_id, randomize_index, "Randomize", true);
|
||||||
|
let randomize_enabled = if let Some(&TaggedValue::Bool(randomize_enabled)) = &document_node.inputs[randomize_index].as_value() {
|
||||||
|
randomize_enabled
|
||||||
} else {
|
} else {
|
||||||
return vec![LayoutGroup::Row { widgets: vec![] }];
|
false
|
||||||
};
|
};
|
||||||
let mut gradient_row = vec![TextLabel::new("Gradient").widget_holder()];
|
let repeat_every_row = number_widget(
|
||||||
add_blank_assist(&mut gradient_row);
|
document_node,
|
||||||
gradient_row.extend([
|
node_id,
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
repeat_every_index,
|
||||||
ColorButton::default()
|
"Repeat Every",
|
||||||
.allow_none(false)
|
NumberInput::default().min(0.).int().disabled(randomize_enabled),
|
||||||
.value(FillChoice::Gradient(gradient))
|
true,
|
||||||
.on_update(move |x: &ColorButton| {
|
);
|
||||||
NodeGraphMessage::SetInputValue {
|
|
||||||
node_id,
|
|
||||||
input_index: gradient_input,
|
|
||||||
value: TaggedValue::GradientStops(x.value.as_gradient().unwrap().clone()),
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.on_commit(commit_value)
|
|
||||||
.widget_holder(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let reverse_row = bool_widget(document_node, node_id, reverse_input, "Reverse", true);
|
vec![
|
||||||
|
LayoutGroup::Row { widgets: fill_row },
|
||||||
vec![LayoutGroup::Row { widgets: gradient_row }, LayoutGroup::Row { widgets: reverse_row }]
|
LayoutGroup::Row { widgets: stroke_row },
|
||||||
|
gradient_row,
|
||||||
|
LayoutGroup::Row { widgets: reverse_row },
|
||||||
|
LayoutGroup::Row { widgets: randomize_row },
|
||||||
|
LayoutGroup::Row { widgets: repeat_every_row },
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vibrance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub fn vibrance_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let vibrance = number_widget(document_node, node_id, 1, "Vibrance", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
|
let vibrance_index = 1;
|
||||||
|
|
||||||
|
let vibrance = number_widget(
|
||||||
|
document_node,
|
||||||
|
node_id,
|
||||||
|
vibrance_index,
|
||||||
|
"Vibrance",
|
||||||
|
NumberInput::default().mode_range().min(-100.).max(100.).unit("%"),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
vec![LayoutGroup::Row { widgets: vibrance }]
|
vec![LayoutGroup::Row { widgets: vibrance }]
|
||||||
}
|
}
|
||||||
|
|
@ -1189,7 +1310,12 @@ pub fn channel_mixer_properties(document_node: &DocumentNode, node_id: NodeId, _
|
||||||
let output_channel_index = 18;
|
let output_channel_index = 18;
|
||||||
let mut output_channel = vec![TextLabel::new("Output Channel").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
let mut output_channel = vec![TextLabel::new("Output Channel").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
||||||
add_blank_assist(&mut output_channel);
|
add_blank_assist(&mut output_channel);
|
||||||
if let Some(&TaggedValue::RedGreenBlue(choice)) = &document_node.inputs[output_channel_index].as_non_exposed_value() {
|
|
||||||
|
let Some(input) = document_node.inputs.get(output_channel_index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::RedGreenBlue(choice)) = input.as_non_exposed_value() {
|
||||||
let entries = vec![
|
let entries = vec![
|
||||||
RadioEntryData::new(format!("{:?}", RedGreenBlue::Red))
|
RadioEntryData::new(format!("{:?}", RedGreenBlue::Red))
|
||||||
.label(RedGreenBlue::Red.to_string())
|
.label(RedGreenBlue::Red.to_string())
|
||||||
|
|
@ -1206,6 +1332,7 @@ pub fn channel_mixer_properties(document_node: &DocumentNode, node_id: NodeId, _
|
||||||
];
|
];
|
||||||
output_channel.extend([RadioInput::new(entries).selected_index(Some(choice as u32)).widget_holder()]);
|
output_channel.extend([RadioInput::new(entries).selected_index(Some(choice as u32)).widget_holder()]);
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_output_channel = if let Some(&TaggedValue::RedGreenBlue(choice)) = &document_node.inputs[output_channel_index].as_value() {
|
let is_output_channel = if let Some(&TaggedValue::RedGreenBlue(choice)) = &document_node.inputs[output_channel_index].as_value() {
|
||||||
choice
|
choice
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1274,7 +1401,12 @@ pub fn selective_color_properties(document_node: &DocumentNode, node_id: NodeId,
|
||||||
let colors_index = 38;
|
let colors_index = 38;
|
||||||
let mut colors = vec![TextLabel::new("Colors").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
let mut colors = vec![TextLabel::new("Colors").widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()];
|
||||||
add_blank_assist(&mut colors);
|
add_blank_assist(&mut colors);
|
||||||
if let Some(&TaggedValue::SelectiveColorChoice(choice)) = &document_node.inputs[colors_index].as_non_exposed_value() {
|
|
||||||
|
let Some(input) = document_node.inputs.get(colors_index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::SelectiveColorChoice(choice)) = input.as_non_exposed_value() {
|
||||||
use SelectiveColorChoice::*;
|
use SelectiveColorChoice::*;
|
||||||
let entries = [[Reds, Yellows, Greens, Cyans, Blues, Magentas].as_slice(), [Whites, Neutrals, Blacks].as_slice()]
|
let entries = [[Reds, Yellows, Greens, Cyans, Blues, Magentas].as_slice(), [Whites, Neutrals, Blacks].as_slice()]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -1291,7 +1423,8 @@ pub fn selective_color_properties(document_node: &DocumentNode, node_id: NodeId,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
colors.extend([DropdownInput::new(entries).selected_index(Some(choice as u32)).widget_holder()]);
|
colors.extend([DropdownInput::new(entries).selected_index(Some(choice as u32)).widget_holder()]);
|
||||||
};
|
}
|
||||||
|
|
||||||
let colors_choice_index = if let Some(&TaggedValue::SelectiveColorChoice(choice)) = &document_node.inputs[colors_index].as_value() {
|
let colors_choice_index = if let Some(&TaggedValue::SelectiveColorChoice(choice)) = &document_node.inputs[colors_index].as_value() {
|
||||||
choice
|
choice
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1320,7 +1453,12 @@ pub fn selective_color_properties(document_node: &DocumentNode, node_id: NodeId,
|
||||||
let mode_index = 1;
|
let mode_index = 1;
|
||||||
let mut mode = start_widgets(document_node, node_id, mode_index, "Mode", FrontendGraphDataType::General, true);
|
let mut mode = start_widgets(document_node, node_id, mode_index, "Mode", FrontendGraphDataType::General, true);
|
||||||
mode.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
mode.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
if let Some(&TaggedValue::RelativeAbsolute(relative_or_absolute)) = &document_node.inputs[mode_index].as_non_exposed_value() {
|
|
||||||
|
let Some(input) = document_node.inputs.get(mode_index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::RelativeAbsolute(relative_or_absolute)) = &input.as_non_exposed_value() {
|
||||||
let entries = vec![
|
let entries = vec![
|
||||||
RadioEntryData::new("relative")
|
RadioEntryData::new("relative")
|
||||||
.label("Relative")
|
.label("Relative")
|
||||||
|
|
@ -1490,14 +1628,22 @@ pub fn rectangle_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
||||||
corner_radius_row_2.push(TextLabel::new("").widget_holder());
|
corner_radius_row_2.push(TextLabel::new("").widget_holder());
|
||||||
add_blank_assist(&mut corner_radius_row_2);
|
add_blank_assist(&mut corner_radius_row_2);
|
||||||
|
|
||||||
if let Some(&TaggedValue::Bool(is_individual)) = &document_node.inputs[corner_rounding_type_index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(corner_rounding_type_index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::Bool(is_individual)) = &input.as_non_exposed_value() {
|
||||||
// Values
|
// Values
|
||||||
let uniform_val = match document_node.inputs[corner_radius_index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(corner_radius_index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
let uniform_val = match input.as_non_exposed_value() {
|
||||||
Some(TaggedValue::F64(x)) => *x,
|
Some(TaggedValue::F64(x)) => *x,
|
||||||
Some(TaggedValue::F64Array4(x)) => x[0],
|
Some(TaggedValue::F64Array4(x)) => x[0],
|
||||||
_ => 0.,
|
_ => 0.,
|
||||||
};
|
};
|
||||||
let individual_val = match document_node.inputs[corner_radius_index].as_non_exposed_value() {
|
let individual_val = match input.as_non_exposed_value() {
|
||||||
Some(&TaggedValue::F64Array4(x)) => x,
|
Some(&TaggedValue::F64Array4(x)) => x,
|
||||||
Some(&TaggedValue::F64(x)) => [x; 4],
|
Some(&TaggedValue::F64(x)) => [x; 4],
|
||||||
_ => [0.; 4],
|
_ => [0.; 4],
|
||||||
|
|
@ -1624,7 +1770,11 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
||||||
|
|
||||||
let mut widgets = start_widgets(document_node, node_id, index, "Rotation", FrontendGraphDataType::Number, true);
|
let mut widgets = start_widgets(document_node, node_id, index, "Rotation", FrontendGraphDataType::Number, true);
|
||||||
|
|
||||||
if let Some(&TaggedValue::F64(val)) = document_node.inputs[index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::F64(val)) = input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
NumberInput::new(Some(val.to_degrees()))
|
NumberInput::new(Some(val.to_degrees()))
|
||||||
|
|
@ -1839,7 +1989,11 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
let seed = {
|
let seed = {
|
||||||
let mut widgets = start_widgets(document_node, node_id, seed_index, "Seed", FrontendGraphDataType::Number, false);
|
let mut widgets = start_widgets(document_node, node_id, seed_index, "Seed", FrontendGraphDataType::Number, false);
|
||||||
|
|
||||||
if let Some(&TaggedValue::F64(seed)) = &document_node.inputs[seed_index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(seed_index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::F64(seed)) = &input.as_non_exposed_value() {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
IconButton::new("Regenerate", 24)
|
IconButton::new("Regenerate", 24)
|
||||||
|
|
@ -1906,7 +2060,11 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
DVec2::new(x as f64, y as f64)
|
DVec2::new(x as f64, y as f64)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(&TaggedValue::OptionalDVec2(vec2)) = &document_node.inputs[resolution_index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(resolution_index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::OptionalDVec2(vec2)) = &input.as_non_exposed_value() {
|
||||||
let dimensions_is_auto = vec2.is_none();
|
let dimensions_is_auto = vec2.is_none();
|
||||||
let vec2 = vec2.unwrap_or_else(|| round((image_size.0 as f64, image_size.1 as f64).into()));
|
let vec2 = vec2.unwrap_or_else(|| round((image_size.0 as f64, image_size.1 as f64).into()));
|
||||||
|
|
||||||
|
|
@ -1985,7 +2143,11 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
let sampling_method = {
|
let sampling_method = {
|
||||||
let mut widgets = start_widgets(document_node, node_id, sampling_method_index, "Sampling Method", FrontendGraphDataType::General, true);
|
let mut widgets = start_widgets(document_node, node_id, sampling_method_index, "Sampling Method", FrontendGraphDataType::General, true);
|
||||||
|
|
||||||
if let Some(&TaggedValue::ImaginateSamplingMethod(sampling_method)) = &document_node.inputs[sampling_method_index].as_non_exposed_value() {
|
let Some(input) = document_node.inputs.get(sampling_method_index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
if let Some(&TaggedValue::ImaginateSamplingMethod(sampling_method)) = &input.as_non_exposed_value() {
|
||||||
let sampling_methods = ImaginateSamplingMethod::list();
|
let sampling_methods = ImaginateSamplingMethod::list();
|
||||||
let mut entries = Vec::with_capacity(sampling_methods.len());
|
let mut entries = Vec::with_capacity(sampling_methods.len());
|
||||||
for method in sampling_methods {
|
for method in sampling_methods {
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,22 @@ impl core::hash::Hash for GraphicGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GraphicGroup {
|
||||||
|
pub const EMPTY: Self = Self {
|
||||||
|
elements: Vec::new(),
|
||||||
|
transform: DAffine2::IDENTITY,
|
||||||
|
alpha_blending: AlphaBlending::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn new(elements: Vec<GraphicElement>) -> Self {
|
||||||
|
Self {
|
||||||
|
elements,
|
||||||
|
transform: DAffine2::IDENTITY,
|
||||||
|
alpha_blending: AlphaBlending::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The possible forms of graphical content held in a Vec by the `elements` field of [`GraphicElement`].
|
/// The possible forms of graphical content held in a Vec by the `elements` field of [`GraphicElement`].
|
||||||
/// Can be another recursively nested [`GraphicGroup`], a [`VectorData`] shape, an [`ImageFrame`], or an [`Artboard`].
|
/// Can be another recursively nested [`GraphicGroup`], a [`VectorData`] shape, an [`ImageFrame`], or an [`Artboard`].
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
|
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
|
||||||
|
|
@ -74,6 +90,50 @@ impl Default for GraphicElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GraphicElement {
|
||||||
|
pub fn as_group(&self) -> Option<&GraphicGroup> {
|
||||||
|
match self {
|
||||||
|
GraphicElement::GraphicGroup(group) => Some(group),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_group_mut(&mut self) -> Option<&mut GraphicGroup> {
|
||||||
|
match self {
|
||||||
|
GraphicElement::GraphicGroup(group) => Some(group),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_vector_data(&self) -> Option<&VectorData> {
|
||||||
|
match self {
|
||||||
|
GraphicElement::VectorData(data) => Some(data),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_vector_data_mut(&mut self) -> Option<&mut VectorData> {
|
||||||
|
match self {
|
||||||
|
GraphicElement::VectorData(data) => Some(data),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_raster(&self) -> Option<&Raster> {
|
||||||
|
match self {
|
||||||
|
GraphicElement::Raster(raster) => Some(raster),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_raster_mut(&mut self) -> Option<&mut Raster> {
|
||||||
|
match self {
|
||||||
|
GraphicElement::Raster(raster) => Some(raster),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
|
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
|
||||||
pub enum Raster {
|
pub enum Raster {
|
||||||
/// A bitmap image with a finite position and extent, equivalent to the SVG <image> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
|
/// A bitmap image with a finite position and extent, equivalent to the SVG <image> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
|
||||||
|
|
@ -301,11 +361,3 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GraphicGroup {
|
|
||||||
pub const EMPTY: Self = Self {
|
|
||||||
elements: Vec::new(),
|
|
||||||
transform: DAffine2::IDENTITY,
|
|
||||||
alpha_blending: AlphaBlending::new(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ impl GradientStops {
|
||||||
|
|
||||||
Color::BLACK
|
Color::BLACK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reversed(&self) -> Self {
|
||||||
|
Self(self.0.iter().rev().map(|(position, color)| (1. - position, *color)).collect())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A gradient fill.
|
/// A gradient fill.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::misc::CentroidType;
|
use super::misc::CentroidType;
|
||||||
use super::style::{Fill, Stroke};
|
use super::style::{Fill, GradientStops, Stroke};
|
||||||
use super::{PointId, SegmentId, StrokeId, VectorData};
|
use super::{PointId, SegmentId, StrokeId, VectorData};
|
||||||
use crate::renderer::GraphicElementRendered;
|
use crate::renderer::GraphicElementRendered;
|
||||||
use crate::transform::{Footprint, Transform, TransformMut};
|
use crate::transform::{Footprint, Transform, TransformMut};
|
||||||
|
|
@ -9,6 +9,78 @@ use bezier_rs::{Cap, Join, Subpath, SubpathTValue, TValue};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct AssignColorsNode<Fill, Stroke, Gradient, Reverse, Randomize, RepeatEvery> {
|
||||||
|
fill: Fill,
|
||||||
|
stroke: Stroke,
|
||||||
|
gradient: Gradient,
|
||||||
|
reverse: Reverse,
|
||||||
|
randomize: Randomize,
|
||||||
|
repeat_every: RepeatEvery,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node_fn(AssignColorsNode)]
|
||||||
|
fn assign_colors_node(group: GraphicGroup, fill: bool, stroke: bool, gradient: GradientStops, reverse: bool, randomize: bool, repeat_every: u32) -> GraphicGroup {
|
||||||
|
let mut group = group;
|
||||||
|
let vector_data_list: Vec<_> = group.iter_mut().filter_map(|element| element.as_vector_data_mut()).collect();
|
||||||
|
let list = (vector_data_list.len(), vector_data_list.into_iter());
|
||||||
|
|
||||||
|
assign_colors(list, fill, stroke, gradient, reverse, randomize, repeat_every);
|
||||||
|
|
||||||
|
group
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node_impl(AssignColorsNode)]
|
||||||
|
fn assign_colors_node(vector_data: VectorData, fill: bool, stroke: bool, gradient: GradientStops, reverse: bool, randomize: bool, repeat_every: u32) -> GraphicGroup {
|
||||||
|
let mut vector_data_list: Vec<_> = vector_data
|
||||||
|
.region_bezier_paths()
|
||||||
|
.map(|(_, subpath)| {
|
||||||
|
let mut vector = VectorData::from_subpath(subpath);
|
||||||
|
|
||||||
|
vector.style = vector_data.style.clone();
|
||||||
|
|
||||||
|
crate::GraphicElement::VectorData(Box::new(vector))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let list = (vector_data_list.len(), vector_data_list.iter_mut().map(|element| element.as_vector_data_mut().unwrap()));
|
||||||
|
|
||||||
|
assign_colors(list, fill, stroke, gradient, reverse, randomize, repeat_every);
|
||||||
|
|
||||||
|
let mut group = GraphicGroup::new(vector_data_list);
|
||||||
|
group.transform = vector_data.transform;
|
||||||
|
group.alpha_blending = vector_data.alpha_blending;
|
||||||
|
|
||||||
|
group
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_colors<'a>((length, vector_data): (usize, impl Iterator<Item = &'a mut VectorData>), fill: bool, stroke: bool, gradient: GradientStops, reverse: bool, randomize: bool, repeat_every: u32) {
|
||||||
|
let gradient = if reverse { gradient.reversed() } else { gradient };
|
||||||
|
|
||||||
|
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
|
||||||
|
|
||||||
|
for (i, vector_data) in vector_data.enumerate() {
|
||||||
|
let factor = match randomize {
|
||||||
|
true => rng.gen::<f64>(),
|
||||||
|
false => match repeat_every {
|
||||||
|
0 => i as f64 / (length - 1) as f64,
|
||||||
|
1 => 0.,
|
||||||
|
_ => i as f64 % repeat_every as f64 / (repeat_every - 1) as f64,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let color = gradient.evalute(factor);
|
||||||
|
|
||||||
|
if fill {
|
||||||
|
vector_data.style.set_fill(Fill::Solid(color));
|
||||||
|
}
|
||||||
|
if stroke {
|
||||||
|
if let Some(stroke) = vector_data.style.stroke().and_then(|stroke| stroke.with_color(&Some(color))) {
|
||||||
|
vector_data.style.set_stroke(stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct SetFillNode<Fill> {
|
pub struct SetFillNode<Fill> {
|
||||||
fill: Fill,
|
fill: Fill,
|
||||||
|
|
|
||||||
|
|
@ -679,6 +679,8 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||||
register_node!(graphene_core::vector::SetFillNode<_>, input: VectorData, params: [Color]),
|
register_node!(graphene_core::vector::SetFillNode<_>, input: VectorData, params: [Color]),
|
||||||
register_node!(graphene_core::vector::SetFillNode<_>, input: VectorData, params: [Option<Color>]),
|
register_node!(graphene_core::vector::SetFillNode<_>, input: VectorData, params: [Option<Color>]),
|
||||||
register_node!(graphene_core::vector::SetFillNode<_>, input: VectorData, params: [graphene_std::vector::style::Gradient]),
|
register_node!(graphene_core::vector::SetFillNode<_>, input: VectorData, params: [graphene_std::vector::style::Gradient]),
|
||||||
|
register_node!(graphene_core::vector::AssignColorsNode<_, _, _, _, _, _>, input: GraphicGroup, params: [bool, bool, graphene_std::vector::style::GradientStops, bool, bool, u32]),
|
||||||
|
register_node!(graphene_core::vector::AssignColorsNode<_, _, _, _, _, _>, input: VectorData, params: [bool, bool, graphene_std::vector::style::GradientStops, bool, bool, u32]),
|
||||||
register_node!(graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>, input: VectorData, params: [Option<graphene_core::Color>, f64, Vec<f64>, f64, graphene_core::vector::style::LineCap, graphene_core::vector::style::LineJoin, f64]),
|
register_node!(graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>, input: VectorData, params: [Option<graphene_core::Color>, f64, Vec<f64>, f64, graphene_core::vector::style::LineCap, graphene_core::vector::style::LineJoin, f64]),
|
||||||
register_node!(graphene_core::vector::RepeatNode<_, _, _>, input: VectorData, params: [DVec2, f64, u32]),
|
register_node!(graphene_core::vector::RepeatNode<_, _, _>, input: VectorData, params: [DVec2, f64, u32]),
|
||||||
register_node!(graphene_core::vector::BoundingBoxNode, input: VectorData, params: []),
|
register_node!(graphene_core::vector::BoundingBoxNode, input: VectorData, params: []),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue