Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,33 @@ jobs:
echo "All $MAX_ATTEMPTS Cloudflare Pages publish attempts failed."
exit 1

- name: 🚀 Create GitHub Deployment for "View deployment" button
if: inputs.checkout_repo == '' || inputs.checkout_repo == github.repository
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CF_URL: ${{ steps.cloudflare.outputs.url }}
run: |
if [ -z "$CF_URL" ]; then
echo "No Cloudflare URL available, skipping deployment."
exit 0
fi
DEPLOY_ID=$(gh api \
-X POST \
-H "Accept: application/vnd.github+json" \
repos/${{ github.repository }}/deployments \
-f ref="$(git rev-parse HEAD)" \
-f environment="graphite-dev (Preview)" \
-F auto_merge=false \
-f required_contexts="[]" \
--jq '.id')
gh api \
-X POST \
-H "Accept: application/vnd.github+json" \
repos/${{ github.repository }}/deployments/$DEPLOY_ID/statuses \
-f state=success \
-f environment_url="$CF_URL" \
-f log_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"

- name: 💬 Comment with the build link
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,21 @@ use kurbo::{CubicBez, DEFAULT_ACCURACY, Line, ParamCurve, PathSeg, Point, QuadBe

/// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable.
pub fn should_extend(document: &DocumentMessageHandler, goal: DVec2, tolerance: f64, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Option<(LayerNodeIdentifier, PointId, DVec2)> {
closest_point(document, goal, tolerance, layers, |_| false)
let mut best = None;
let mut best_distance_squared = tolerance * tolerance;
for layer in layers {
let viewspace = document.metadata().transform_to_viewport(layer);
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
for id in vector.anchor_endpoints() {
let Some(point) = vector.point_domain.position_from_id(id) else { continue };
let distance_squared = viewspace.transform_point2(point).distance_squared(goal);
if distance_squared < best_distance_squared {
best = Some((layer, id, point));
best_distance_squared = distance_squared;
}
}
}
best
}

/// Determine the closest point to the goal point under max_distance.
Expand Down
99 changes: 70 additions & 29 deletions node-graph/nodes/vector/src/generator_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,40 +42,66 @@ impl CornerRadius for [f64; 4] {
/// Generates a circle shape with a chosen radius.
#[node_macro::node(category("Vector: Shape"))]
fn circle(
_: impl Ctx,
_primary: (),
#[unit(" px")]
#[default(50.)]
radius: f64,
_: impl Ctx,
_primary: (),
#[unit(" px")]
#[default(50.)]
radius: f64,
) -> Table<Vector> {
let radius = radius.abs();
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius))))
let radius = radius.abs();
// 1. Create the vector
let mut circle = Vector::from_subpath(subpath::Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius)));

// Created the collinear_manipulators so that all handles are linked, making it easier to edit the circle as a circle instead of a 4 point shape.
let ids = circle.segment_domain.ids();
let len = ids.len();
for i in 0..len {
circle.colinear_manipulators.push([
HandleId::end(ids[i]),
HandleId::primary(ids[(i + 1) % len]),
]);
}

Table::new_from_element(circle)
}

/// Generates an arc shape forming a portion of a circle which may be open, closed, or a pie slice.
#[node_macro::node(category("Vector: Shape"))]
fn arc(
_: impl Ctx,
_primary: (),
#[unit(" px")]
#[default(50.)]
radius: f64,
start_angle: Angle,
#[default(270.)]
#[range((0., 360.))]
sweep_angle: Angle,
arc_type: ArcType,
_: impl Ctx,
_primary: (),
#[unit(" px")]
#[default(50.)]
radius: f64,
start_angle: Angle,
#[default(270.)]
#[range((0., 360.))]
sweep_angle: Angle,
arc_type: ArcType,
) -> Table<Vector> {
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_arc(
radius,
start_angle / 360. * std::f64::consts::TAU,
sweep_angle / 360. * std::f64::consts::TAU,
match arc_type {
ArcType::Open => subpath::ArcType::Open,
ArcType::Closed => subpath::ArcType::Closed,
ArcType::PieSlice => subpath::ArcType::PieSlice,
},
)))
let mut arc_vector = Vector::from_subpath(subpath::Subpath::new_arc(
radius,
start_angle / 360. * std::f64::consts::TAU,
sweep_angle / 360. * std::f64::consts::TAU,
match arc_type {
ArcType::Open => subpath::ArcType::Open,
ArcType::Closed => subpath::ArcType::Closed,
ArcType::PieSlice => subpath::ArcType::PieSlice,
},
));

// 2. Link handles only if both adjacent segments are cubic beziers
let len = arc_vector.segment_domain.ids().len();
for i in 0..len.saturating_sub(1) {
if arc_vector.segment_domain.handles()[i].is_cubic() && arc_vector.segment_domain.handles()[i + 1].is_cubic() {
arc_vector.colinear_manipulators.push([
HandleId::end(arc_vector.segment_domain.ids()[i]),
HandleId::primary(arc_vector.segment_domain.ids()[i + 1])
]);
}
}

Table::new_from_element(arc_vector)
}

/// Generates a spiral shape that winds from an inner to an outer radius.
Expand All @@ -90,14 +116,29 @@ fn spiral(
#[default(25)] outer_radius: f64,
#[default(90.)] angular_resolution: f64,
) -> Table<Vector> {
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_spiral(

let mut spiral_vector = Vector::from_subpath(subpath::Subpath::new_spiral(
inner_radius,
outer_radius,
turns,
start_angle.to_radians(),
angular_resolution.to_radians(),
spiral_type,
)))
));


let len = spiral_vector.segment_domain.ids().len();
for i in 0..len.saturating_sub(1) {
// Ensure both segments meeting at the anchor point are cubic beziers
if spiral_vector.segment_domain.handles()[i].is_cubic() && spiral_vector.segment_domain.handles()[i + 1].is_cubic() {
spiral_vector.colinear_manipulators.push([
HandleId::end(spiral_vector.segment_domain.ids()[i]),
HandleId::primary(spiral_vector.segment_domain.ids()[i + 1])
]);
}
}

Table::new_from_element(spiral_vector)
}

/// Generates an ellipse shape (an oval or stretched circle) with the chosen radii.
Expand Down