From 3287b2d1e5f11e715e627f2996f8665f6853b78e Mon Sep 17 00:00:00 2001 From: Oliver Davies Date: Mon, 28 Apr 2025 00:31:58 -0700 Subject: [PATCH] Add Outer Path, Fix mscv package.json bug --- frontend/package-lock.json | 34 ++++---- frontend/package.json | 9 ++- node-graph/gcore/src/vector/vector_nodes.rs | 90 ++++++++++++++++++++- 3 files changed, 112 insertions(+), 21 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6a1c360d1c..a610d34f25 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -7,7 +7,7 @@ "name": "graphite-web-frontend", "license": "Apache-2.0", "dependencies": { - "@tauri-apps/api": "^2.2.0", + "@rollup/rollup-win32-x64-msvc": "^4.40.1", "class-transformer": "^0.5.1", "idb-keyval": "^6.2.1", "reflect-metadata": "^0.2.2" @@ -989,15 +989,13 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.9", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", - "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", + "version": "4.40.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", + "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", - "optional": true, "os": [ "win32" ] @@ -1050,16 +1048,6 @@ "vite": "^5.0.0" } }, - "node_modules/@tauri-apps/api": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.2.0.tgz", - "integrity": "sha512-R8epOeZl1eJEl603aUMIGb4RXlhPjpgxbGVEaqY+0G5JG9vzV/clNlzTeqc+NLYXVqXcn8mb4c5b9pJIUDEyAg==", - "license": "Apache-2.0 OR MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/tauri" - } - }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -4668,6 +4656,20 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/rollup/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 838987db0b..8fa1df85a0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,6 +34,7 @@ "reflect-metadata": "^0.2.2" }, "devDependencies": { + "@rollup/rollup-win32-x64-msvc": "^4.40.1", "@sveltejs/vite-plugin-svelte": "^3.1.2", "@types/node": "^22.6.1", "@typescript-eslint/eslint-plugin": "^8.7.0", @@ -46,17 +47,17 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-svelte": "^2.44.0", "postcss": "^8.4.47", - "prettier": "^3.3.3", "prettier-plugin-svelte": "^3.2.6", + "prettier": "^3.3.3", "process": "^0.11.10", "rollup-plugin-license": "^3.5.3", "sass": "1.78.0", - "svelte": "^4.2.19", "svelte-preprocess": "^6.0.2", + "svelte": "^4.2.19", "ts-node": "^10.9.2", "typescript": "^5.6.2", - "vite": "^5.4.14", - "vite-multiple-assets": "1.3.1" + "vite-multiple-assets": "1.3.1", + "vite": "^5.4.14" }, "//": "The dev dependency for `sass` can be removed once is fixed, but meanwhile we have to", "///": "list it here and pin it to 1.78.0 so `vite` and `svelte-preprocess` use it instead of 1.79+ which spams the console with deprecation warnings.", diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index caf31619a8..ae19929cc8 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -1,7 +1,7 @@ use super::algorithms::offset_subpath::offset_subpath; use super::misc::CentroidType; use super::style::{Fill, Gradient, GradientStops, Stroke}; -use super::{PointId, RegionId, SegmentDomain, SegmentId, StrokeId, VectorData, VectorDataTable}; +use super::{PointId, SegmentDomain, SegmentId, StrokeId, VectorData, VectorDataTable}; use crate::instances::{InstanceMut, Instances}; use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, SeedValue}; use crate::renderer::GraphicElementRendered; @@ -1480,6 +1480,94 @@ async fn offset_path(_: impl Ctx, vector_data: VectorDataTable, distance: f64, l result } +#[node_macro::node(category("Vector"), path(graphene_core::vector))] +async fn outer_path(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable { + let vector_data_transform = vector_data.transform(); + let vector_data = vector_data.one_instance().instance; + + // Create a new VectorData to store our result + let mut result = VectorData::empty(); + result.style = vector_data.style.clone(); + + // Collect all closed subpaths with their indices + let subpaths: Vec<_> = vector_data + .stroke_bezier_paths() + .enumerate() + .filter(|(_, subpath)| subpath.closed()) + .map(|(i, mut subpath)| { + // Apply transform to work in world space + subpath.apply_transform(vector_data_transform); + (i, subpath) + }) + .collect(); + + if subpaths.is_empty() { + // No closed paths found, return empty result + let mut result_table = VectorDataTable::new(result); + *result_table.transform_mut() = vector_data_transform; + return result_table; + } + + // Find the outermost path: + // 1. Start by assuming all subpaths could be the outermost + let mut potential_outer_paths: Vec<_> = subpaths.iter().map(|(i, _)| *i).collect(); + + // 2. For each subpath, test against all others + for (i, subpath_i) in &subpaths { + // Skip if already eliminated + if !potential_outer_paths.contains(i) { + continue; + } + + for (j, subpath_j) in &subpaths { + // Skip comparing to self + if i == j { + continue; + } + + // Sample points from subpath_i + let total_points = 20; + let mut contained_points = 0; + + // Check several points along subpath_i to see if they're contained in subpath_j + for k in 0..total_points { + let t = k as f64 / total_points as f64; + let point = subpath_i.evaluate(bezier_rs::SubpathTValue::GlobalParametric(t)); + + if subpath_j.contains_point(point) { + contained_points += 1; + } + } + + // If most points from subpath_i are inside subpath_j, + // then subpath_i is not an outer path + if contained_points > total_points / 2 { + potential_outer_paths.retain(|&x| x != *i); + break; + } + } + } + + // 3. Among remaining potential outer paths, choose the one with the largest area + if let Some(&outer_index) = potential_outer_paths.iter().max_by(|&&a, &&b| { + let area_a = subpaths.iter().find(|(i, _)| *i == a).map(|(_, s)| s.area(None, None).abs()).unwrap_or(0.0); + let area_b = subpaths.iter().find(|(i, _)| *i == b).map(|(_, s)| s.area(None, None).abs()).unwrap_or(0.0); + area_a.partial_cmp(&area_b).unwrap_or(std::cmp::Ordering::Equal) + }) { + // Find the outer path and add it to the result + if let Some((_, outer_path)) = subpaths.iter().find(|(i, _)| *i == outer_index) { + let mut path_copy = outer_path.clone(); + path_copy.apply_transform(vector_data_transform.inverse()); + result.append_subpath(path_copy, true); + } + } + + // Create the resulting VectorDataTable + let mut result_table = VectorDataTable::new(result); + *result_table.transform_mut() = vector_data_transform; + result_table +} + #[node_macro::node(category("Vector"), path(graphene_core::vector))] async fn solidify_stroke(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable { let vector_data_transform = vector_data.transform();