From 51f1ee49a67072545757053924f7da525192086f Mon Sep 17 00:00:00 2001
From: Benedikt Seeger <benedikt.seeger@ptb.de>
Date: Thu, 27 Feb 2025 13:56:15 +0100
Subject: [PATCH] added LOg-X and Log-Y toggle

---
 src/renderers/MeasurementRenderer.js | 161 +++++++++++++++++----------
 1 file changed, 103 insertions(+), 58 deletions(-)

diff --git a/src/renderers/MeasurementRenderer.js b/src/renderers/MeasurementRenderer.js
index 8befef2..97430b6 100644
--- a/src/renderers/MeasurementRenderer.js
+++ b/src/renderers/MeasurementRenderer.js
@@ -38,7 +38,6 @@ export function renderMeasurementResults(measurementResults, language) {
   }
 
   let results = measurementResults['dcc:measurementResult'];
-  console.debug('Raw dcc:measurementResult:', results);
   if (!results) {
     console.error("Missing 'dcc:measurementResult' in measurementResults");
     return;
@@ -46,10 +45,7 @@ export function renderMeasurementResults(measurementResults, language) {
   if (!Array.isArray(results)) {
     results = [results];
   }
-  console.debug('Processed measurement result array:', results);
-
   const measurementResult = results[0];
-  console.debug('Using measurementResult:', measurementResult);
 
   let resultObj = measurementResult['dcc:results'] && measurementResult['dcc:results']['dcc:result'];
   if (!resultObj) {
@@ -59,7 +55,6 @@ export function renderMeasurementResults(measurementResults, language) {
   if (Array.isArray(resultObj)) {
     resultObj = resultObj[0];
   }
-  console.debug('Using result object:', resultObj);
 
   let resultName = 'Measurement Result';
   if (measurementResult['dcc:name'] && measurementResult['dcc:name']['dcc:content']) {
@@ -71,7 +66,6 @@ export function renderMeasurementResults(measurementResults, language) {
       resultName = content._ || content;
     }
   }
-  console.debug('Result name:', resultName);
   const tabTitle = document.createElement('h2');
   tabTitle.textContent = resultName;
   container.appendChild(tabTitle);
@@ -81,7 +75,6 @@ export function renderMeasurementResults(measurementResults, language) {
     return;
   }
   const listData = resultObj['dcc:data']['dcc:list'];
-  console.debug('List data:', listData);
 
   let quantityJSONs = [];
   if (listData['dcc:quantity']) {
@@ -100,7 +93,6 @@ export function renderMeasurementResults(measurementResults, language) {
       }
     });
   }
-  console.debug('Combined quantity JSONs:', quantityJSONs);
 
   const indexQuantities = [];
   const dataQuantities = [];
@@ -138,9 +130,37 @@ export function renderMeasurementResults(measurementResults, language) {
       extraInfo.push({ uncertainty, comment, conformity });
     }
   });
-  console.debug('Index Quantities (objects):', indexQuantities);
-  console.debug('Data Quantities (objects):', dataQuantities);
-  console.debug('Extra info for data quantities:', extraInfo);
+
+  // Build headers for data quantities
+  const dataHeaders = dataQuantities.map(q => {
+    let header = q.getName(language);
+    let unit = q.getUnit();
+    if (!header.toLowerCase().includes(" in ")) {
+      header = header + " in " + unit;
+    }
+    return header;
+  });
+
+  // Create log scaling toggles
+  const scalingContainer = document.createElement('div');
+  scalingContainer.innerHTML = '<strong>Scaling:</strong> ';
+  const logXToggle = document.createElement('input');
+  logXToggle.type = 'checkbox';
+  logXToggle.id = 'logXToggle';
+  const logXLabel = document.createElement('label');
+  logXLabel.htmlFor = 'logXToggle';
+  logXLabel.textContent = 'Log X';
+  scalingContainer.appendChild(logXToggle);
+  scalingContainer.appendChild(logXLabel);
+  const logYToggle = document.createElement('input');
+  logYToggle.type = 'checkbox';
+  logYToggle.id = 'logYToggle';
+  const logYLabel = document.createElement('label');
+  logYLabel.htmlFor = 'logYToggle';
+  logYLabel.textContent = 'Log Y';
+  scalingContainer.appendChild(logYToggle);
+  scalingContainer.appendChild(logYLabel);
+  container.appendChild(scalingContainer);
 
   const xAxisContainer = document.createElement('div');
   xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> ';
