mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Remove dead code from the unused curves adjustment widget
This commit is contained in:
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> -->
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user