diff --git a/src/core/constants.js b/src/core/constants.js index c203106730..e4c2be9486 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -1072,6 +1072,18 @@ export const CHAR = 'CHAR'; * @final */ export const WORD = 'WORD'; +/** + * @typedef {'PRETTY'} PRETTY + * @property {PRETTY} PRETTY + * @final + */ +export const PRETTY = 'PRETTY'; +/** + * @typedef {'BALANCE'} BALANCE + * @property {BALANCE} BALANCE + * @final + */ +export const BALANCE = 'BALANCE'; // TYPOGRAPHY-INTERNAL export const _DEFAULT_TEXT_FILL = '#000000'; diff --git a/src/type/textCore.js b/src/type/textCore.js index 70c05cf927..50deb83d4a 100644 --- a/src/type/textCore.js +++ b/src/type/textCore.js @@ -2387,6 +2387,48 @@ function textCore(p5, fn) { opts = {} ) { + // Handle PRETTY/BALANCE with DP algorithm + if (textWrap === fn.PRETTY || textWrap === fn.BALANCE) { + let newLines = []; + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + const wordsArr = lines[lineIndex].split(' ').filter(s => s.length > 0); + const spaceW = this._textWidthSingle(' '); + const N = wordsArr.length; + const widths = wordsArr.map(s => this._textWidthSingle(s)); + + // DP arrays + const dp = new Array(N + 1).fill(Infinity); + const next = new Array(N + 1).fill(-1); + dp[N] = 0; + + // DP algorithm to minimize raggedness + for (let i = N - 1; i >= 0; i--) { + let sum = 0; + for (let j = i; j < N; j++) { + sum += widths[j]; + const gaps = j - i; + const total = sum + gaps * spaceW; + if (total > maxWidth) break; + const slack = maxWidth - total; + const cost = (j === N - 1 ? 0 : slack * slack) + dp[j + 1]; + if (cost < dp[i]) { + dp[i] = cost; + next[i] = j + 1; + } + } + } + + // Reconstruct lines + let i = 0; + while (i < N) { + const j = next[i] > 0 ? next[i] : i + 1; + newLines.push(wordsArr.slice(i, j).join(' ')); + i = j; + } + } + return newLines; + } + let splitter = opts.splitChar ?? (textWrap === fn.WORD ? ' ' : ''); let line, testLine, testWidth, words, newLines = []; diff --git a/test/manual-test-examples/type/pretty-sketch.js b/test/manual-test-examples/type/pretty-sketch.js new file mode 100644 index 0000000000..2879b80258 --- /dev/null +++ b/test/manual-test-examples/type/pretty-sketch.js @@ -0,0 +1,47 @@ +function setup() { + createCanvas(800, 600); + background(255); + + let sampleText = 'This is a sample text that will be wrapped using the new PRETTY and BALANCE modes. It should result in more balanced line lengths compared to standard WORD wrapping.'; + let boxWidth = 200; + let boxHeight = 150; + + // Test 1: WORD wrap (standard) + fill(0); + textSize(16); + textAlign(LEFT, TOP); + textWrap(WORD); + + stroke(200); + noFill(); + rect(50, 50, boxWidth, boxHeight); + + fill(0); + noStroke(); + text('WORD wrap:', 50, 30); + text(sampleText, 50, 50, boxWidth, boxHeight); + + // Test 2: PRETTY wrap + stroke(200); + noFill(); + rect(300, 50, boxWidth, boxHeight); + + fill(0); + noStroke(); + textWrap(PRETTY); + text('PRETTY wrap:', 300, 30); + text(sampleText, 300, 50, boxWidth, boxHeight); + + // Test 3: BALANCE wrap (alias for PRETTY) + stroke(200); + noFill(); + rect(550, 50, boxWidth, boxHeight); + + fill(0); + noStroke(); + textWrap(BALANCE); + text('BALANCE wrap:', 550, 30); + text(sampleText, 550, 50, boxWidth, boxHeight); + + noLoop(); +} diff --git a/test/manual-test-examples/type/pretty-test.html b/test/manual-test-examples/type/pretty-test.html new file mode 100644 index 0000000000..a516556094 --- /dev/null +++ b/test/manual-test-examples/type/pretty-test.html @@ -0,0 +1,10 @@ + + + + PRETTY/BALANCE TextWrap Test + + + + + +