const textWords = [ { text: 'come', bbox: { x0: 40, x1: 60 } }, { text: 'sei', bbox: { x0: 70, x1: 90 } } ]; const chordWords = [ { text: 'la', bbox: { x0: 65, x1: 75 } }, { text: 'mi', bbox: { x0: 95, x1: 105 } } ]; function mergeChordsAndLyrics(chordWords: any[], textWords: any[]): string { let result = ''; const chordAssignments = new Map(); chordWords.forEach(chord => { const chordX = (chord.bbox.x0 + chord.bbox.x1) / 2; let closestWord: any = null; let minDistance = Infinity; textWords.forEach(textWord => { const wordXCenter = (textWord.bbox.x0 + textWord.bbox.x1) / 2; const dist = Math.abs(chordX - wordXCenter); if (dist < minDistance) { minDistance = dist; closestWord = textWord; } }); if (closestWord) { if (!chordAssignments.has(closestWord)) { chordAssignments.set(closestWord, []); } chordAssignments.get(closestWord)!.push(chord); } }); textWords.forEach((textWord, index) => { const assignedChords = chordAssignments.get(textWord) || []; // Split chords into before and after the word const chordsBefore = assignedChords.filter(c => (c.bbox.x0 + c.bbox.x1)/2 <= textWord.bbox.x1); const chordsAfter = assignedChords.filter(c => (c.bbox.x0 + c.bbox.x1)/2 > textWord.bbox.x1); chordsBefore.sort((a, b) => a.bbox.x0 - b.bbox.x0); chordsAfter.sort((a, b) => a.bbox.x0 - b.bbox.x0); chordsBefore.forEach(chord => { const cleanChord = chord.text.replace(/[\(\)\[\]]/g, '').toUpperCase(); result += `[${cleanChord}]`; }); result += textWord.text; chordsAfter.forEach(chord => { const cleanChord = chord.text.replace(/[\(\)\[\]]/g, '').toUpperCase(); result += `[${cleanChord}]`; }); if (index < textWords.length - 1) { result += ' '; } }); return result; } console.log(mergeChordsAndLyrics(chordWords, textWords));