New node: 'Lorem Ipsum' and generator library

This commit is contained in:
Keavon Chambers
2026-04-23 22:55:10 -07:00
parent c9c76df40c
commit e952987bec
11 changed files with 1934 additions and 2 deletions

View File

@@ -265,6 +265,7 @@ tagged_value! {
CentroidType(vector::misc::CentroidType),
BooleanOperation(vector::misc::BooleanOperation),
TextAlign(text_nodes::TextAlign),
TextDenomination(text_nodes::TextDenomination),
ScaleType(core_types::transform::ScaleType),
}

View File

@@ -112,6 +112,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::LuminanceCalculation]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::extract_xy::XY]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text_nodes::StringCapitalization]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text_nodes::TextDenomination]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RedGreenBlue]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RedGreenBlueAlpha]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::animation::RealTimeMode]),
@@ -195,6 +196,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::QRCodeErrorCorrectionLevel]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::extract_xy::XY]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::text_nodes::StringCapitalization]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::text_nodes::TextDenomination]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::RedGreenBlue]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::RedGreenBlueAlpha]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::animation::RealTimeMode]),

View File

@@ -28,6 +28,8 @@ convert_case = { workspace = true }
titlecase = { workspace = true }
fancy-regex = { workspace = true }
unicode-segmentation = { workspace = true }
ipsum = { workspace = true }
rand = { workspace = true }
# Optional workspace dependencies
serde = { workspace = true, optional = true }

View File

@@ -7,11 +7,12 @@ mod to_path;
use convert_case::{Boundary, Converter, pattern};
use core_types::Color;
use core_types::registry::types::{SignedInteger, TextArea};
use core_types::registry::types::{SeedValue, SignedInteger, TextArea};
use core_types::table::Table;
use core_types::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractVarArgs, OwnedContextImpl};
use dyn_any::DynAny;
use glam::{DAffine2, DVec2};
use rand::{Rng, SeedableRng};
use raster_types::{CPU, Raster};
use unicode_segmentation::UnicodeSegmentation;
@@ -146,6 +147,60 @@ fn string_value(_: impl Ctx, _primary: (), string: TextArea) -> String {
string
}
/// Denomination used to measure a quantity of text.
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, DynAny, node_macro::ChoiceType)]
pub enum TextDenomination {
/// Text measured character-by-character.
Characters,
/// Text measured word-by-word.
#[default]
Words,
/// Text measured sentence-by-sentence.
Sentences,
/// Text measured paragraph-by-paragraph.
Paragraphs,
}
impl From<TextDenomination> for ipsum::Unit {
fn from(unit: TextDenomination) -> Self {
match unit {
TextDenomination::Characters => ipsum::Unit::Characters,
TextDenomination::Words => ipsum::Unit::Words,
TextDenomination::Sentences => ipsum::Unit::Sentences,
TextDenomination::Paragraphs => ipsum::Unit::Paragraphs,
}
}
}
/// Generates *Lorem Ipsum* placeholder text of a desired length. The classic "Lorem ipsum dolor sit amet…" intro may be included up to its full four-sentence (or one-paragraph) length, or used in part or not at all, after which the randomized Latin-like text continues producing paragraphs until the requested length is reached.
#[node_macro::node(category("Value"))]
fn lorem_ipsum(
_: impl Ctx,
_primary: (),
/// Total length of generated text in the chosen denomination (characters, words, sentences, or paragraphs), including the classic "Lorem ipsum dolor sit amet…" intro.
#[default(50)]
length: u32,
/// How the desired quantity of generated text is counted.
length_in: TextDenomination,
/// Length of the classic "Lorem ipsum dolor sit amet…" intro to include at the start of the generated text. Disable by setting this to 0.
#[default(8)]
#[name("\"Lorem…\" Intro")]
lorem_intro: u32,
/// How the desired quantity of classic intro text is counted for inclusion at the start.
///
/// If one paragraph is chosen, the full intro is included and the randomized continuation begins on the next paragraph; otherwise the continuation always follows in the same paragraph.
#[name("\"Lorem…\" Intro In")]
lorem_intro_in: TextDenomination,
/// Seed to determine unique variations on the randomized text generated after the optional classic intro.
seed: SeedValue,
) -> String {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
let rng = |n| rng.random_range(0..n);
ipsum::generate(lorem_intro as usize, lorem_intro_in.into(), length as usize, length_in.into(), rng)
}
/// Type-asserts a value to be a string.
#[node_macro::node(category("Debug"))]
fn to_string(_: impl Ctx, value: String) -> String {