/* * Open-Source tuning tools * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package com.vgi.mafscaling; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.TreeMap; import java.util.regex.Pattern; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextPane; import javax.swing.ScrollPaneConstants; import org.apache.log4j.Logger; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.SeriesRenderingOrder; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYSplineRenderer; import org.jfree.chart.title.LegendTitle; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.ui.RectangleEdge; import org.jfree.util.ShapeUtilities; public class MafRescale extends JTabbedPane implements IMafChartHolder, ActionListener { private static final long serialVersionUID = -3803091816206090707L; private static final Logger logger = Logger.getLogger(MafRescale.class); private static final int ColumnWidth = 50; private static final int CellsPerSection = 2; private static final String OrigMafTableName = "Original MAF Scaling"; private static final String NewMafTableName = "New MAF Scaling"; private static final String XAxisName = "MAF Sensor (Voltage)"; private static final String YAxisName = "Mass Airflow (g/s)"; private static final String currentDataName = "Original"; private static final String correctedDataName = "Rescaled"; private final XYSeries currMafData = new XYSeries(currentDataName); private final XYSeries corrMafData = new XYSeries(correctedDataName); private JTable origMafTable = null; private JTable newMafTable = null; private TableCellListener newMafTableCellListener = null; private JFormattedTextField newMaxVFmtTextBox = null; private JFormattedTextField maxVUnchangedFmtTextBox = null; private JFormattedTextField minVFmtTextBox = null; private JFormattedTextField modeDeltaVFmtTextBox = null; private Insets insets0 = new Insets(0, 0, 0, 0); private Insets insets3 = new Insets(3, 3, 3, 3); private MafChartPanel mafChartPanel = null; private ExcelAdapter excelAdapter = null; private ExcelAdapter newMafExcelAdapter = null; private ArrayList<Double> origVoltArray = null; private ArrayList<Double> origGsArray = null; private ArrayList<Double> deltaVoltArray = null; private double modeDeltaV; public MafRescale(int tabPlacement) { super(tabPlacement); excelAdapter = new ExcelAdapter() { protected void onPaste(JTable table, boolean extendRows, boolean extendCols) { super.onPaste(table, extendRows, extendCols); calculateModeDeltaV(); } protected void onPasteVertical(JTable table, boolean extendRows, boolean extendCols) { super.onPasteVertical(table, extendRows, extendCols); calculateModeDeltaV(); } protected void onClearSelection(JTable table) { super.onClearSelection(table); calculateModeDeltaV(); updateNewMafScale(); } }; newMafExcelAdapter = new ExcelAdapter() { protected void onPaste(JTable table, boolean extendRows, boolean extendCols) { super.onPaste(table, extendRows, extendCols); recalculateNewGs(); } protected void onPasteVertical(JTable table, boolean extendRows, boolean extendCols) { super.onPasteVertical(table, extendRows, extendCols); recalculateNewGs(); } }; initialize(); } private void initialize() { createDataTab(); createUsageTab(); } public TableCellListener getNewMafTableCellListenerListener() { return newMafTableCellListener; } public void setNewMafTableCellListenerListener(TableCellListener listener) { newMafTableCellListener = listener; } ////////////////////////////////////////////////////////////////////////////////////// // DATA TAB ////////////////////////////////////////////////////////////////////////////////////// private void createDataTab() { JPanel dataPanel = new JPanel(); add(dataPanel, "<html><div style='text-align: center;'>D<br>a<br>t<br>a</div></html>"); GridBagLayout gbl_dataPanel = new GridBagLayout(); gbl_dataPanel.columnWidths = new int[] {0}; gbl_dataPanel.rowHeights = new int[] {0, 0, 0}; gbl_dataPanel.columnWeights = new double[]{0.0}; gbl_dataPanel.rowWeights = new double[]{0.0, 0.0, 1.0}; dataPanel.setLayout(gbl_dataPanel); createControlPanel(dataPanel); createMafScalesScrollPane(dataPanel); createGraghPanel(dataPanel); } private void createControlPanel(JPanel dataPanel) { JPanel cntlPanel = new JPanel(); GridBagConstraints gbl_ctrlPanel = new GridBagConstraints(); gbl_ctrlPanel.insets = insets3; gbl_ctrlPanel.anchor = GridBagConstraints.PAGE_START; gbl_ctrlPanel.fill = GridBagConstraints.HORIZONTAL; gbl_ctrlPanel.weightx = 1.0; gbl_ctrlPanel.gridx = 0; gbl_ctrlPanel.gridy = 0; dataPanel.add(cntlPanel, gbl_ctrlPanel); GridBagLayout gbl_cntlPanel = new GridBagLayout(); gbl_cntlPanel.columnWidths = new int[]{0, 0, 0, 0, 0, 0, 0, 0}; gbl_cntlPanel.rowHeights = new int[]{0, 0}; gbl_cntlPanel.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0}; gbl_cntlPanel.rowWeights = new double[]{0}; cntlPanel.setLayout(gbl_cntlPanel); NumberFormat doubleFmt = NumberFormat.getNumberInstance(); doubleFmt.setGroupingUsed(false); doubleFmt.setMaximumIntegerDigits(1); doubleFmt.setMinimumIntegerDigits(1); doubleFmt.setMaximumFractionDigits(3); doubleFmt.setMinimumFractionDigits(1); doubleFmt.setRoundingMode(RoundingMode.HALF_UP); NumberFormat scaleDoubleFmt = NumberFormat.getNumberInstance(); scaleDoubleFmt.setGroupingUsed(false); scaleDoubleFmt.setMaximumIntegerDigits(1); scaleDoubleFmt.setMinimumIntegerDigits(1); scaleDoubleFmt.setMaximumFractionDigits(8); scaleDoubleFmt.setMinimumFractionDigits(1); scaleDoubleFmt.setRoundingMode(RoundingMode.HALF_UP); GridBagConstraints gbc_cntlPanelLabel = new GridBagConstraints(); gbc_cntlPanelLabel.anchor = GridBagConstraints.EAST; gbc_cntlPanelLabel.insets = new Insets(2, 3, 2, 1); gbc_cntlPanelLabel.gridx = 0; gbc_cntlPanelLabel.gridy = 0; GridBagConstraints gbc_cntlPanelInput = new GridBagConstraints(); gbc_cntlPanelInput.anchor = GridBagConstraints.WEST; gbc_cntlPanelInput.insets = new Insets(2, 1, 2, 3); gbc_cntlPanelInput.gridx = 1; gbc_cntlPanelInput.gridy = 0; cntlPanel.add(new JLabel("New Max V"), gbc_cntlPanelLabel); newMaxVFmtTextBox = new JFormattedTextField(doubleFmt); newMaxVFmtTextBox.setColumns(7); newMaxVFmtTextBox.addPropertyChangeListener("value", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { Object source = e.getSource(); if (source == newMaxVFmtTextBox) updateNewMafScale(); } }); cntlPanel.add(newMaxVFmtTextBox, gbc_cntlPanelInput); gbc_cntlPanelLabel.gridx += 2; cntlPanel.add(new JLabel("Min V"), gbc_cntlPanelLabel); minVFmtTextBox = new JFormattedTextField(doubleFmt); minVFmtTextBox.setColumns(7); minVFmtTextBox.addPropertyChangeListener("value", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { Object source = e.getSource(); if (source == minVFmtTextBox) updateNewMafScale(); } }); gbc_cntlPanelInput.gridx += 2; cntlPanel.add(minVFmtTextBox, gbc_cntlPanelInput); gbc_cntlPanelLabel.gridx += 2; cntlPanel.add(new JLabel("Max Unchanged"), gbc_cntlPanelLabel); maxVUnchangedFmtTextBox = new JFormattedTextField(doubleFmt); maxVUnchangedFmtTextBox.setColumns(7); maxVUnchangedFmtTextBox.addPropertyChangeListener("value", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { Object source = e.getSource(); if (source == maxVUnchangedFmtTextBox) updateNewMafScale(); } }); gbc_cntlPanelInput.gridx += 2; cntlPanel.add(maxVUnchangedFmtTextBox, gbc_cntlPanelInput); gbc_cntlPanelLabel.gridx += 2; cntlPanel.add(new JLabel("Mode deltaV"), gbc_cntlPanelLabel); modeDeltaVFmtTextBox = new JFormattedTextField(scaleDoubleFmt); modeDeltaVFmtTextBox.setColumns(7); modeDeltaVFmtTextBox.setEditable(false); modeDeltaVFmtTextBox.setBackground(new Color(210,210,210)); gbc_cntlPanelInput.gridx += 2; cntlPanel.add(modeDeltaVFmtTextBox, gbc_cntlPanelInput); } private void createMafScalesScrollPane(JPanel dataPanel) { JPanel mafPanel = new JPanel(); GridBagLayout gbl_mafPanelLayout = new GridBagLayout(); gbl_mafPanelLayout.columnWidths = new int[]{0}; gbl_mafPanelLayout.rowHeights = new int[]{0, 0}; gbl_mafPanelLayout.columnWeights = new double[]{0.0, 1.0}; gbl_mafPanelLayout.rowWeights = new double[]{0.0, 1.0}; mafPanel.setLayout(gbl_mafPanelLayout); JScrollPane mafScrollPane = new JScrollPane(mafPanel); mafScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); mafScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); GridBagConstraints gbl_mafScrollPane = new GridBagConstraints(); gbl_mafScrollPane.anchor = GridBagConstraints.PAGE_START; gbl_mafScrollPane.fill = GridBagConstraints.HORIZONTAL; gbl_mafScrollPane.insets = insets0; gbl_mafScrollPane.weightx = 1.0; gbl_mafScrollPane.gridx = 0; gbl_mafScrollPane.gridy = 1; gbl_mafScrollPane.ipady = 120; dataPanel.add(mafScrollPane, gbl_mafScrollPane); GridBagConstraints gbc_mafScrollPane = new GridBagConstraints(); gbc_mafScrollPane.anchor = GridBagConstraints.PAGE_START; gbc_mafScrollPane.weightx = 1.0; gbc_mafScrollPane.weighty = 1.0; gbc_mafScrollPane.insets = insets0; gbc_mafScrollPane.fill = GridBagConstraints.HORIZONTAL; gbc_mafScrollPane.gridx = 0; gbc_mafScrollPane.gridy = 0; MafTablePane origMafScrollPane = new MafTablePane(ColumnWidth, OrigMafTableName, false, false); origMafScrollPane.setBorder(null); origMafScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); origMafTable = origMafScrollPane.getJTable(); excelAdapter.addTable(origMafTable, false, false, false, false, false, false, false, false, true); mafPanel.add(origMafScrollPane, gbc_mafScrollPane); MafTablePane newMafScrollPane = new MafTablePane(ColumnWidth, NewMafTableName, false, true); newMafScrollPane.setBorder(null); newMafScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); newMafTable = newMafScrollPane.getJTable(); newMafExcelAdapter.addTable(newMafTable, false, false, false, false, false, false, false, false, true); gbc_mafScrollPane.gridy++; mafPanel.add(newMafScrollPane, gbc_mafScrollPane); Action action = new AbstractAction() { private static final long serialVersionUID = 1L; public void actionPerformed(ActionEvent e) { recalculateNewGs(); } }; setNewMafTableCellListenerListener(new TableCellListener(newMafTable, action)); } private void createGraghPanel(JPanel dataPanel) { JFreeChart chart = ChartFactory.createScatterPlot(null, null, null, null, PlotOrientation.VERTICAL, false, true, false); chart.setBorderVisible(true); mafChartPanel = new MafChartPanel(chart, this); GridBagConstraints gbl_chartPanel = new GridBagConstraints(); gbl_chartPanel.anchor = GridBagConstraints.PAGE_START; gbl_chartPanel.insets = insets0; gbl_chartPanel.fill = GridBagConstraints.BOTH; gbl_chartPanel.weightx = 1.0; gbl_chartPanel.weighty = 1.0; gbl_chartPanel.gridx = 0; gbl_chartPanel.gridy = 2; dataPanel.add(mafChartPanel.getChartPanel(), gbl_chartPanel); XYSplineRenderer lineRenderer = new XYSplineRenderer(3); lineRenderer.setUseFillPaint(true); lineRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator( StandardXYToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT, new DecimalFormat("0.00"), new DecimalFormat("0.00"))); Stroke stroke = new BasicStroke(2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f, null, 0.0f); lineRenderer.setSeriesStroke(0, stroke); lineRenderer.setSeriesStroke(1, stroke); lineRenderer.setSeriesPaint(0, new Color(201, 0, 0)); lineRenderer.setSeriesPaint(1, new Color(0, 0, 255)); lineRenderer.setSeriesShape(0, ShapeUtilities.createDiamond((float) 2.5)); lineRenderer.setSeriesShape(1, ShapeUtilities.createUpTriangle((float) 2.5)); ValueAxis mafvDomain = new NumberAxis(XAxisName); ValueAxis mafgsRange = new NumberAxis(YAxisName); XYSeriesCollection lineDataset = new XYSeriesCollection(); lineDataset.addSeries(currMafData); lineDataset.addSeries(corrMafData); XYPlot plot = chart.getXYPlot(); plot.setRangePannable(true); plot.setDomainPannable(true); plot.setDomainGridlinePaint(Color.DARK_GRAY); plot.setRangeGridlinePaint(Color.DARK_GRAY); plot.setBackgroundPaint(new Color(224, 224, 224)); plot.setSeriesRenderingOrder(SeriesRenderingOrder.FORWARD); plot.setDataset(0, lineDataset); plot.setRenderer(0, lineRenderer); plot.setDomainAxis(0, mafvDomain); plot.setRangeAxis(0, mafgsRange); plot.mapDatasetToDomainAxis(0, 0); plot.mapDatasetToRangeAxis(0, 0); LegendTitle legend = new LegendTitle(plot.getRenderer()); legend.setItemFont(new Font("Arial", 0, 10)); legend.setPosition(RectangleEdge.TOP); chart.addLegend(legend); } ////////////////////////////////////////////////////////////////////////////////////// // CREATE USAGE TAB ////////////////////////////////////////////////////////////////////////////////////// private void createUsageTab() { JTextPane usageTextArea = new JTextPane(); usageTextArea.setMargin(new Insets(10, 10, 10, 10)); usageTextArea.setContentType("text/html"); usageTextArea.setText(usage()); usageTextArea.setEditable(false); usageTextArea.setCaretPosition(0); JScrollPane textScrollPane = new JScrollPane(usageTextArea); textScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); textScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); add(textScrollPane, "<html><div style='text-align: center;'>U<br>s<br>a<br>g<br>e</div></html>"); } private String usage() { ResourceBundle bundle; bundle = ResourceBundle.getBundle("com.vgi.mafscaling.mafrescale"); return bundle.getString("usage"); } ////////////////////////////////////////////////////////////////////////////////////// // WORK FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////// private boolean getMafTableData(JTable mafTable, ArrayList<Double> voltArray, ArrayList<Double> gsArray) { String value; for (int i = 0; i < mafTable.getColumnCount(); ++i) { for (int j = 0; j < mafTable.getRowCount(); ++j) { value = mafTable.getValueAt(j, i).toString(); if (value.isEmpty()) return true; if (!Utils.validateDouble(value, j, i, mafTable.getName())) return false; } voltArray.add(Double.parseDouble(mafTable.getValueAt(0, i).toString())); gsArray.add(Double.parseDouble(mafTable.getValueAt(1, i).toString())); } if (voltArray.size() != gsArray.size()) { JOptionPane.showMessageDialog(null, "Data sets (volt/gs) in " + mafTable.getName() + " have different length", "Invalid Data", JOptionPane.ERROR_MESSAGE); return false; } return true; } private void calculateModeDeltaV() { origVoltArray = new ArrayList<Double>(); origGsArray = new ArrayList<Double>(); if (!getMafTableData(origMafTable, origVoltArray, origGsArray) || origVoltArray.size() == 0) return; deltaVoltArray = new ArrayList<Double>(); deltaVoltArray.add(0.0); int i; for (i = 1; i < origVoltArray.size(); ++i) deltaVoltArray.add(origVoltArray.get(i) - origVoltArray.get(i - 1)); modeDeltaV = Utils.mode(deltaVoltArray); modeDeltaVFmtTextBox.setValue(modeDeltaV); for (i = deltaVoltArray.size() - 1; i > 0; --i) { if (modeDeltaV == deltaVoltArray.get(i)) { // hack as if value being set is the same then change even is not sent and updateNewMafScale() is not triggered maxVUnchangedFmtTextBox.setValue(null); maxVUnchangedFmtTextBox.setValue(origVoltArray.get(i)); return; } } maxVUnchangedFmtTextBox.setValue(null); } private void calculateNewGs(ArrayList<Double> newVoltArray, ArrayList<Double> newGsArray) { TreeMap<Double, Integer> vgsTree = new TreeMap<Double, Integer>(); for (int i = origVoltArray.size() - 1; i >= 0; --i) vgsTree.put(origVoltArray.get(i), i); Map.Entry<Double, Integer> kv; double x0, y0, x, y, x1, y1; for (int i = 1; i < newVoltArray.size(); ++i) { x = newVoltArray.get(i); kv = vgsTree.floorEntry(x); if (kv == null) { newGsArray.add(0.0); continue; } x0 = kv.getKey(); if (x0 == x) { newGsArray.add(origGsArray.get(kv.getValue())); continue; } y0 = origGsArray.get(kv.getValue()); kv = vgsTree.ceilingEntry(x); if (kv == null) { newGsArray.add(0.0); continue; } x1 = kv.getKey(); y1 = origGsArray.get(kv.getValue()); y = Utils.linearInterpolation(x, x0, x1, y0, y1); newGsArray.add(y); } } private void recalculateNewGs() { try { if (origVoltArray.size() == 0 || origVoltArray.size() != origGsArray.size()) return; ArrayList<Double> newVoltArray = new ArrayList<Double>(); ArrayList<Double> newGsArray = new ArrayList<Double>(); newGsArray.add(origGsArray.get(0)); for (int i = 0; i < newMafTable.getColumnCount(); ++i) { if (Pattern.matches(Utils.fpRegex, newMafTable.getValueAt(0, i).toString())) newVoltArray.add(Double.valueOf(newMafTable.getValueAt(0, i).toString())); else break; } if (newVoltArray.size() != origVoltArray.size()) return; calculateNewGs(newVoltArray, newGsArray); for (int i = 0; i < newVoltArray.size(); ++i) newMafTable.setValueAt(newGsArray.get(i), 1, i); corrMafData.clear(); setXYSeries(corrMafData, newVoltArray, newGsArray); } catch (Exception e) { e.printStackTrace(); logger.error(e); } } private void updateNewMafScale() { try { corrMafData.clear(); currMafData.clear(); Utils.clearTable(newMafTable); if (newMaxVFmtTextBox.getValue() == null || maxVUnchangedFmtTextBox.getValue() == null || minVFmtTextBox.getValue() == null || origMafTable.getValueAt(0, 0).toString().isEmpty()) return; if (origVoltArray.size() == 0 || origVoltArray.size() != origGsArray.size()) return; if (origVoltArray.size() < 10) { JOptionPane.showMessageDialog(null, "It looks like you have only partial original MAF scale table", "Invalid Data", JOptionPane.ERROR_MESSAGE); return; } double newMafV = (((Number)newMaxVFmtTextBox.getValue()).doubleValue()); if (newMafV < origVoltArray.get(0)) { JOptionPane.showMessageDialog(null, "New Max V [" + newMafV + "] can't be lower than first MAF table value", "Invalid Data", JOptionPane.ERROR_MESSAGE); return; } if (newMafV > origVoltArray.get(origVoltArray.size() - 1)) { JOptionPane.showMessageDialog(null, "New Max V [" + newMafV + "] can't be higher than last MAF table value", "Invalid Data", JOptionPane.ERROR_MESSAGE); return; } double minV = (((Number)minVFmtTextBox.getValue()).doubleValue()); if (minV <= origVoltArray.get(1)) { JOptionPane.showMessageDialog(null, "Min V [" + minV + "] must be higher than second MAF table value", "Invalid Data", JOptionPane.ERROR_MESSAGE); return; } if (minV > newMafV) { JOptionPane.showMessageDialog(null, "Min V [" + minV + "] can't be higher than new MAF V value", "Invalid Data", JOptionPane.ERROR_MESSAGE); return; } double maxVUnch = (((Number)maxVUnchangedFmtTextBox.getValue()).doubleValue()); if (maxVUnch <= minV) { JOptionPane.showMessageDialog(null, "Max Unchanged [" + maxVUnch + "] must be higher than Min V value", "Invalid Data", JOptionPane.ERROR_MESSAGE); return; } if (maxVUnch > newMafV) { JOptionPane.showMessageDialog(null, "Max Unchanged [" + maxVUnch + "] can't be higher than new MAF V value", "Invalid Data", JOptionPane.ERROR_MESSAGE); return; } int i, j, z; ArrayList<Double> newVoltArray = new ArrayList<Double>(); ArrayList<Double> newGsArray = new ArrayList<Double>(); newVoltArray.add(origVoltArray.get(0)); newGsArray.add(origGsArray.get(0)); // Find first value greater than MinV from original scale, // calculate mid-point and add them as second and third values to the new array. // After that simply copy all original values up until Max Unchanged value. boolean minFound = false; double val; for (i = 2; i < origVoltArray.size(); ++i) { val = origVoltArray.get(i); if (minFound) { if (val <= maxVUnch) newVoltArray.add(val); else break; } else if (minV <= val) { newVoltArray.add((val - origVoltArray.get(0)) / 2.0 + origVoltArray.get(0)); newVoltArray.add(val); minFound = true; } } int newMaxUnchIdx = newVoltArray.size() - 1; // Find avg % change per section in the original scale but for the same number of points as new scale double pointsCount = origVoltArray.size() - newVoltArray.size(); int sectionCount = (int)Math.ceil((double)pointsCount / (double)CellsPerSection); List<Double> modDeltaList = deltaVoltArray.subList(newMaxUnchIdx, deltaVoltArray.size()); double avgDelta = Utils.mean(modDeltaList); double maxDelta = Collections.max(modDeltaList); double minDelta = Collections.min(modDeltaList); double avgSectionChange = (maxDelta - minDelta) / sectionCount; double changePercent = (maxDelta - minDelta) / avgSectionChange; // Calculate delta per section double delta; ArrayList<Double> adj = new ArrayList<Double>(); for (i = 0; i < sectionCount; ++i) adj.add(avgDelta); int end = (int) Math.floor(sectionCount / 2.0); for (i = 0, j = sectionCount - 1; i < j; ++i, --j) { delta = avgDelta / 100.00 * changePercent * (end - i); adj.set(i, avgDelta - delta); adj.set(j, avgDelta + delta); } // Apply diff for each cell of each section for (i = newMaxUnchIdx + 1, j = 0, z = 0; i < origVoltArray.size(); ++i, ++j) { double diff = adj.get(z); if (j >= CellsPerSection) { j = 0; ++z; diff = adj.get(z); } newVoltArray.add(newVoltArray.get(i - 1) + diff); } // Since the above diffs are based of the original scale change simply adjust the new values to fit the new scale double corr = (newMafV - newVoltArray.get(newVoltArray.size() - 1)) / pointsCount; for (i = newMaxUnchIdx + 1, j = 1; i < newVoltArray.size(); ++i, ++j) newVoltArray.set(i, newVoltArray.get(i) + j * corr); calculateNewGs(newVoltArray, newGsArray); Utils.ensureColumnCount(newVoltArray.size(), newMafTable); for (i = 0; i < newVoltArray.size(); ++i) { newMafTable.setValueAt(newVoltArray.get(i), 0, i); newMafTable.setValueAt(newGsArray.get(i), 1, i); } setXYSeries(currMafData, origVoltArray, origGsArray); setXYSeries(corrMafData, newVoltArray, newGsArray); setRanges(mafChartPanel); } catch (Exception e) { logger.error(e); } } private boolean setXYSeries(XYSeries series, ArrayList<Double> xarr, ArrayList<Double> yarr) { if (xarr.size() == 0 || xarr.size() != yarr.size()) return false; series.clear(); for (int i = 0; i < xarr.size(); ++i) series.add(xarr.get(i), yarr.get(i), false); series.fireSeriesChanged(); return true; } private void setRanges(MafChartPanel chartPanel) { double paddingX = currMafData.getMaxX() * 0.05; double paddingY = currMafData.getMaxY() * 0.05; chartPanel.getChartPanel().getChart().getXYPlot().getDomainAxis(0).setRange(currMafData.getMinX() - paddingX, currMafData.getMaxX() + paddingX); chartPanel.getChartPanel().getChart().getXYPlot().getRangeAxis(0).setRange(currMafData.getMinY() - paddingY, currMafData.getMaxY() + paddingY); } @Override public void onMovePoint(int itemIndex, double valueX, double valueY) { newMafTable.setValueAt(valueX, 0, itemIndex); newMafTable.setValueAt(valueY, 1, itemIndex); } @Override public void actionPerformed(ActionEvent e) { } }