diff --git a/webapp/path_navigator.html b/webapp/path_navigator.html
index d4854a4..ea7a588 100644
--- a/webapp/path_navigator.html
+++ b/webapp/path_navigator.html
@@ -581,13 +581,37 @@
// Load from Flask API - get chords and compute graphs client-side
async function loadAllGraphs() {
try {
+ // Step 1: Get raw chord data
const response = await fetch("/api/chords");
if (!response.ok) throw new Error("API not available");
const data = await response.json();
- // Compute graphs from raw chord data client-side
+ // Step 2: Collect all fractions from all chords
+ const allFractions = [];
+ const chordFractions = []; // Track which fractions belong to which chord
+
+ for (const chord of data.chords) {
+ const fractions = [];
+ for (const pitch of chord) {
+ fractions.push(pitch.fraction || "1");
+ }
+ chordFractions.push(fractions);
+ allFractions.push(...fractions);
+ }
+
+ // Step 3: Batch fetch cents from server (avoid N+1 problem)
+ const centsResponse = await fetch("/api/batch-calculate-cents", {
+ method: "POST",
+ headers: {"Content-Type": "application/json"},
+ body: JSON.stringify({ fractions: allFractions })
+ });
+ const centsData = await centsResponse.json();
+ const allCents = centsData.results.map(r => r.cents);
+
+ // Step 4: Build graphs with cached cents values
+ let centsIndex = 0;
const graphs = data.chords.map((chord, index) => {
- return calculateGraph(chord, index);
+ return calculateGraph(chord, index, () => allCents[centsIndex++]);
});
allGraphsData = {
@@ -611,18 +635,17 @@
}
}
- // Compute graph (nodes + edges) from raw chord data - client-side version
- function calculateGraph(chord, index) {
+ // Compute graph (nodes + edges) from raw chord data - using API for cents
+ function calculateGraph(chord, index, getNextCent) {
if (!chord) return { nodes: [], edges: [] };
const nodes = [];
const dims = [2, 3, 5, 7];
- // Calculate cents for each pitch
+ // Calculate cents for each pitch (fetched from server)
for (let i = 0; i < chord.length; i++) {
const pitch = chord[i];
- const fraction = parseFraction(pitch.fraction || "1");
- const cents = fraction > 0 ? 1200 * Math.log2(fraction) : 0;
+ const cents = getNextCent();
nodes.push({
id: i,
@@ -689,18 +712,6 @@
return { nodes, edges, index };
}
- // Parse fraction string to number
- function parseFraction(fracStr) {
- if (typeof fracStr === 'number') return fracStr;
- if (!fracStr) return 1;
-
- if (fracStr.includes('/')) {
- const [num, den] = fracStr.split('/').map(Number);
- return num / den;
- }
- return Number(fracStr);
- }
-
// Update UI elements
function updateUI() {
hasPrev = currentIndex > 0;
diff --git a/webapp/server.py b/webapp/server.py
index 47942af..2ae6a6e 100644
--- a/webapp/server.py
+++ b/webapp/server.py
@@ -406,6 +406,45 @@ def load_file():
return jsonify({"error": f"Invalid JSON in: {filepath}"}), 400
+@app.route("/api/parse-fraction", methods=["POST"])
+def parse_fraction_api():
+ """Parse a fraction string to float."""
+ data = request.json
+ fraction = data.get("fraction", "1")
+ try:
+ value = parse_fraction(fraction)
+ return jsonify({"fraction": fraction, "value": value})
+ except Exception as e:
+ return jsonify({"error": str(e)}), 400
+
+
+@app.route("/api/calculate-cents", methods=["POST"])
+def calculate_cents_api():
+ """Calculate cents from fraction string."""
+ data = request.json
+ fraction = data.get("fraction", "1")
+ try:
+ cents = calculate_cents(fraction)
+ return jsonify({"fraction": fraction, "cents": cents})
+ except Exception as e:
+ return jsonify({"error": str(e)}), 400
+
+
+@app.route("/api/batch-calculate-cents", methods=["POST"])
+def batch_calculate_cents_api():
+ """Calculate cents for multiple fractions at once."""
+ data = request.json
+ fractions = data.get("fractions", [])
+ try:
+ results = []
+ for fraction in fractions:
+ cents = calculate_cents(fraction)
+ results.append({"fraction": fraction, "cents": cents})
+ return jsonify({"results": results})
+ except Exception as e:
+ return jsonify({"error": str(e)}), 400
+
+
if __name__ == "__main__":
print("Starting Path Navigator server...")
print(f"Loading chords from: {get_chords_file()}")