|
JMeter example source code file (Spline3.java)
The JMeter Spline3.java source code/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.jmeter.visualizers; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; /* * TODO : - implement ImageProducer interface - suggestions ;-) */ /** * This class implements the representation of an interpolated Spline curve. * <P> * The curve described by such an object interpolates an arbitrary number of * fixed points called <I>nodes. The distance between two nodes should * currently be constant. This is about to change in a later version but it can * last a while as it's not really needed. Nevertheless, if you need the * feature, just <a href="mailto:norguet@bigfoot.com?subject=Spline3eq">write me * a note</a> and I'll write it asap. * <P> * The interpolated Spline curve can't be described by an polynomial analytic * equation, the degree of which would be as high as the number of nodes, which * would cause extreme oscillations of the curve on the edges. * <P> * The solution is to split the curve accross a lot of little <I>intervals : * an interval starts at one node and ends at the next one. Then, the * interpolation is done on each interval, according to the following conditions : * <OL> * <LI>the interpolated curve is degree 3 : it's a cubic curve ; * <LI>the interpolated curve contains the two points delimiting the interval. * This condition obviously implies the curve is continuous ; * <LI>the interpolated curve has a smooth slope : the curvature has to be the * same on the left and the right sides of each node ; * <LI>the curvature of the global curve is 0 at both edges. * </OL> * Every part of the global curve is represented by a cubic (degree-3) * polynomial, the coefficients of which have to be computed in order to meet * the above conditions. * <P> * This leads to a n-unknow n-equation system to resolve. One can resolve an * equation system by several manners ; this class uses the Jacobi iterative * method, particularly well adapted to this situation, as the diagonal of the * system matrix is strong compared to the other elements. This implies the * algorithm always converges ! This is not the case of the Gauss-Seidel * algorithm, which is quite faster (it uses intermediate results of each * iteration to speed up the convergence) but it doesn't converge in all the * cases or it converges to a wrong value. This is not acceptable and that's why * the Jacobi method is safer. Anyway, the gain of speed is about a factor of 3 * but, for a 100x100 system, it means 10 ms instead of 30 ms, which is a pretty * good reason not to explore the question any further :) * <P> * Here is a little piece of code showing how to use this class : * * <PRE> // ... float[] nodes = {3F, 2F, 4F, 1F, 2.5F, 5F, 3F}; Spline3 curve = * new Spline3(nodes); // ... public void paint(Graphics g) { int[] plot = * curve.getPlots(); for (int i = 1; i < n; i++) { g.drawLine(i - 1, plot[i - * 1], i, plot[i]); } } // ... * * </PRE> * */ public class Spline3 { private static final Logger log = LoggingManager.getLoggerForClass(); protected float[][] _coefficients; protected float[][] _A; protected float[] _B; protected float[] _r; protected float[] _rS; protected int _m; // number of nodes protected int _n; // number of non extreme nodes (_m-2) final static protected float DEFAULT_PRECISION = (float) 1E-1; final static protected int DEFAULT_MAX_ITERATIONS = 100; protected float _minPrecision = DEFAULT_PRECISION; protected int _maxIterations = DEFAULT_MAX_ITERATIONS; /** * Creates a new Spline curve by calculating the coefficients of each part * of the curve, i.e. by resolving the equation system implied by the * interpolation condition on every interval. * * @param r * an array of float containing the vertical coordinates of the * nodes to interpolate ; the vertical coordinates start at 0 and * are equidistant with a step of 1. */ public Spline3(float[] r) { int n = r.length; // the number of nodes is defined by the length of r this._m = n; // grab the nodes this._r = new float[n]; for (int i = 0; i < n; i++) { _r[i] = r[i]; } // the number of non extreme nodes is the number of intervals // minus 1, i.e. the length of r minus 2 this._n = n - 2; // computes interpolation coefficients try { long startTime = System.currentTimeMillis(); this.interpolation(); if (log.isDebugEnabled()) { long endTime = System.currentTimeMillis(); long elapsedTime = endTime - startTime; if (log.isDebugEnabled()) { log.debug("New Spline curve interpolated in "); log.debug(elapsedTime + " ms"); } } } catch (Exception e) { log.error("Error when interpolating : ", e); } } /** * Computes the coefficients of the Spline interpolated curve, on each * interval. The matrix system to resolve is <CODE>AX=B */ protected void interpolation() { // creation of the interpolation structure _rS = new float[_m]; _B = new float[_n]; _A = new float[_n][_n]; _coefficients = new float[_n + 1][4]; // local variables int i = 0, j = 0; // initialize system structures (just to be safe) for (i = 0; i < _n; i++) { _B[i] = 0; for (j = 0; j < _n; j++) { _A[i][j] = 0; } for (j = 0; j < 4; j++) { _coefficients[i][j] = 0; } } for (i = 0; i < _n; i++) { _rS[i] = 0; } // initialize the diagonal of the system matrix (A) to 4 for (i = 0; i < _n; i++) { _A[i][i] = 4; } // initialize the two minor diagonals of A to 1 for (i = 1; i < _n; i++) { _A[i][i - 1] = 1; _A[i - 1][i] = 1; } // initialize B for (i = 0; i < _n; i++) { _B[i] = 6 * (_r[i + 2] - 2 * _r[i + 1] + _r[i]); } // Jacobi system resolving this.jacobi(); // results are stored in _rS // computes the coefficients (di, ci, bi, ai) from the results for (i = 0; i < _n + 1; i++) { // di (degree 0) _coefficients[i][0] = _r[i]; // ci (degree 1) _coefficients[i][1] = _r[i + 1] - _r[i] - (_rS[i + 1] + 2 * _rS[i]) / 6; // bi (degree 2) _coefficients[i][2] = _rS[i] / 2; // ai (degree 3) _coefficients[i][3] = (_rS[i + 1] - _rS[i]) / 6; } } /** * Resolves the equation system by a Jacobi algorithm. The use of the slower * Jacobi algorithm instead of Gauss-Seidel is choosen here because Jacobi * is assured of to be convergent for this particular equation system, as * the system matrix has a strong diagonal. */ protected void jacobi() { // local variables int i = 0, j = 0, iterations = 0; // intermediate arrays float[] newX = new float[_n]; float[] oldX = new float[_n]; // Jacobi convergence test if (!converge()) { if (log.isDebugEnabled()) { log.debug("Warning : equation system resolving is unstable"); } } // init newX and oldX arrays to 0 for (i = 0; i < _n; i++) { newX[i] = 0; oldX[i] = 0; } // main iteration while ((this.precision(oldX, newX) > this._minPrecision) && (iterations < this._maxIterations)) { for (i = 0; i < _n; i++) { oldX[i] = newX[i]; } for (i = 0; i < _n; i++) { newX[i] = _B[i]; for (j = 0; j < i; j++) { newX[i] = newX[i] - (_A[i][j] * oldX[j]); } for (j = i + 1; j < _n; j++) { newX[i] = newX[i] - (_A[i][j] * oldX[j]); } newX[i] = newX[i] / _A[i][i]; } iterations++; } if (this.precision(oldX, newX) < this._minPrecision) { if (log.isDebugEnabled()) { log.debug("Minimal precision ("); log.debug(this._minPrecision + ") reached after "); log.debug(iterations + " iterations"); } } else if (iterations > this._maxIterations) { if (log.isDebugEnabled()) { log.debug("Maximal number of iterations ("); log.debug(this._maxIterations + ") reached"); log.debug("Warning : precision is only "); log.debug("" + this.precision(oldX, newX)); log.debug(", divergence is possible"); } } for (i = 0; i < _n; i++) { _rS[i + 1] = newX[i]; } } /** * Test if the Jacobi resolution of the equation system converges. It's OK * if A has a strong diagonal. */ protected boolean converge() { boolean converge = true; int i = 0, j = 0; float lineSum = 0F; for (i = 0; i < _n; i++) { if (converge) { lineSum = 0; for (j = 0; j < _n; j++) { lineSum = lineSum + Math.abs(_A[i][j]); } lineSum = lineSum - Math.abs(_A[i][i]); if (lineSum > Math.abs(_A[i][i])) { converge = false; } } } return converge; } /** * Computes the current precision reached. */ protected float precision(float[] oldX, float[] newX) { float N = 0F, D = 0F, erreur = 0F; int i = 0; for (i = 0; i < _n; i++) { N = N + Math.abs(newX[i] - oldX[i]); D = D + Math.abs(newX[i]); } if (D != 0F) { erreur = N / D; } else { erreur = Float.MAX_VALUE; } return erreur; } /** * Computes a (vertical) Y-axis value of the global curve. * * @param t * abscissa * @return computed ordinate */ public float value(float t) { int i = 0, splineNumber = 0; float abscissa = 0F, result = 0F; // verify t belongs to the curve (range [0, _m-1]) if ((t < 0) || (t > (_m - 1))) { if (log.isDebugEnabled()) { log.debug("Warning : abscissa " + t + " out of bounds [0, " + (_m - 1) + "]"); } // silent error, consider the curve is constant outside its range if (t < 0) { t = 0; } else { t = _m - 1; } } // seek the good interval for t and get the piece of curve on it splineNumber = (int) Math.floor(t); if (t == (_m - 1)) { // the upper limit of the curve range belongs by definition // to the last interval splineNumber--; } // computes the value of the curve at the pecified abscissa // and relative to the beginning of the right piece of Spline curve abscissa = t - splineNumber; // the polynomial calculation is done by the (fast) Euler method for (i = 0; i < 4; i++) { result = result * abscissa; result = result + _coefficients[splineNumber][3 - i]; } return result; } /** * Manual check of the curve at the interpolated points. */ public void debugCheck() { int i = 0; for (i = 0; i < _m; i++) { log.info("Point " + i + " : "); log.info(_r[i] + " =? " + value(i)); } } /** * Computes drawable plots from the curve for a given draw space. The values * returned are drawable vertically and from the <B>bottom of a Panel. * * @param width * width within the plots have to be computed * @param height * height within the plots are expected to be drawed * @return drawable plots within the limits defined, in an array of int (as * many int as the value of the <CODE>width parameter) */ public int[] getPlots(int width, int height) { int[] plot = new int[width]; // computes auto-scaling and absolute plots float[] y = new float[width]; float max = java.lang.Integer.MIN_VALUE; float min = java.lang.Integer.MAX_VALUE; for (int i = 0; i < width; i++) { y[i] = value(((float) i) * (_m - 1) / width); if (y[i] < min) { min = y[i]; } if (y[i] > max) { max = y[i]; } } if (min < 0) { min = 0; // shouldn't draw negative values } // computes relative auto-scaled plots to fit in the specified area for (int i = 0; i < width; i++) { plot[i] = Math.round(((y[i] - min) * (height - 1)) / (max - min)); } return plot; } public void setPrecision(float precision) { this._minPrecision = precision; } public float getPrecision() { return this._minPrecision; } public void setToDefaultPrecision() { this._minPrecision = DEFAULT_PRECISION; } public float getDefaultPrecision() { return DEFAULT_PRECISION; } public void setMaxIterations(int iterations) { this._maxIterations = iterations; } public int getMaxIterations() { return this._maxIterations; } public void setToDefaultMaxIterations() { this._maxIterations = DEFAULT_MAX_ITERATIONS; } public int getDefaultMaxIterations() { return DEFAULT_MAX_ITERATIONS; } } Other JMeter examples (source code examples)Here is a short list of links related to this JMeter Spline3.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.