Remove dead code from the unused curves adjustment widget

This commit is contained in:
Keavon Chambers
2026-06-15 00:42:38 -07:00
parent 971d1fb16b
commit 3414b32d5e
8 changed files with 1 additions and 496 deletions

View File

@@ -421,31 +421,6 @@ pub struct TextInput {
pub on_commit: WidgetCallback<()>,
}
// #[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
// #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder)]
// #[derivative(Debug, PartialEq, Default)]
// pub struct CurveInput {
// // Content
// #[widget_builder(constructor)]
// pub value: Curve,
// // Tooltips
// #[serde(rename = "tooltipLabel")]
// pub tooltip_label: String,
// #[serde(rename = "tooltipDescription")]
// pub tooltip_description: String,
// #[serde(rename = "tooltipShortcut")]
// pub tooltip_shortcut: Option<ActionShortcut>,
// // Callbacks
// #[serde(skip)]
// #[derivative(Debug = "ignore", PartialEq = "ignore")]
// pub on_update: WidgetCallback<CurveInput>,
// #[serde(skip)]
// #[derivative(Debug = "ignore", PartialEq = "ignore")]
// pub on_commit: WidgetCallback<()>,
// }
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Clone, Default, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder)]
#[derivative(Debug, PartialEq)]

View File

@@ -1162,28 +1162,6 @@ pub fn font_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup
font_widgets.into_iter().chain(style_widgets.unwrap_or_default()).collect::<Vec<_>>().into()
}
// pub fn curve_widget(parameter_widgets_info: ParameterWidgetsInfo) -> LayoutGroup {
// let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
// let mut widgets = start_widgets(parameter_widgets_info);
// let Some(document_node) = document_node else { return LayoutGroup::default() };
// 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(vec![]);
// };
// if let Some(TaggedValue::Curve(curve)) = &input.as_non_exposed_value() {
// widgets.extend_from_slice(&[
// Separator::new(SeparatorStyle::Unrelated).widget_instance(),
// CurveInput::new(curve.clone())
// .on_update(update_value(|x: &CurveInput| TaggedValue::Curve(x.value.clone()), node_id, index))
// .on_commit(commit_value)
// .widget_instance(),
// ])
// }
// LayoutGroup::row(widgets)
// }
pub fn get_document_node<'a>(node_id: NodeId, context: &'a NodePropertiesContext<'a>) -> Result<&'a DocumentNode, String> {
let network = context
.network_interface

View File

@@ -1,259 +0,0 @@
<!-- <script lang="ts">
import { createEventDispatcher } from "svelte";
import LayoutRow from "/src/components/layout/LayoutRow.svelte";
import type { Curve, CurveManipulatorGroup, ActionShortcut } from "/wrapper/pkg/graphite_wasm_wrapper";
const dispatch = createEventDispatcher<{
value: Curve;
}>();
const GRID_SIZE = 4;
// Content
export let value: Curve;
// Tooltips
export let tooltipLabel: string | undefined = undefined;
export let tooltipDescription: string | undefined = undefined;
export let tooltipShortcut: ActionShortcut | undefined = undefined;
let manipulatorsList: CurveManipulatorGroup[] = [
{
anchor: [0, 0],
handles: [
[-1, -1],
[0.25, 0.25],
],
},
{
anchor: [0.5, 0.5],
handles: [
[0.25, 0.25],
[0.75, 0.75],
],
},
{
anchor: [1, 1],
handles: [
[0.75, 0.75],
[2, 2],
],
},
];
let selectedNodeIndex: number | undefined = undefined;
let draggedNodeIndex: number | undefined = undefined;
let dAttribute = recalculateSvgPath();
$: {
manipulatorsList = [manipulatorsList[0]].concat(value.manipulatorGroups).concat([manipulatorsList[manipulatorsList.length - 1]]);
manipulatorsList[0].handles[1] = value.firstHandle;
manipulatorsList[manipulatorsList.length - 1].handles[0] = value.lastHandle;
dAttribute = recalculateSvgPath();
}
function updateCurve() {
dispatch("value", {
manipulatorGroups: manipulatorsList.slice(1, manipulatorsList.length - 1),
firstHandle: manipulatorsList[0].handles[1],
lastHandle: manipulatorsList[manipulatorsList.length - 1].handles[0],
});
}
function recalculateSvgPath() {
let dAttribute = "";
let anchor = manipulatorsList[0].anchor;
let handle = manipulatorsList[0].handles[1];
manipulatorsList.slice(1).forEach((m) => {
dAttribute += `M${anchor[0]} ${1 - anchor[1]} C${handle[0]} ${1 - handle[1]}, ${m.handles[0][0]} ${1 - m.handles[0][1]}, ${m.anchor[0]} ${1 - m.anchor[1]} `;
anchor = m.anchor;
handle = m.handles[1];
});
return dAttribute;
}
function handleManipulatorPointerDown(e: PointerEvent, i: number) {
// Delete an anchor with right click or middle click
if (e.button > 0 && i > 0 && i < manipulatorsList.length - 1) {
draggedNodeIndex = undefined;
selectedNodeIndex = undefined;
manipulatorsList.splice(i, 1);
manipulatorsList = manipulatorsList;
dAttribute = recalculateSvgPath();
updateCurve();
return;
}
draggedNodeIndex = i;
if (i >= 0) selectedNodeIndex = i;
}
function getSvgPositionFromPointerEvent(e: PointerEvent): [number, number] | undefined {
if (!(e.target instanceof SVGElement)) return undefined;
const target = e.target?.closest("svg") || undefined;
if (!target) return undefined;
const rect = target.getBoundingClientRect();
const x = (e.x - rect.x) / rect.width;
const y = 1 - (e.y - rect.y) / rect.height;
return [clamp(x), clamp(y)];
}
function clampHandles() {
for (let i = 0; i < manipulatorsList.length - 1; i++) {
const [min, max] = [manipulatorsList[i].anchor[0], manipulatorsList[i + 1].anchor[0]];
for (let j = 0; j < 2; j++) {
manipulatorsList[i + j].handles[1 - j][0] = clamp(manipulatorsList[i + j].handles[1 - j][0], min, max);
manipulatorsList[i + j].handles[1 - j][1] = clamp(manipulatorsList[i + j].handles[1 - j][1]);
}
}
}
function handlePointerUp(e: PointerEvent) {
if (draggedNodeIndex !== undefined) {
draggedNodeIndex = undefined;
return;
}
if (e.button !== 0) return;
const anchor = getSvgPositionFromPointerEvent(e);
if (!anchor) return;
let nodeIndex = manipulatorsList.findIndex((manipulators) => manipulators.anchor[0] > anchor[0]);
if (nodeIndex === -1) nodeIndex = manipulatorsList.length;
manipulatorsList.splice(nodeIndex, 0, {
anchor: anchor,
handles: [
[anchor[0] - 0.05, anchor[1]],
[anchor[0] + 0.05, anchor[1]],
],
});
selectedNodeIndex = nodeIndex;
clampHandles();
dAttribute = recalculateSvgPath();
updateCurve();
}
function setHandlePosition(anchorIndex: number, handleIndex: number, position: [number, number]) {
const { anchor, handles } = manipulatorsList[anchorIndex];
const otherHandle = handles[1 - handleIndex];
const handleVector = [anchor[0] - position[0], anchor[1] - position[1]];
const handleVectorLength = Math.hypot(...handleVector);
const handleVectorNormalized = [handleVector[0] / handleVectorLength, handleVector[1] / handleVectorLength];
const otherHandleVectorLength = Math.hypot(anchor[0] - otherHandle[0], anchor[1] - otherHandle[1]);
handles[handleIndex] = position;
handles[1 - handleIndex] = [anchor[0] + handleVectorNormalized[0] * otherHandleVectorLength, anchor[1] + handleVectorNormalized[1] * otherHandleVectorLength];
}
function handlePointerMove(e: PointerEvent) {
if (draggedNodeIndex === undefined || draggedNodeIndex === 0 || draggedNodeIndex === manipulatorsList.length - 1) return;
const position = getSvgPositionFromPointerEvent(e);
if (!position) return;
if (draggedNodeIndex > 0) {
position[0] = clamp(position[0], manipulatorsList[draggedNodeIndex - 1].anchor[0], manipulatorsList[draggedNodeIndex + 1].anchor[0]);
const manipulators = manipulatorsList[draggedNodeIndex];
manipulators.handles = [
[manipulators.handles[0][0] + position[0] - manipulators.anchor[0], manipulators.handles[0][1] + position[1] - manipulators.anchor[1]],
[manipulators.handles[1][0] + position[0] - manipulators.anchor[0], manipulators.handles[1][1] + position[1] - manipulators.anchor[1]],
];
manipulators.anchor = position;
} else {
if (selectedNodeIndex === undefined) return;
setHandlePosition(selectedNodeIndex, -draggedNodeIndex - 1, position);
const manipulators = manipulatorsList[selectedNodeIndex];
if (manipulators.handles[0][0] > manipulators.anchor[0]) {
manipulators.handles = [manipulators.handles[1], manipulators.handles[0]];
draggedNodeIndex = -3 - draggedNodeIndex;
}
}
clampHandles();
dAttribute = recalculateSvgPath();
updateCurve();
}
function clamp(value: number, min = 0, max = 1): number {
return Math.max(min, Math.min(value, max));
}
</script>
<LayoutRow class="curve-input" {tooltipLabel} {tooltipDescription} {tooltipShortcut}>
<svg viewBox="0 0 1 1" on:pointermove={handlePointerMove} on:pointerup={handlePointerUp}>
{#each { length: GRID_SIZE - 1 } as _, i}
<path class="grid" d={`M 0 ${(i + 1) / GRID_SIZE} L 1 ${(i + 1) / GRID_SIZE}`} />
<path class="grid" d={`M ${(i + 1) / GRID_SIZE} 0 L ${(i + 1) / GRID_SIZE} 1`} />
{/each}
<path class="curve" d={dAttribute} />
{#if selectedNodeIndex !== undefined}
{@const m = manipulatorsList[selectedNodeIndex]}
{#each [0, 1] as i}
<path d={`M ${m.anchor[0]} ${1 - m.anchor[1]} L ${m.handles[i][0]} ${1 - m.handles[i][1]}`} class="handle-line" />
<circle cx={m.handles[i][0]} cy={1 - m.handles[i][1]} class="manipulator handle" r="0.02" on:pointerdown={(e) => handleManipulatorPointerDown(e, -i - 1)} />
{/each}
{/if}
{#each manipulatorsList as manipulators, i}
<circle cx={manipulators.anchor[0]} cy={1 - manipulators.anchor[1]} class="manipulator" r="0.02" on:pointerdown={(e) => handleManipulatorPointerDown(e, i)} />
{/each}
</svg>
<slot />
</LayoutRow>
<style lang="scss">
.curve-input {
background: var(--color-1-nearblack);
display: flex;
position: relative;
min-width: calc(2 * var(--widget-height));
max-width: calc(8 * var(--widget-height));
.grid {
stroke: var(--color-5-dullgray);
stroke-width: 0.005;
pointer-events: none;
}
.curve {
fill: none;
stroke: var(--color-e-nearwhite);
stroke-width: 0.01;
}
.manipulator {
fill: var(--color-1-nearblack);
stroke: var(--color-e-nearwhite);
stroke-width: 0.01;
&:hover {
fill: var(--color-f-white);
stroke: var(--color-f-white);
}
&.handle {
fill: var(--color-1-nearblack);
stroke: var(--color-c-brightgray);
&:hover {
fill: var(--color-a-softgray);
stroke: var(--color-a-softgray);
}
}
}
.handle-line {
stroke: var(--color-5-dullgray);
stroke-width: 0.005;
pointer-events: none;
}
}
</style> -->

View File

@@ -11,15 +11,6 @@ pub trait Linear {
fn to_f32(self) -> f32;
fn from_f64(x: f64) -> Self;
fn to_f64(self) -> f64;
fn lerp(self, other: Self, value: Self) -> Self
where
Self: Sized + Copy,
Self: core::ops::Sub<Self, Output = Self>,
Self: core::ops::Mul<Self, Output = Self>,
Self: core::ops::Add<Self, Output = Self>,
{
self + (other - self) * value
}
}
#[rustfmt::skip]
@@ -174,10 +165,6 @@ pub trait Luminance {
}
}
pub trait LuminanceMut: Luminance {
fn set_luminance(&mut self, luminance: Self::LuminanceChannel);
}
// TODO: We might rename this to Raster at some point
pub trait Sample {
type Pixel: Pixel;

View File

@@ -1,4 +1,4 @@
use super::color_traits::{Alpha, AlphaMut, AssociatedAlpha, Luminance, LuminanceMut, Pixel, RGB, RGBMut, Rec709Primaries, SRGB};
use super::color_traits::{Alpha, AlphaMut, AssociatedAlpha, Luminance, Pixel, RGB, RGBMut, Rec709Primaries, SRGB};
use super::discrete_srgb::{float_to_srgb_u8, srgb_u8_to_float};
use bytemuck::{Pod, Zeroable};
use core::fmt::Debug;
@@ -236,12 +236,6 @@ impl Luminance for Luma {
}
}
impl LuminanceMut for Luma {
fn set_luminance(&mut self, luminance: Self::LuminanceChannel) {
self.0 = luminance
}
}
impl RGB for Luma {
type ColorChannel = f32;
#[inline(always)]
@@ -411,28 +405,6 @@ impl Luminance for Color {
}
}
impl LuminanceMut for Color {
fn set_luminance(&mut self, luminance: f32) {
let current = self.luminance();
// When we have a black-ish color, we just set the color to a grey-scale value. This prohibits a divide-by-0.
if current < f32::EPSILON {
self.red = 0.2126 * luminance;
self.green = 0.7152 * luminance;
self.blue = 0.0722 * luminance;
return;
}
let fac = luminance / current;
// TODO: when we have for example the rgb color (0, 0, 1) and want to
// TODO: do `.set_luminance(1)`, then the actual luminance is not 1 at
// TODO: the end. With no clamp, the resulting color would be
// TODO: (0, 0, 12.8504). The excess should be spread to the other
// TODO: channels, but is currently just clamped away.
self.red = (self.red * fac).clamp(0., 1.);
self.green = (self.green * fac).clamp(0., 1.);
self.blue = (self.blue * fac).clamp(0., 1.);
}
}
impl Rec709Primaries for Color {}
impl SRGB for Color {}

View File

@@ -5,7 +5,6 @@ use crate::vector::algorithms::offset_subpath::MAX_ABSOLUTE_DIFFERENCE;
use crate::vector::misc::{PointSpacingType, dvec2_to_point, point_to_dvec2};
use core_types::math::polynomial::pathseg_to_parametric_polynomial;
use glam::{DMat2, DVec2};
use kurbo::common::{solve_cubic, solve_quadratic};
use kurbo::{BezPath, CubicBez, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveArclen, ParamCurveDeriv, PathEl, PathSeg, Point, QuadBez, Rect, Shape, Vec2};
use std::f64::consts::{FRAC_PI_2, PI};
@@ -201,39 +200,6 @@ pub fn pathseg_compute_lookup_table(segment: PathSeg, steps: Option<usize>, eucl
})
}
/// Returns an `Iterator` containing all possible parametric `t`-values at the given `x`-coordinate.
pub fn pathseg_find_tvalues_for_x(segment: PathSeg, x: f64) -> impl Iterator<Item = f64> + use<> {
match segment {
PathSeg::Line(Line { p0, p1 }) => {
// If the transformed linear bezier is on the x-axis, `a` and `b` will both be zero and `solve_linear` will return no roots
let a = p1.x - p0.x;
let b = p0.x - x;
// Find the roots of the linear equation `ax + b`.
// There exist roots when `a` is not 0
if a.abs() > MAX_ABSOLUTE_DIFFERENCE { [Some(-b / a), None, None] } else { [None; 3] }
}
PathSeg::Quad(QuadBez { p0, p1, p2 }) => {
let a = p2.x - 2. * p1.x + p0.x;
let b = 2. * (p1.x - p0.x);
let c = p0.x - x;
let r = solve_quadratic(c, b, a);
[r.first().copied(), r.get(1).copied(), None]
}
PathSeg::Cubic(CubicBez { p0, p1, p2, p3 }) => {
let a = p3.x - 3. * p2.x + 3. * p1.x - p0.x;
let b = 3. * (p2.x - 2. * p1.x + p0.x);
let c = 3. * (p1.x - p0.x);
let d = p0.x - x;
let r = solve_cubic(d, c, b, a);
[r.first().copied(), r.get(1).copied(), r.get(2).copied()]
}
}
.into_iter()
.flatten()
.filter(|&t| (0.0..1.).contains(&t))
}
/// Find the `t`-value(s) such that the normal(s) at `t` pass through the specified point.
pub fn pathseg_normals_to_point(segment: PathSeg, point: Point) -> Vec<f64> {
// We solve deriv(t) dot (self(t) - point) = 0.

View File

@@ -1,69 +0,0 @@
use core_types::Node;
use core_types::color::{Channel, Linear, LuminanceMut};
use dyn_any::{DynAny, StaticType, StaticTypeSized};
use std::ops::{Add, Mul, Sub};
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, PartialEq, core_types::CacheHash, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Curve {
#[cfg_attr(feature = "serde", serde(rename = "manipulatorGroups"))]
pub manipulator_groups: Vec<CurveManipulatorGroup>,
#[cfg_attr(feature = "serde", serde(rename = "firstHandle"))]
pub first_handle: [f32; 2],
#[cfg_attr(feature = "serde", serde(rename = "lastHandle"))]
pub last_handle: [f32; 2],
}
impl Default for Curve {
fn default() -> Self {
Self {
manipulator_groups: vec![],
first_handle: [0.2; 2],
last_handle: [0.8; 2],
}
}
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, Copy, PartialEq, core_types::CacheHash, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CurveManipulatorGroup {
pub anchor: [f32; 2],
pub handles: [[f32; 2]; 2],
}
pub struct ValueMapperNode<C> {
lut: Vec<C>,
}
unsafe impl<C: StaticTypeSized> StaticType for ValueMapperNode<C> {
type Static = ValueMapperNode<C::Static>;
}
impl<C> ValueMapperNode<C> {
pub const fn new(lut: Vec<C>) -> Self {
Self { lut }
}
}
impl<'i, L: LuminanceMut + 'i> Node<'i, L> for ValueMapperNode<L::LuminanceChannel>
where
L::LuminanceChannel: Linear + Copy,
L::LuminanceChannel: Add<Output = L::LuminanceChannel>,
L::LuminanceChannel: Sub<Output = L::LuminanceChannel>,
L::LuminanceChannel: Mul<Output = L::LuminanceChannel>,
{
type Output = L;
fn eval(&'i self, mut val: L) -> L {
let luminance: f32 = val.luminance().to_linear();
let floating_sample_index = luminance * (self.lut.len() - 1) as f32;
let index_in_lut = floating_sample_index.floor() as usize;
let a = self.lut[index_in_lut];
let b = self.lut[(index_in_lut + 1).clamp(0, self.lut.len() - 1)];
let result = a.lerp(b, L::LuminanceChannel::from_linear(floating_sample_index.fract()));
val.set_luminance(result);
val
}
}

View File

@@ -1,45 +0,0 @@
use crate::curve::{Curve, CurveManipulatorGroup, ValueMapperNode};
use core_types::color::{Channel, Linear};
use core_types::context::Ctx;
use kurbo::{CubicBez, ParamCurve, PathSeg, Point};
use vector_types::vector::algorithms::bezpath_algorithms::pathseg_find_tvalues_for_x;
const WINDOW_SIZE: usize = 1024;
#[node_macro::node(category(""))]
fn generate_curves<C: Channel + Linear>(_: impl Ctx, curve: Curve, #[implementations(f32, f64)] _target_format: C) -> ValueMapperNode<C> {
let [mut pos, mut param]: [[f32; 2]; 2] = [[0.; 2], curve.first_handle];
let mut lut = vec![C::from_f64(0.); WINDOW_SIZE];
let end = CurveManipulatorGroup {
anchor: [1.; 2],
handles: [curve.last_handle, [0.; 2]],
};
for sample in curve.manipulator_groups.iter().chain(std::iter::once(&end)) {
let [x0, y0, x1, y1, x2, y2, x3, y3] = [pos[0], pos[1], param[0], param[1], sample.handles[0][0], sample.handles[0][1], sample.anchor[0], sample.anchor[1]].map(f64::from);
let segment = PathSeg::Cubic(CubicBez::new(Point::new(x0, y0), Point::new(x1, y1), Point::new(x2, y2), Point::new(x3, y3)));
let [left, right] = [pos[0], sample.anchor[0]].map(|c| c.clamp(0., 1.));
let lut_index_left: usize = (left * (lut.len() - 1) as f32).floor() as _;
let lut_index_right: usize = (right * (lut.len() - 1) as f32).ceil() as _;
for index in lut_index_left..=lut_index_right {
let x = index as f64 / (lut.len() - 1) as f64;
let y = if x <= x0 {
y0
} else if x >= x3 {
y3
} else {
pathseg_find_tvalues_for_x(segment, x)
.next()
.map(|t| segment.eval(t.clamp(0., 1.)).y)
// Fall back to a very bad approximation if the above fails
.unwrap_or_else(|| (x - x0) / (x3 - x0) * (y3 - y0) + y0)
};
lut[index] = C::from_f64(y);
}
pos = sample.anchor;
param = sample.handles[1];
}
ValueMapperNode::new(lut)
}