@@ -195,28 +215,19 @@ export function renderMeasurementResults(measurementResults, language) {
     headers.push(xHeader);
 
     const dataValues = [];
-    const commentsArray = [];
-    const conformityArray = [];
     const uncertaintiesArray = [];
+    const conformityArray = [];
     dataQuantities.forEach((q, idx) => {
-      let header = q.getName(language);
-      let unit = q.getUnit();
-      if (!header.toLowerCase().includes(" in ")) {
-        header = header + " in " + unit;
-      }
-      headers.push(header);
-      headers.push('Comments');
-      headers.push(extraInfo[idx] && extraInfo[idx].conformity ? 'Conformity' : '');
-      let values = q.getValues();
-      if (values.length === 1 && xValues.length > 1) {
-        values = new Array(xValues.length).fill(values[0]);
+      headers.push(dataHeaders[idx]);
+      if (extraInfo[idx] && extraInfo[idx].conformity) {
+        headers.push('Conformity');
       }
-      dataValues.push(values);
-      uncertaintiesArray.push(extraInfo[idx] ? extraInfo[idx].uncertainty : null);
-      commentsArray.push(extraInfo[idx] ? extraInfo[idx].comment : '');
+      dataValues.push(q.getValues());
+      uncertaintiesArray.push(extraInfo[idx] ? extraInfo[idx].uncertainty : []);
       conformityArray.push(extraInfo[idx] ? extraInfo[idx].conformity : []);
     });
-    console.debug('Table data arrays:', { dataValues, commentsArray, conformityArray, uncertaintiesArray });
+    headers.push('Comments');
+
     const tableData = [headers];
     for (let i = 0; i < xValues.length; i++) {
       const row = [];
@@ -227,12 +238,16 @@ export function renderMeasurementResults(measurementResults, language) {
           cellValue = cellValue + ' ± ' + uncertaintiesArray[idx][i];
         }
         row.push(cellValue);
-        row.push(commentsArray[idx] || '');
-        row.push(conformityArray[idx] && conformityArray[idx][i] ? conformityArray[idx][i] : '');
+        if (extraInfo[idx] && extraInfo[idx].conformity && extraInfo[idx].conformity[i]) {
+          row.push(extraInfo[idx].conformity[i]);
+        } else if (extraInfo[idx] && extraInfo[idx].conformity) {
+          row.push('');
+        }
       });
+      let globalComment = extraInfo.map(e => e.comment).filter(c => c).join(' ; ');
+      row.push(globalComment);
       tableData.push(row);
     }
-    console.debug('Final table data:', tableData);
     renderTable(tableData);
 
     const unitGroups = {};
@@ -241,64 +256,86 @@ export function renderMeasurementResults(measurementResults, language) {
       if (!unitGroups[unit]) {
         unitGroups[unit] = [];
       }
-      let header = headers[idx * 3 + 1];
+      const header = dataHeaders[idx];
       let values = q.getValues();
-      if (values.length === 1 && xValues.length > 1) {
-        values = new Array(xValues.length).fill(values[0]);
-      }
       const uncertainty = uncertaintiesArray[idx];
       const conformity = conformityArray[idx];
-      const traceColor = palette[idx % palette.length];
-      unitGroups[unit].push({ name: header, y: values, uncertainty, conformity, color: traceColor, index: idx });
+      const defaultColor = palette[idx % palette.length];
+      const markerColorArray = values.map((val, i) => {
+        if (conformity && conformity[i] && conformity[i].toLowerCase().includes('fail')) {
+          return '#d62728';
+        }
+        return defaultColor;
+      });
+      unitGroups[unit].push({ name: header, y: values, uncertainty, conformity, defaultColor, markerColor: markerColorArray, index: idx });
     });
-    console.debug('Unit groups for plots:', unitGroups);
 
     subplotsContainer.innerHTML = '';
     const unitKeys = Object.keys(unitGroups);
