/* * This file is part of LaTeXDraw. * Copyright (c) 2005-2017 Arnaud BLOUIN * LaTeXDraw 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. * LaTeXDraw is distributed 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. */ package net.sf.latexdraw.models; import net.sf.latexdraw.models.interfaces.shape.ILine; import net.sf.latexdraw.models.interfaces.shape.IPoint; /** * A singleton that contains helper geom methods. * @author Arnaud Blouin */ public final class MathUtils { public static final MathUtils INST = new MathUtils(); /** The threshold used to compare double values. */ public static final double THRESHOLD = 0.001; private MathUtils() { super(); } /** * @param pt The point to test. * @return True if the given point is valid (not NaN nor infinite nor null). * @since 3.0 */ public boolean isValidPt(final IPoint pt) { return pt != null && isValidPt(pt.getX(), pt.getY()); } /** * @param coord The value to test. * @return True if the given value is value (not NaN nor infinite). * @since 3.0 */ public boolean isValidCoord(final double coord) { return !(java.lang.Double.isNaN(coord) || java.lang.Double.isInfinite(coord)); } /** * @param x The X coordinates to test. * @param y The Y coordinates to test. * @return True if the given values are value (not NaN nor infinite). * @since 3.0 */ public boolean isValidPt(final double x, final double y) { return isValidCoord(x) && isValidCoord(y); } /** * Computes the altitude ha of the <b>right-triangle<b> ABC, right in A. * @param a The point A. * @param b The point B. * @param c The point C. * @return The altitude ha or 0. * @since 2.0.0 */ public double getAltitude(final IPoint a, final IPoint b, final IPoint c) { if(!isValidPt(a) || !isValidPt(b) || !isValidPt(c)) return 0d; final double ac = a.distance(c); final double ab = a.distance(b); if(MathUtils.INST.equalsDouble(ab, ac)) return a.distance((b.getX() + c.getX()) / 2d, (b.getY() + c.getY()) / 2d); return ab * ac / b.distance(c); } /** * Given a right-rectangle ABC right in A, it computes the gap created by * the corner of the triangle in B based on an initial gap. * @param a The point A. * @param b The point B. * @param c The point C. * @param gap The initial gap (for example, the thickness, the double border gap,...). * @return The gap created by the corner of the point B. * @since 2.0.0 */ public double getCornerGap(final IPoint a, final IPoint b, final IPoint c, final double gap) { if(!isValidPt(a) || !isValidPt(b) || !isValidPt(c)) return 0d; return gap / getAltitude(a, b, c) * a.distance(b); } /** * Creates the tangent to the ellipse at the given angle. * @param angle The position of the tangent point in radian * @param orientation Change the orientation of the tangent * @return The tangent. */ public ILine getTangenteAt(final IPoint tl, final IPoint br, final IPoint gc, final double angle, final boolean orientation) { final IPoint pt = ShapeFactory.INST.createPoint(br.getX(), (br.getY() + tl.getY()) / 2d).rotatePoint(gc, -angle); final double dec = 100d; final ILine tgt = ShapeFactory.INST.createLine(pt.getX(), pt.getY(), 0d, 0d); final float fAngle = MathUtils.INST.getCutNumber((float) angle); final float fPI = MathUtils.INST.getCutNumber((float) Math.PI); if(fAngle % fPI <= 0.01f) { tgt.setX2(pt.getX()); if(orientation) { tgt.setY2(pt.getY() - dec); }else { tgt.setY2(pt.getY() + dec); } }else { if(orientation) { tgt.setX2(pt.getX() - dec); }else { tgt.setX2(pt.getX() + dec); } if(fAngle % (fPI / 2f) <= 0.01f) { tgt.setY2(pt.getY()); }else { final double a = Math.abs(tl.getX() - gc.getX()); final double b = Math.abs(tl.getY() - gc.getY()); tgt.setY2(-(b * (pt.getX() - gc.getX()) * (tgt.getX2() - pt.getX())) / (a * (pt.getY() - gc.getY())) + pt.getY()); } } tgt.updateAandB(); return tgt; } /** * Compares two double values to know if they are approximately equal. * @param a The first double value. * @param b The second double value. * @param threshold The threshold used to compare the given values. * @return True if both values are approximatively equal using a threshold approximation value. * @since 3.0 */ public boolean equalsDouble(final double a, final double b, final double threshold) { return Math.abs(a - b) <= threshold; } /** * Compares two double values to know if they are approximatively equal. * @param a The first double value. * @param b The second double value. * @return True if both values are approximatively equal using a threshold approximation value. * @since 3.0 */ public boolean equalsDouble(final double a, final double b) { return equalsDouble(a, b, THRESHOLD); } /** * See getCutNumber(float value, double threshold). The threshold is by default the constant value THRESHOLD. * @param value The value to cut. * @return The cut or the intact number. * @since 3.0 */ public float getCutNumber(final float value) { return getCutNumber(value, THRESHOLD); } /** * See getCutNumber(double value, double threshold). The threshold is by default the constant value THRESHOLD. * @param value The value to cut. * @return The cut or the intact number. * @since 3.0 */ public double getCutNumber(final double value) { return getCutNumber(value, THRESHOLD); } /** * See getCutNumber(double value, double threshold). The threshold is by default the constant value THRESHOLD. * @param value The value to cut. * @return The cut or the intact number. * @since 3.0 */ public float getCutNumberFloat(final double value) { return (float) getCutNumber(value, THRESHOLD); } /** * This method allows to set a threshold that if a number is under it, it will be considered as valuing 0; * for instance, given the numbers 2E-10 and 0.002 and the * threshold 0.00001; then this method will cut the first number and will return 0. The absolute value of * the second number is not lesser than the threshold so it will be returned intact. * @param value The number to check. * @param threshold The minimum threshold of the value. * @return The cut or the intact number. * @since 1.9 */ public float getCutNumber(final float value, final double threshold) { return Math.abs(value) < threshold ? 0f : value; } /** * This method allows to set a threshold that if a number is under it, it will be considered as valuing 0; * for instance, given the numbers 2E-10 and 0.002 and the * threshold 0.00001; then this method will cut the first number and will return 0. The absolute value of * the second number is not lesser than the threshold so it will be returned intact. * @param value The number to check. * @param threshold The minimum threshold of the value. * @return The cut or the intact number. * @since 1.9 */ public double getCutNumber(final double value, final double threshold) { return Math.abs(value) < threshold ? 0d : value; } }