diff --git a/src/App/Expression.cpp b/src/App/Expression.cpp index bf17369569..3100afdf4b 100644 --- a/src/App/Expression.cpp +++ b/src/App/Expression.cpp @@ -678,7 +678,12 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f case ATAN2: case POW: if (args.size() != 2) - throw ExpressionError("Invalid number of arguments: eaxctly two required."); + throw ExpressionError("Invalid number of arguments: exactly two required."); + break; + case HYPOT: + case CATH: + if (args.size() < 2 || args.size() > 3) + throw ExpressionError("Invalid number of arguments: exactly two, or three required."); break; case STDDEV: case SUM: @@ -922,8 +927,10 @@ Expression * FunctionExpression::eval() const std::unique_ptr e1(args[0]->eval()); std::unique_ptr e2(args.size() > 1 ? args[1]->eval() : 0); + std::unique_ptr e3(args.size() > 2 ? args[2]->eval() : 0); NumberExpression * v1 = freecad_dynamic_cast(e1.get()); NumberExpression * v2 = freecad_dynamic_cast(e2.get()); + NumberExpression * v3 = freecad_dynamic_cast(e3.get()); double output; Unit unit; double scaler = 1; @@ -1026,6 +1033,16 @@ Expression * FunctionExpression::eval() const } break; } + case HYPOT: + case CATH: + if (v1->getUnit() != v2->getUnit()) + throw ExpressionError("Units must be equal"); + if (args.size() > 2) { + if (v2->getUnit() != v3->getUnit()) + throw ExpressionError("Units must be equal"); + } + unit = v1->getUnit(); + break; default: assert(0); } @@ -1086,6 +1103,14 @@ Expression * FunctionExpression::eval() const output = pow(value, v2->getValue()); break; } + case HYPOT: { + output = sqrt(pow(v1->getValue(), 2) + pow(v2->getValue(), 2) + (v3 ? pow(v3->getValue(), 2) : 0)); + break; + } + case CATH: { + output = sqrt(pow(v1->getValue(), 2) - pow(v2->getValue(), 2) - (v3 ? pow(v3->getValue(), 2) : 0)); + break; + } case ROUND: output = boost::math::round(value); break; @@ -1190,6 +1215,10 @@ std::string FunctionExpression::toString() const return "atan2(" + ss.str() + ")"; case POW: return "pow(" + ss.str() + ")"; + case HYPOT: + return "hypot(" + ss.str() + ")"; + case CATH: + return "cath(" + ss.str() + ")"; case ROUND: return "round(" + ss.str() + ")"; case TRUNC: @@ -1792,6 +1821,8 @@ static void initParser(const App::DocumentObject *owner) registered_functions["trunc"] = FunctionExpression::TRUNC; registered_functions["ceil"] = FunctionExpression::CEIL; registered_functions["floor"] = FunctionExpression::FLOOR; + registered_functions["hypot"] = FunctionExpression::HYPOT; + registered_functions["cath"] = FunctionExpression::CATH; // Aggregates registered_functions["sum"] = FunctionExpression::SUM; diff --git a/src/App/Expression.h b/src/App/Expression.h index f197e94cce..410f4eb38e 100644 --- a/src/App/Expression.h +++ b/src/App/Expression.h @@ -328,6 +328,8 @@ public: TRUNC, CEIL, FLOOR, + HYPOT, + CATH, // Aggregates AGGREGATES, diff --git a/src/Mod/Spreadsheet/TestSpreadsheet.py b/src/Mod/Spreadsheet/TestSpreadsheet.py index 62df39e5b5..30af73a13c 100644 --- a/src/Mod/Spreadsheet/TestSpreadsheet.py +++ b/src/Mod/Spreadsheet/TestSpreadsheet.py @@ -229,6 +229,25 @@ class SpreadsheetCases(unittest.TestCase): sheet.set('B21', '=pow(-7; 4)') sheet.set('C21', '=pow(7mm; 4)') sheet.set('D21', '=pow(7mm; 4mm)') + sheet.set('A23', '=hypot(3; 4)') # Hypot + sheet.set('B23', '=hypot(-3; 4)') + sheet.set('C23', '=hypot(3mm; 4)') + sheet.set('D23', '=hypot(3mm; 4mm)') + sheet.set('A24', '=hypot(3; 4; 5)') # Hypot + sheet.set('B24', '=hypot(-3; 4; 5)') + sheet.set('C24', '=hypot(3mm; 4; 5)') + sheet.set('D24', '=hypot(3mm; 4mm; 5mm)') + sheet.set('A26', '=cath(5; 3)') # Cath + sheet.set('B26', '=cath(-5; 3)') + sheet.set('C26', '=cath(5mm; 3)') + sheet.set('D26', '=cath(5mm; 3mm)') + + l = math.sqrt(5 * 5 + 4*4 + 3*3) + sheet.set('A27', '=cath(%0.15f; 5; 4)' % l) # Cath + sheet.set('B27', '=cath(%0.15f; -5; 4)' % l) + sheet.set('C27', '=cath(%0.15f mm; 5mm; 4)' % l) + sheet.set('D27', '=cath(%0.15f mm; 5mm; 4mm)' % l) + self.doc.recompute() self.assertMostlyEqual(sheet.A1, 0.5) # Cos self.assertMostlyEqual(sheet.B1, 0.5) @@ -309,6 +328,27 @@ class SpreadsheetCases(unittest.TestCase): self.assertMostlyEqual(sheet.B21, 2401) self.assertMostlyEqual(sheet.C21, Units.Quantity('2401mm^4')) self.assertEqual(sheet.D21, u'ERR: Exponent is not allowed to have a unit.') + self.assertMostlyEqual(sheet.A23, 5) # Hypot + self.assertMostlyEqual(sheet.B23, 5) + self.assertEqual(sheet.C23, u'ERR: Units must be equal') + self.assertMostlyEqual(sheet.D23, Units.Quantity('5mm')) + + l = math.sqrt(3*3 + 4*4 + 5*5) + self.assertMostlyEqual(sheet.A24, l) # Hypot + self.assertMostlyEqual(sheet.B24, l) + self.assertEqual(sheet.C24, u'ERR: Units must be equal') + self.assertMostlyEqual(sheet.D24, Units.Quantity("7.07106781186548 mm")) + self.assertMostlyEqual(sheet.A26, 4) # Cath + self.assertMostlyEqual(sheet.B26, 4) + self.assertEqual(sheet.C26, u'ERR: Units must be equal') + self.assertMostlyEqual(sheet.D26, Units.Quantity('4mm')) + + l = math.sqrt(5 * 5 + 4*4 + 3*3) + l = math.sqrt(l * l - 5*5 - 4*4) + self.assertMostlyEqual(sheet.A27, l) # Cath + self.assertMostlyEqual(sheet.B27, l) + self.assertEqual(sheet.C27, u'ERR: Units must be equal') + self.assertMostlyEqual(sheet.D27, Units.Quantity("3 mm")) FreeCAD.closeDocument(doc.Name) def testRelationalOperators(self):