+    const plotDivs = [];
     unitKeys.forEach((unit, groupIdx) => {
       const group = unitGroups[unit];
       const graphDiv = document.createElement('div');
       graphDiv.style.width = '100%';
       graphDiv.style.height = '300px';
       subplotsContainer.appendChild(graphDiv);
-
+      plotDivs.push(graphDiv);
       const groupTraces = group.map(trace => {
-        const hovertemplate = 'X: %{x} ' + xUnit + ' | ' + trace.name + ': %{y} ± %{customdata_unc} ' + unit + ' | Conformity: %{customdata}<extra></extra>';
+        let tooltip = 'X: %{x} ' + xUnit + ' | ' + trace.name + ': %{y}';
+        if (trace.conformity && trace.conformity.length > 0) {
+          tooltip += ' | Conformity: %{customdata}';
+        }
+        tooltip += '<extra></extra>';
         return {
           x: xValues,
           y: trace.y,
-          error_y: { type: 'data', array: trace.uncertainty || new Array(xValues.length).fill(0), visible: true, color: trace.color },
+          error_y: {
+            type: 'data',
+            array: (trace.uncertainty && trace.uncertainty.length === xValues.length)
+                ? trace.uncertainty
+                : new Array(xValues.length).fill(0),
+            visible: true,
+            color: trace.defaultColor
+          },
           type: 'scatter',
           mode: 'lines+markers',
           name: trace.name,
-          marker: { color: trace.color },
-          hovertemplate: hovertemplate,
-          customdata: (trace.conformity && trace.conformity.length === xValues.length) ? trace.conformity : new Array(xValues.length).fill(''),
-          customdata_unc: (trace.uncertainty && trace.uncertainty.length === xValues.length) ? trace.uncertainty : new Array(xValues.length).fill('0')
+          marker: { color: trace.markerColor },
+          line: { color: trace.defaultColor },
+          hovertemplate: tooltip,
+          customdata: (trace.conformity && trace.conformity.length === xValues.length)
+              ? trace.conformity
+              : new Array(xValues.length).fill('')
         };
       });
-
       let xaxisTitle = '';
       if (groupIdx === unitKeys.length - 1) {
         let xLabel = 'X: ' + xQuantity.getName(language);
-        xaxisTitle = xLabel + ' in ' + xUnit;
+        xaxisTitle = '<b>' + xLabel + ' in ' + xUnit + '</b>';
       }
+      const logX = document.getElementById('logXToggle').checked;
+      const logY = document.getElementById('logYToggle').checked;
       const layout = {
-        xaxis: { title: { text: xaxisTitle, font: { size: 18, family: 'Arial', color: 'black' } }, tickfont: { family: 'Arial', size: 14, color: 'black' } },
-        yaxis: { title: { text: unit, font: { size: 18, family: 'Arial', color: 'black' } }, tickfont: { family: 'Arial', size: 14, color: 'black' } },
+        xaxis: {
+          title: { text: xaxisTitle, font: { size: 36, family: 'Arial', color: 'black' } },
+          tickfont: { family: 'Arial', size: 14, color: 'black' },
+          type: logX ? 'log' : 'linear'
+        },
+        yaxis: {
+          title: { text: unit, font: { size: 18, family: 'Arial', color: 'black' } },
+          tickfont: { family: 'Arial', size: 14, color: 'black' },
+          type: logY ? 'log' : 'linear'
+        },
         hovermode: 'closest',
         margin: { t: 20, b: 40 }
       };
-
       Plotly.newPlot(graphDiv, groupTraces, layout).then(() => {
-        console.debug('Plot rendered for unit:', unit);
         const caption = document.createElement('div');
-        caption.innerHTML = '<b>' + group[0].name +'</b>';
+        caption.innerHTML = '<b>' + group[0].name + '</b>';
         caption.style.textAlign = 'center';
         caption.style.marginBottom = '5px';
         graphDiv.parentNode.insertBefore(caption, graphDiv);
       });
-
       graphDiv.on('plotly_hover', function(data) {
         if (data.points && data.points.length > 0) {
           const pointIndex = data.points[0].pointIndex + 1;
@@ -308,6 +345,13 @@ export function renderMeasurementResults(measurementResults, language) {
       graphDiv.on('plotly_unhover', function() {
         clearTableRowHighlights();
       });
+      graphDiv.on('plotly_relayout', function(eventData) {
+        plotDivs.forEach(div => {
+          if (div !== graphDiv && eventData['xaxis.range[0]'] && eventData['xaxis.range[1]']) {
+            Plotly.relayout(div, { 'xaxis.range': [eventData['xaxis.range[0]'], eventData['xaxis.range[1]']] });
+          }
+        });
+      });
     });
   }
 
@@ -319,18 +363,17 @@ export function renderMeasurementResults(measurementResults, language) {
       const tr = document.createElement('tr');
       rowData.forEach((cellData, cellIndex) => {
         const cell = document.createElement(rowIndex === 0 ? 'th' : 'td');
-        // For headers, allow HTML rendering (dsiUnits output) by setting innerHTML.
         if (rowIndex === 0) {
           cell.innerHTML = cellData;
+          if (cellData !== 'Comments' && cellIndex > 0) {
+            const qtyIndex = Math.floor((cellIndex - 1) / 2);
+            cell.style.backgroundColor = lightPalette[qtyIndex % lightPalette.length];
+          }
         } else {
           cell.textContent = cellData;
         }
         cell.style.padding = '4px';
         cell.style.border = '1px solid #ccc';
-        if (rowIndex === 0 && cellIndex > 0) {
-          const dataIndex = Math.floor((cellIndex - 1) / 3);
-          cell.style.backgroundColor = lightPalette[dataIndex % lightPalette.length];
-        }
         tr.appendChild(cell);
       });
       tr.addEventListener('mouseover', () => { tr.style.backgroundColor = '#eef'; });
@@ -357,6 +400,8 @@ export function renderMeasurementResults(measurementResults, language) {
   radios.forEach(radio => {
     radio.addEventListener('change', updateVisualization);
   });
+  document.getElementById('logXToggle').addEventListener('change', updateVisualization);
+  document.getElementById('logYToggle').addEventListener('change', updateVisualization);
   toleranceToggle.addEventListener('change', () => {
     console.log('Tolerance toggle:', toleranceToggle.checked);
   });
-- 
GitLab