FreeCAD
1577 строк · 64.8 Кб
1# ***************************************************************************
2# * Copyright (c) 2016 Werner Mayer <wmayer[at]users.sourceforge.net> *
3# * Copyright (c) 2016 Eivind Kvedalen <eivind@kvedalen.name> *
4# * *
5# * This program is free software; you can redistribute it and/or modify *
6# * it under the terms of the GNU General Public License (GPL) *
7# * as published by the Free Software Foundation; either version 2 of *
8# * the License, or (at your option) any later version. *
9# * for detail see the LICENCE text file. *
10# * *
11# * FreeCAD is distributed in the hope that it will be useful, *
12# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14# * GNU Library General Public License for more details. *
15# * *
16# * You should have received a copy of the GNU Library General Public *
17# * License along with FreeCAD; if not, write to the Free Software *
18# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
19# * USA *
20# ***************************************************************************/
21
22import os23import sys24import math25from math import sqrt26import unittest27import FreeCAD28import Part29import Sketcher30import tempfile31from FreeCAD import Base32from FreeCAD import Units33
34v = Base.Vector35
36# ----------------------------------------------------------------------------------
37# define the functions to test the FreeCAD Spreadsheet module and expression engine
38# ----------------------------------------------------------------------------------
39
40
41class SpreadsheetCases(unittest.TestCase):42def setUp(self):43self.doc = FreeCAD.newDocument()44self.TempPath = tempfile.gettempdir()45FreeCAD.Console.PrintLog(" Using temp path: " + self.TempPath + "\n")46
47def testAggregates(self):48"""Test all aggregate functions"""49sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")50sheet.set("B13", "4")51sheet.set("B14", "5")52sheet.set("B15", "6")53sheet.set("C13", "4mm")54sheet.set("C14", "5mm")55sheet.set("C15", "6mm")56sheet.set("C16", "6")57
58sheet.set("A1", "=sum(1)")59sheet.set("A2", "=sum(1;2)")60sheet.set("A3", "=sum(1;2;3)")61sheet.set("A4", "=sum(1;2;3;B13)")62sheet.set("A5", "=sum(1;2;3;B13:B15)")63
64sheet.set("B1", "=min(1)")65sheet.set("B2", "=min(1;2)")66sheet.set("B3", "=min(1;2;3)")67sheet.set("B4", "=min(1;2;3;B13)")68sheet.set("B5", "=min(1;2;3;B13:B15)")69
70sheet.set("C1", "=max(1)")71sheet.set("C2", "=max(1;2)")72sheet.set("C3", "=max(1;2;3)")73sheet.set("C4", "=max(1;2;3;B13)")74sheet.set("C5", "=max(1;2;3;B13:B15)")75
76sheet.set("D1", "=stddev(1)")77sheet.set("D2", "=stddev(1;2)")78sheet.set("D3", "=stddev(1;2;3)")79sheet.set("D4", "=stddev(1;2;3;B13)")80sheet.set("D5", "=stddev(1;2;3;B13:B15)")81
82sheet.set("E1", "=count(1)")83sheet.set("E2", "=count(1;2)")84sheet.set("E3", "=count(1;2;3)")85sheet.set("E4", "=count(1;2;3;B13)")86sheet.set("E5", "=count(1;2;3;B13:B15)")87
88sheet.set("F1", "=average(1)")89sheet.set("F2", "=average(1;2)")90sheet.set("F3", "=average(1;2;3)")91sheet.set("F4", "=average(1;2;3;B13)")92sheet.set("F5", "=average(1;2;3;B13:B15)")93
94sheet.set("G1", "=average(C13:C15)")95sheet.set("G2", "=min(C13:C15)")96sheet.set("G3", "=max(C13:C15)")97sheet.set("G4", "=count(C13:C15)")98sheet.set("G5", "=stddev(C13:C15)")99sheet.set("G6", "=sum(C13:C15)")100
101sheet.set("H1", "=average(C13:C16)")102sheet.set("H2", "=min(C13:C16)")103sheet.set("H3", "=max(C13:C16)")104sheet.set("H4", "=count(C13:C16)")105sheet.set("H5", "=stddev(C13:C16)")106sheet.set("H6", "=sum(C13:C16)")107
108self.doc.recompute()109self.assertEqual(sheet.A1, 1)110self.assertEqual(sheet.A2, 3)111self.assertEqual(sheet.A3, 6)112self.assertEqual(sheet.A4, 10)113self.assertEqual(sheet.A5, 21)114
115self.assertEqual(sheet.B1, 1)116self.assertEqual(sheet.B2, 1)117self.assertEqual(sheet.B3, 1)118self.assertEqual(sheet.B4, 1)119self.assertEqual(sheet.B5, 1)120
121self.assertEqual(sheet.C1, 1)122self.assertEqual(sheet.C2, 2)123self.assertEqual(sheet.C3, 3)124self.assertEqual(sheet.C4, 4)125self.assertEqual(sheet.C5, 6)126
127self.assertTrue(128sheet.D1.startswith("ERR: Invalid number of entries: at least two required.")129)130self.assertEqual(sheet.D2, 0.7071067811865476)131self.assertEqual(sheet.D3, 1.0)132self.assertEqual(sheet.D4, 1.2909944487358056)133self.assertEqual(sheet.D5, 1.8708286933869707)134
135self.assertEqual(sheet.E1, 1)136self.assertEqual(sheet.E2, 2)137self.assertEqual(sheet.E3, 3)138self.assertEqual(sheet.E4, 4)139self.assertEqual(sheet.E5, 6)140
141self.assertEqual(sheet.F1, 1)142self.assertEqual(sheet.F2, (1.0 + 2.0) / 2.0)143self.assertEqual(sheet.F3, (1.0 + 2 + 3) / 3)144self.assertEqual(sheet.F4, (1.0 + 2 + 3 + 4) / 4)145self.assertEqual(sheet.F5, (1.0 + 2 + 3 + 4 + 5 + 6) / 6)146
147self.assertEqual(sheet.G1, Units.Quantity("5 mm"))148self.assertEqual(sheet.G2, Units.Quantity("4 mm"))149self.assertEqual(sheet.G3, Units.Quantity("6 mm"))150self.assertEqual(sheet.G4, 3)151self.assertEqual(sheet.G5, Units.Quantity("1 mm"))152self.assertEqual(sheet.G6, Units.Quantity("15 mm"))153
154self.assertTrue(155sheet.H1.startswith("ERR: Quantity::operator +=(): Unit mismatch in plus operation")156)157self.assertTrue(158sheet.H2.startswith(159"ERR: Quantity::operator <(): quantities need to have same unit to compare"160)161)162self.assertTrue(163sheet.H3.startswith(164"ERR: Quantity::operator >(): quantities need to have same unit to compare"165)166)167self.assertEqual(sheet.H4, 4)168self.assertTrue(169sheet.H5.startswith("ERR: Quantity::operator -(): Unit mismatch in minus operation")170)171self.assertTrue(172sheet.H6.startswith("ERR: Quantity::operator +=(): Unit mismatch in plus operation")173)174
175def assertMostlyEqual(self, a, b):176if type(a) is Units.Quantity:177self.assertTrue(math.fabs(a.Value - b.Value) < 1e-14)178self.assertTrue(a.Unit == b.Unit)179else:180self.assertTrue(math.fabs(a - b) < 1e-14)181
182def testFunctions(self):183"""Test all built-in simple functions"""184sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")185sheet.set("A1", "=cos(60)") # Cos186sheet.set("B1", "=cos(60deg)")187sheet.set("C1", "=cos(pi / 2 * 1rad)")188sheet.set("A2", "=sin(30)") # Sin189sheet.set("B2", "=sin(30deg)")190sheet.set("C2", "=sin(pi / 6 * 1rad)")191sheet.set("A3", "=tan(45)") # Tan192sheet.set("B3", "=tan(45deg)")193sheet.set("C3", "=tan(pi / 4 * 1rad)")194sheet.set("A4", "=abs(3)") # Abs195sheet.set("B4", "=abs(-3)")196sheet.set("C4", "=abs(-3mm)")197sheet.set("A5", "=exp(3)") # Exp198sheet.set("B5", "=exp(-3)")199sheet.set("C5", "=exp(-3mm)")200sheet.set("A6", "=log(3)") # Log201sheet.set("B6", "=log(-3)")202sheet.set("C6", "=log(-3mm)")203sheet.set("A7", "=log10(10)") # Log10204sheet.set("B7", "=log10(-3)")205sheet.set("C7", "=log10(-3mm)")206sheet.set("A8", "=round(3.4)") # Round207sheet.set("B8", "=round(3.6)")208sheet.set("C8", "=round(-3.4)")209sheet.set("D8", "=round(-3.6)")210sheet.set("E8", "=round(3.4mm)")211sheet.set("F8", "=round(3.6mm)")212sheet.set("G8", "=round(-3.4mm)")213sheet.set("H8", "=round(-3.6mm)")214sheet.set("A9", "=trunc(3.4)") # Trunc215sheet.set("B9", "=trunc(3.6)")216sheet.set("C9", "=trunc(-3.4)")217sheet.set("D9", "=trunc(-3.6)")218sheet.set("E9", "=trunc(3.4mm)")219sheet.set("F9", "=trunc(3.6mm)")220sheet.set("G9", "=trunc(-3.4mm)")221sheet.set("H9", "=trunc(-3.6mm)")222sheet.set("A10", "=ceil(3.4)") # Ceil223sheet.set("B10", "=ceil(3.6)")224sheet.set("C10", "=ceil(-3.4)")225sheet.set("D10", "=ceil(-3.6)")226sheet.set("E10", "=ceil(3.4mm)")227sheet.set("F10", "=ceil(3.6mm)")228sheet.set("G10", "=ceil(-3.4mm)")229sheet.set("H10", "=ceil(-3.6mm)")230sheet.set("A11", "=floor(3.4)") # Floor231sheet.set("B11", "=floor(3.6)")232sheet.set("C11", "=floor(-3.4)")233sheet.set("D11", "=floor(-3.6)")234sheet.set("E11", "=floor(3.4mm)")235sheet.set("F11", "=floor(3.6mm)")236sheet.set("G11", "=floor(-3.4mm)")237sheet.set("H11", "=floor(-3.6mm)")238sheet.set("A12", "=asin(0.5)") # Asin239sheet.set("B12", "=asin(0.5mm)")240sheet.set("A13", "=acos(0.5)") # Acos241sheet.set("B13", "=acos(0.5mm)")242sheet.set("A14", "=atan(sqrt(3))") # Atan243sheet.set("B14", "=atan(0.5mm)")244sheet.set("A15", "=sinh(0.5)") # Sinh245sheet.set("B15", "=sinh(0.5mm)")246sheet.set("A16", "=cosh(0.5)") # Cosh247sheet.set("B16", "=cosh(0.5mm)")248sheet.set("A17", "=tanh(0.5)") # Tanh249sheet.set("B17", "=tanh(0.5mm)")250sheet.set("A18", "=sqrt(4)") # Sqrt251sheet.set("B18", "=sqrt(4mm^2)")252sheet.set("A19", "=mod(7; 4)") # Mod253sheet.set("B19", "=mod(-7; 4)")254sheet.set("C19", "=mod(7mm; 4)")255sheet.set("D19", "=mod(7mm; 4mm)")256sheet.set("A20", "=atan2(3; 3)") # Atan2257sheet.set("B20", "=atan2(-3; 3)")258sheet.set("C20", "=atan2(3mm; 3)")259sheet.set("D20", "=atan2(3mm; 3mm)")260sheet.set("A21", "=pow(7; 4)") # Pow261sheet.set("B21", "=pow(-7; 4)")262sheet.set("C21", "=pow(7mm; 4)")263sheet.set("D21", "=pow(7mm; 4mm)")264sheet.set("A23", "=hypot(3; 4)") # Hypot265sheet.set("B23", "=hypot(-3; 4)")266sheet.set("C23", "=hypot(3mm; 4)")267sheet.set("D23", "=hypot(3mm; 4mm)")268sheet.set("A24", "=hypot(3; 4; 5)") # Hypot269sheet.set("B24", "=hypot(-3; 4; 5)")270sheet.set("C24", "=hypot(3mm; 4; 5)")271sheet.set("D24", "=hypot(3mm; 4mm; 5mm)")272sheet.set("A26", "=cath(5; 3)") # Cath273sheet.set("B26", "=cath(-5; 3)")274sheet.set("C26", "=cath(5mm; 3)")275sheet.set("D26", "=cath(5mm; 3mm)")276
277l = math.sqrt(5 * 5 + 4 * 4 + 3 * 3)278sheet.set("A27", "=cath(%0.15f; 5; 4)" % l) # Cath279sheet.set("B27", "=cath(%0.15f; -5; 4)" % l)280sheet.set("C27", "=cath(%0.15f mm; 5mm; 4)" % l)281sheet.set("D27", "=cath(%0.15f mm; 5mm; 4mm)" % l)282
283self.doc.recompute()284self.assertMostlyEqual(sheet.A1, 0.5) # Cos285self.assertMostlyEqual(sheet.B1, 0.5)286self.assertMostlyEqual(sheet.C1, 0)287self.assertMostlyEqual(sheet.A2, 0.5) # Sin288self.assertMostlyEqual(sheet.B2, 0.5)289self.assertMostlyEqual(sheet.C2, 0.5)290self.assertMostlyEqual(sheet.A3, 1) # Tan291self.assertMostlyEqual(sheet.B3, 1)292self.assertMostlyEqual(sheet.C3, 1)293self.assertMostlyEqual(sheet.A4, 3) # Abs294self.assertMostlyEqual(sheet.B4, 3)295self.assertMostlyEqual(sheet.C4, Units.Quantity("3 mm"))296self.assertMostlyEqual(sheet.A5, math.exp(3)) # Exp297self.assertMostlyEqual(sheet.B5, math.exp(-3))298self.assertTrue(sheet.C5.startswith("ERR: Unit must be empty."))299self.assertMostlyEqual(sheet.A6, math.log(3)) # Log300self.assertTrue(math.isnan(sheet.B6))301self.assertTrue(sheet.C6.startswith("ERR: Unit must be empty."))302self.assertMostlyEqual(sheet.A7, math.log10(10)) # Log10303self.assertTrue(math.isnan(sheet.B7))304self.assertTrue(sheet.C7.startswith("ERR: Unit must be empty."))305self.assertMostlyEqual(sheet.A8, 3) # Round306self.assertMostlyEqual(sheet.B8, 4)307self.assertMostlyEqual(sheet.C8, -3)308self.assertMostlyEqual(sheet.D8, -4)309self.assertEqual(sheet.E8, Units.Quantity("3 mm"))310self.assertEqual(sheet.F8, Units.Quantity("4 mm"))311self.assertEqual(sheet.G8, Units.Quantity("-3 mm"))312self.assertEqual(sheet.H8, Units.Quantity("-4 mm"))313self.assertMostlyEqual(sheet.A9, 3) # Trunc314self.assertMostlyEqual(sheet.B9, 3)315self.assertMostlyEqual(sheet.C9, -3)316self.assertMostlyEqual(sheet.D9, -3)317self.assertEqual(sheet.E9, Units.Quantity("3 mm"))318self.assertEqual(sheet.F9, Units.Quantity("3 mm"))319self.assertEqual(sheet.G9, Units.Quantity("-3 mm"))320self.assertEqual(sheet.H9, Units.Quantity("-3 mm"))321self.assertMostlyEqual(sheet.A10, 4) # Ceil322self.assertMostlyEqual(sheet.B10, 4)323self.assertMostlyEqual(sheet.C10, -3)324self.assertMostlyEqual(sheet.D10, -3)325self.assertMostlyEqual(sheet.E10, Units.Quantity("4 mm"))326self.assertMostlyEqual(sheet.F10, Units.Quantity("4 mm"))327self.assertMostlyEqual(sheet.G10, Units.Quantity("-3 mm"))328self.assertMostlyEqual(sheet.H10, Units.Quantity("-3 mm"))329self.assertMostlyEqual(sheet.A11, 3) # Floor330self.assertMostlyEqual(sheet.B11, 3)331self.assertMostlyEqual(sheet.C11, -4)332self.assertMostlyEqual(sheet.D11, -4)333self.assertMostlyEqual(sheet.E11, Units.Quantity("3 mm"))334self.assertMostlyEqual(sheet.F11, Units.Quantity("3 mm"))335self.assertMostlyEqual(sheet.G11, Units.Quantity("-4 mm"))336self.assertMostlyEqual(sheet.H11, Units.Quantity("-4 mm"))337self.assertMostlyEqual(sheet.A12, Units.Quantity("30 deg")) # Asin338self.assertTrue(sheet.B12.startswith("ERR: Unit must be empty."))339self.assertMostlyEqual(sheet.A13, Units.Quantity("60 deg")) # Acos340self.assertTrue(sheet.B13.startswith("ERR: Unit must be empty."))341self.assertMostlyEqual(sheet.A14, Units.Quantity("60 deg")) # Atan342self.assertTrue(sheet.B14.startswith("ERR: Unit must be empty."))343self.assertMostlyEqual(sheet.A15, math.sinh(0.5)) # Sinh344self.assertTrue(sheet.B15.startswith("ERR: Unit must be empty."))345self.assertMostlyEqual(sheet.A16, math.cosh(0.5)) # Cosh346self.assertTrue(sheet.B16.startswith("ERR: Unit must be empty."))347self.assertMostlyEqual(sheet.A17, math.tanh(0.5)) # Tanh348self.assertTrue(sheet.B17.startswith("ERR: Unit must be empty."))349self.assertMostlyEqual(sheet.A18, 2) # Sqrt350self.assertMostlyEqual(sheet.B18, Units.Quantity("2 mm"))351self.assertMostlyEqual(sheet.A19, 3) # Mod352self.assertMostlyEqual(sheet.B19, -3)353self.assertMostlyEqual(sheet.C19, Units.Quantity("3 mm"))354self.assertEqual(sheet.D19, 3)355self.assertMostlyEqual(sheet.A20, Units.Quantity("45 deg")) # Atan2356self.assertMostlyEqual(sheet.B20, Units.Quantity("-45 deg"))357self.assertTrue(sheet.C20.startswith("ERR: Units must be equal"))358self.assertMostlyEqual(sheet.D20, Units.Quantity("45 deg"))359self.assertMostlyEqual(sheet.A21, 2401) # Pow360self.assertMostlyEqual(sheet.B21, 2401)361self.assertMostlyEqual(sheet.C21, Units.Quantity("2401mm^4"))362self.assertTrue(sheet.D21.startswith("ERR: Exponent is not allowed to have a unit."))363self.assertMostlyEqual(sheet.A23, 5) # Hypot364self.assertMostlyEqual(sheet.B23, 5)365self.assertTrue(sheet.C23.startswith("ERR: Units must be equal"))366self.assertMostlyEqual(sheet.D23, Units.Quantity("5mm"))367
368l = math.sqrt(3 * 3 + 4 * 4 + 5 * 5)369self.assertMostlyEqual(sheet.A24, l) # Hypot370self.assertMostlyEqual(sheet.B24, l)371self.assertTrue(sheet.C24.startswith("ERR: Units must be equal"))372self.assertMostlyEqual(sheet.D24, Units.Quantity("7.07106781186548 mm"))373self.assertMostlyEqual(sheet.A26, 4) # Cath374self.assertMostlyEqual(sheet.B26, 4)375self.assertTrue(sheet.C26.startswith("ERR: Units must be equal"))376self.assertMostlyEqual(sheet.D26, Units.Quantity("4mm"))377
378l = math.sqrt(5 * 5 + 4 * 4 + 3 * 3)379l = math.sqrt(l * l - 5 * 5 - 4 * 4)380self.assertMostlyEqual(sheet.A27, l) # Cath381self.assertMostlyEqual(sheet.B27, l)382self.assertTrue(sheet.C27.startswith("ERR: Units must be equal"))383self.assertMostlyEqual(sheet.D27, Units.Quantity("3 mm"))384
385def testRelationalOperators(self):386"""Test relational operators"""387sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")388# All should be 1 as result389sheet.set("A1", "=1 == 1 ? 1 : 0")390sheet.set("A2", "=1 != 1 ? 0 : 1")391sheet.set("A3", "=1e9 == 1e9 ? 1 : 0")392sheet.set("A4", "=1e9 != 1e9 ? 0 : 1")393sheet.set("A5", "=1 > 1 ? 0 : 1")394sheet.set("A6", "=2 > 1 ? 1 : 0")395sheet.set("A7", "=1 > 2 ? 0 : 1")396sheet.set("A8", "=1 < 1 ? 0 : 1")397sheet.set("A9", "=1 < 2 ? 1 : 0")398sheet.set("A10", "=2 < 1 ? 0 : 1")399sheet.set("A11", "=1 >= 1 ? 1 : 0")400sheet.set("A12", "=2 >= 1 ? 1 : 0")401sheet.set("A13", "=1 >= 2 ? 0 : 1")402sheet.set("A14", "=1 <= 1 ? 1 : 1")403sheet.set("A15", "=1 <= 2 ? 1 : 0")404sheet.set("A16", "=2 <= 1 ? 0 : 1")405sheet.set("A17", "=1 >= 1.000000000000001 ? 0 : 1")406sheet.set("A18", "=1 >= 1.0000000000000001 ? 1 : 0")407sheet.set("A19", "=1 <= 1.000000000000001 ? 1 : 0")408sheet.set("A20", "=1 <= 1.0000000000000001 ? 1 : 0")409sheet.set("A21", "=1 == 1.000000000000001 ? 0 : 1")410sheet.set("A22", "=1 == 1.0000000000000001 ? 1 : 0")411sheet.set("A23", "=1 != 1.000000000000001 ? 1 : 0")412sheet.set("A24", "=1 != 1.0000000000000001 ? 0 : 1")413
414self.doc.recompute()415self.assertEqual(sheet.A1, 1)416self.assertEqual(sheet.A2, 1)417self.assertEqual(sheet.A3, 1)418self.assertEqual(sheet.A4, 1)419self.assertEqual(sheet.A5, 1)420self.assertEqual(sheet.A6, 1)421self.assertEqual(sheet.A7, 1)422self.assertEqual(sheet.A8, 1)423self.assertEqual(sheet.A9, 1)424self.assertEqual(sheet.A10, 1)425self.assertEqual(sheet.A11, 1)426self.assertEqual(sheet.A12, 1)427self.assertEqual(sheet.A13, 1)428self.assertEqual(sheet.A14, 1)429self.assertEqual(sheet.A15, 1)430self.assertEqual(sheet.A16, 1)431self.assertEqual(sheet.A17, 1)432self.assertEqual(sheet.A18, 1)433self.assertEqual(sheet.A19, 1)434self.assertEqual(sheet.A20, 1)435self.assertEqual(sheet.A21, 1)436self.assertEqual(sheet.A22, 1)437self.assertEqual(sheet.A23, 1)438self.assertEqual(sheet.A24, 1)439
440def testUnits(self):441"""Units -- test unit calculations."""442sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")443sheet.set("A1", "=2mm + 3mm")444sheet.set("A2", "=2mm - 3mm")445sheet.set("A3", "=2mm * 3mm")446sheet.set("A4", "=4mm / 2mm")447sheet.set("A5", "=(4mm)^2")448sheet.set("A6", "=5(mm^2)")449sheet.set("A7", "=5mm^2") # ^2 operates on whole number450sheet.set("A8", "=5")451sheet.set("A9", "=5*1/K") # Currently fails452sheet.set("A10", "=5 K^-1") # Currently fails453sheet.set("A11", "=9.8 m/s^2") # Currently fails454sheet.setDisplayUnit("A8", "1/K")455self.doc.recompute()456self.assertEqual(sheet.A1, Units.Quantity("5mm"))457self.assertEqual(sheet.A2, Units.Quantity("-1 mm"))458self.assertEqual(sheet.A3, Units.Quantity("6 mm^2"))459self.assertEqual(sheet.A4, Units.Quantity("2"))460self.assertEqual(sheet.A5, Units.Quantity("16 mm^2"))461self.assertEqual(sheet.A6, Units.Quantity("5 mm^2"))462self.assertEqual(sheet.A7, Units.Quantity("5 mm^2"))463self.assertEqual(sheet.A8, Units.Quantity("5"))464self.assertEqual(sheet.A9, Units.Quantity("5 K^-1"))465self.assertEqual(sheet.A10, Units.Quantity("5 K^-1"))466self.assertEqual(sheet.A11, Units.Quantity("9.8 m/s^2"))467
468def testPrecedence(self):469"""Precedence -- test precedence for relational operators and conditional operator."""470sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")471sheet.set("A1", "=1 < 2 ? 3 : 4")472sheet.set("A2", "=1 + 2 < 3 + 4 ? 5 + 6 : 7 + 8")473sheet.set("A3", "=1 + 2 * 1 < 3 + 4 ? 5 * 2 + 6 * 3 + 2 ^ 4 : 7 * 2 + 8 * 3 + 2 ^ 3")474sheet.set("A4", "=123")475sheet.set("A5", "=123 + 321")476sheet.set("A6", "=123 * 2 + 321")477sheet.set("A7", "=123 * 2 + 333 / 3")478sheet.set("A8", "=123 * (2 + 321)")479sheet.set("A9", "=3 ^ 4")480sheet.set("A10", "=3 ^ 4 * 2")481sheet.set("A11", "=3 ^ (4 * 2)")482sheet.set("A12", "=3 ^ 4 + 4")483sheet.set("A13", "=1 + 4 / 2 + 5")484sheet.set("A14", "=(3 + 6) / (1 + 2)")485sheet.set("A15", "=1 * 2 / 3 * 4")486sheet.set("A16", "=(1 * 2) / (3 * 4)")487# Test associativity488sheet.set(489"A17", "=3 ^ 4 ^ 2"490) # exponentiation is left-associative; to follow excel, openoffice, matlab, octave491sheet.set("A18", "=3 ^ (4 ^ 2)") # exponentiation is left-associative492sheet.set("A19", "=(3 ^ 4) ^ 2") # exponentiation is left-associative493sheet.set("A20", "=3 + 4 + 2")494sheet.set("A21", "=3 + (4 + 2)")495sheet.set("A22", "=(3 + 4) + 2")496sheet.set("A23", "=3 - 4 - 2")497sheet.set("A24", "=3 - (4 - 2)")498sheet.set("A25", "=(3 - 4) - 2")499sheet.set("A26", "=3 * 4 * 2")500sheet.set("A27", "=3 * (4 * 2)")501sheet.set("A28", "=(3 * 4) * 2")502sheet.set("A29", "=3 / 4 / 2")503sheet.set("A30", "=3 / (4 / 2)")504sheet.set("A31", "=(3 / 4) / 2")505sheet.set("A32", "=pi * 3")506sheet.set("A33", "=A32 / 3")507sheet.set("A34", "=1 < 2 ? <<A>> : <<B>>")508sheet.set("A35", "=min(A32:A33)")509sheet.set("A36", "=(1 < 2 ? 0 : 1) * 3")510sheet.set("A37", "=8/(2^2*2)")511sheet.set("A38", "=(2^2*2)/8")512sheet.set("A39", "=2^(2*2)/8")513sheet.set("A40", "=8/2^(2*2)")514sheet.set("A41", "=-1")515sheet.set("A42", "=-(1)")516sheet.set("A43", "=-(1 + 1)")517sheet.set("A44", "=-(1 - 1)")518sheet.set("A45", "=-(-1 + 1)")519sheet.set("A46", "=-(-1 + -1)")520sheet.set("A47", "=+1")521sheet.set("A48", "=+(1)")522sheet.set("A49", "=+(1 + 1)")523sheet.set("A50", "=+(1 - 1)")524sheet.set("A51", "=+(-1 + 1)")525sheet.set("A52", "=+(-1 + -1)")526
527self.doc.addObject("Part::Cylinder", "Cylinder")528# We cannot use Thickness, as this feature requires a source shape,529# otherwise it will cause recomputation failure. The new logic of530# App::Document will not continue recompute any dependent objects531
532# self.doc.addObject("Part::Thickness", "Pipe")533self.doc.addObject("Part::Box", "Box")534self.doc.Box.Length = 1535
536sheet.set("B1", "101")537sheet.set("A53", "=-(-(B1-1)/2)")538sheet.set("A54", '=-(Cylinder.Radius + Box.Length - 1"/2)')539
540self.doc.recompute()541self.assertEqual(sheet.getContents("A1"), "=1 < 2 ? 3 : 4")542self.assertEqual(sheet.getContents("A2"), "=1 + 2 < 3 + 4 ? 5 + 6 : 7 + 8")543self.assertEqual(544sheet.getContents("A3"),545"=1 + 2 * 1 < 3 + 4 ? 5 * 2 + 6 * 3 + 2 ^ 4 : 7 * 2 + 8 * 3 + 2 ^ 3",546)547self.assertEqual(sheet.A1, 3)548self.assertEqual(sheet.A2, 11)549self.assertEqual(sheet.A3, 44)550self.assertEqual(sheet.A4, 123)551self.assertEqual(sheet.A5, 444)552self.assertEqual(sheet.A6, 567)553self.assertEqual(sheet.A7, 357)554self.assertEqual(sheet.A8, 39729)555self.assertEqual(sheet.A9, 81)556self.assertEqual(sheet.A10, 162)557self.assertEqual(sheet.A11, 6561)558self.assertEqual(sheet.A12, 85)559self.assertEqual(sheet.A13, 8)560self.assertEqual(sheet.A14, 3)561self.assertEqual(sheet.A15, 8.0 / 3)562self.assertEqual(sheet.A16, 1.0 / 6)563self.assertEqual(sheet.A17, 6561)564self.assertEqual(sheet.A18, 43046721)565self.assertEqual(sheet.A19, 6561)566self.assertEqual(sheet.A20, 9)567self.assertEqual(sheet.A21, 9)568self.assertEqual(sheet.A22, 9)569self.assertEqual(sheet.A23, -3)570self.assertEqual(sheet.A24, 1)571self.assertEqual(sheet.A25, -3)572self.assertEqual(sheet.A26, 24)573self.assertEqual(sheet.A27, 24)574self.assertEqual(sheet.A28, 24)575self.assertEqual(sheet.A29, 3.0 / 8)576self.assertEqual(sheet.A30, 3.0 / 2)577self.assertEqual(sheet.A31, 3.0 / 8)578self.assertEqual(sheet.A37, 1)579self.assertEqual(sheet.A38, 1)580self.assertEqual(sheet.A39, 2)581self.assertEqual(sheet.A40, 0.5)582self.assertEqual(sheet.A41, -1)583self.assertEqual(sheet.A42, -1)584self.assertEqual(sheet.A43, -2)585self.assertEqual(sheet.A44, 0)586self.assertEqual(sheet.A45, 0)587self.assertEqual(sheet.A46, 2)588self.assertEqual(sheet.A47, 1)589self.assertEqual(sheet.A48, 1)590self.assertEqual(sheet.A49, 2)591self.assertEqual(sheet.A50, 0)592self.assertEqual(sheet.A51, 0)593self.assertEqual(sheet.A52, -2)594self.assertEqual(sheet.A53, 50)595self.assertEqual(sheet.A54, Units.Quantity("9.7mm"))596self.assertEqual(sheet.getContents("A1"), "=1 < 2 ? 3 : 4")597self.assertEqual(sheet.getContents("A2"), "=1 + 2 < 3 + 4 ? 5 + 6 : 7 + 8")598self.assertEqual(599sheet.getContents("A3"),600"=1 + 2 * 1 < 3 + 4 ? 5 * 2 + 6 * 3 + 2 ^ 4 : 7 * 2 + 8 * 3 + 2 ^ 3",601)602self.assertEqual(sheet.getContents("A4"), "123")603self.assertEqual(sheet.getContents("A5"), "=123 + 321")604self.assertEqual(sheet.getContents("A6"), "=123 * 2 + 321")605self.assertEqual(sheet.getContents("A7"), "=123 * 2 + 333 / 3")606self.assertEqual(sheet.getContents("A8"), "=123 * (2 + 321)")607self.assertEqual(sheet.getContents("A9"), "=3 ^ 4")608self.assertEqual(sheet.getContents("A10"), "=3 ^ 4 * 2")609self.assertEqual(sheet.getContents("A11"), "=3 ^ (4 * 2)")610self.assertEqual(sheet.getContents("A12"), "=3 ^ 4 + 4")611self.assertEqual(sheet.getContents("A13"), "=1 + 4 / 2 + 5")612self.assertEqual(sheet.getContents("A14"), "=(3 + 6) / (1 + 2)")613self.assertEqual(sheet.getContents("A15"), "=1 * 2 / 3 * 4")614self.assertEqual(sheet.getContents("A16"), "=1 * 2 / (3 * 4)")615self.assertEqual(sheet.getContents("A17"), "=3 ^ 4 ^ 2")616self.assertEqual(sheet.getContents("A18"), "=3 ^ (4 ^ 2)")617self.assertEqual(sheet.getContents("A19"), "=3 ^ 4 ^ 2")618self.assertEqual(sheet.getContents("A20"), "=3 + 4 + 2")619self.assertEqual(sheet.getContents("A21"), "=3 + 4 + 2")620self.assertEqual(sheet.getContents("A22"), "=3 + 4 + 2")621self.assertEqual(sheet.getContents("A23"), "=3 - 4 - 2")622self.assertEqual(sheet.getContents("A24"), "=3 - (4 - 2)")623self.assertEqual(sheet.getContents("A25"), "=3 - 4 - 2")624self.assertEqual(sheet.getContents("A26"), "=3 * 4 * 2")625self.assertEqual(sheet.getContents("A27"), "=3 * 4 * 2")626self.assertEqual(sheet.getContents("A28"), "=3 * 4 * 2")627self.assertEqual(sheet.getContents("A29"), "=3 / 4 / 2")628self.assertEqual(sheet.getContents("A30"), "=3 / (4 / 2)")629self.assertEqual(sheet.getContents("A31"), "=3 / 4 / 2")630self.assertEqual(sheet.getContents("A32"), "=pi * 3")631self.assertEqual(sheet.getContents("A33"), "=A32 / 3")632self.assertEqual(sheet.getContents("A34"), "=1 < 2 ? <<A>> : <<B>>")633self.assertEqual(sheet.getContents("A35"), "=min(A32:A33)")634self.assertEqual(sheet.getContents("A36"), "=(1 < 2 ? 0 : 1) * 3")635self.assertEqual(sheet.getContents("A37"), "=8 / (2 ^ 2 * 2)")636self.assertEqual(sheet.getContents("A38"), "=2 ^ 2 * 2 / 8")637self.assertEqual(sheet.getContents("A39"), "=2 ^ (2 * 2) / 8")638self.assertEqual(sheet.getContents("A40"), "=8 / 2 ^ (2 * 2)")639self.assertEqual(sheet.getContents("A41"), "=-1")640self.assertEqual(sheet.getContents("A42"), "=-1")641self.assertEqual(sheet.getContents("A43"), "=-(1 + 1)")642self.assertEqual(sheet.getContents("A44"), "=-(1 - 1)")643self.assertEqual(sheet.getContents("A45"), "=-(-1 + 1)")644self.assertEqual(sheet.getContents("A46"), "=-(-1 + -1)")645self.assertEqual(sheet.getContents("A47"), "=+1")646self.assertEqual(sheet.getContents("A48"), "=+1")647self.assertEqual(sheet.getContents("A49"), "=+(1 + 1)")648self.assertEqual(sheet.getContents("A50"), "=+(1 - 1)")649self.assertEqual(sheet.getContents("A51"), "=+(-1 + 1)")650self.assertEqual(sheet.getContents("A52"), "=+(-1 + -1)")651
652def testNumbers(self):653"""Test different numbers"""654sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")655sheet.set("A1", "1")656sheet.set("A2", "1.5")657sheet.set("A3", ".5")658sheet.set("A4", "1e2")659sheet.set("A5", "1E2")660sheet.set("A6", "1e-2")661sheet.set("A7", "1E-2")662sheet.set("A8", "1.5e2")663sheet.set("A9", "1.5E2")664sheet.set("A10", "1.5e-2")665sheet.set("A11", "1.5E-2")666sheet.set("A12", ".5e2")667sheet.set("A13", ".5E2")668sheet.set("A14", ".5e-2")669sheet.set("A15", ".5E-2")670sheet.set("A16", "1/1")671sheet.set("A17", "1/2")672sheet.set("A18", "2/4")673self.doc.recompute()674self.assertEqual(sheet.A1, 1)675self.assertEqual(sheet.A2, 1.5)676self.assertEqual(sheet.A3, 0.5)677self.assertEqual(sheet.A4, 1e2)678self.assertEqual(sheet.A5, 1e2)679self.assertEqual(sheet.A6, 1e-2)680self.assertEqual(sheet.A7, 1e-2)681self.assertEqual(sheet.A8, 1.5e2)682self.assertEqual(sheet.A9, 1.5e2)683self.assertEqual(sheet.A10, 1.5e-2)684self.assertEqual(sheet.A11, 1.5e-2)685self.assertEqual(sheet.A12, 0.5e2)686self.assertEqual(sheet.A13, 0.5e2)687self.assertEqual(sheet.A14, 0.5e-2)688self.assertEqual(sheet.A15, 0.5e-2)689self.assertEqual(sheet.A16, 1)690self.assertEqual(sheet.A17, 0.5)691self.assertEqual(sheet.A18, 0.5)692
693def testQuantitiesAndFractionsAsNumbers(self):694"""Test quantities and simple fractions as numbers"""695sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")696sheet.set("A1", "1mm")697sheet.set("A2", "1/2")698sheet.set("A3", "4mm/2")699sheet.set("A4", "2/mm")700sheet.set("A5", "4/2mm")701sheet.set("A6", "6mm/3s")702self.doc.recompute()703self.assertEqual(sheet.A1, Units.Quantity("1 mm"))704self.assertEqual(sheet.A2, 0.5)705self.assertEqual(sheet.A3, Units.Quantity("2 mm"))706self.assertEqual(sheet.A4, Units.Quantity("2 1/mm"))707self.assertEqual(sheet.A5, Units.Quantity("2 1/mm"))708self.assertEqual(sheet.A6, Units.Quantity("2 mm/s"))709
710def testRemoveRows(self):711"""Removing rows -- check renaming of internal cells"""712sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")713sheet.set("A3", "123")714sheet.set("A1", "=A3")715sheet.removeRows("2", 1)716self.assertEqual(sheet.getContents("A1"), "=A2")717
718def testInsertRows(self):719"""Inserting rows -- check renaming of internal cells"""720sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")721sheet.set("B1", "=B2")722sheet.set("B2", "124")723# Calling getContents() here activates ObjectIdentifier internal cache,724# which needs to be tested as well.725self.assertEqual(sheet.getContents("B1"), "=B2")726sheet.insertRows("2", 1)727self.assertEqual(sheet.getContents("B1"), "=B3")728
729def testIssue3225(self):730"""Inserting rows -- check renaming of internal cells"""731sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")732sheet.set("B2", "25")733sheet.set("B3", "=B2")734sheet.insertRows("2", 1)735self.assertEqual(sheet.getContents("B4"), "=B3")736
737def testRenameAlias(self):738"""Test renaming of alias1 to alias2 in a spreadsheet"""739sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")740sheet.set("B1", "124")741sheet.setAlias("B1", "alias1")742sheet.set("B2", "=alias1")743self.doc.recompute()744self.assertEqual(sheet.get("alias1"), 124)745self.assertEqual(sheet.get("B1"), 124)746self.assertEqual(sheet.get("B2"), 124)747sheet.setAlias("B1", "alias2")748self.doc.recompute()749self.assertEqual(sheet.get("alias2"), 124)750self.assertEqual(sheet.getContents("B2"), "=alias2")751
752def testRenameAlias2(self):753"""Test renaming of alias1 to alias2 in a spreadsheet, when referenced from another object"""754sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")755sheet.set("B1", "124")756sheet.setAlias("B1", "alias1")757box = self.doc.addObject("Part::Box", "Box")758box.setExpression("Length", "Spreadsheet.alias1")759sheet.setAlias("B1", "alias2")760self.assertEqual(box.ExpressionEngine[0][1], "Spreadsheet.alias2")761
762def testRenameAlias3(self):763"""Test renaming of document object referenced from another object"""764sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")765sheet.set("B1", "124")766sheet.setAlias("B1", "alias1")767box = self.doc.addObject("Part::Box", "Box")768box.setExpression("Length", "Spreadsheet.alias1")769box2 = self.doc.addObject("Part::Box", "Box")770box2.setExpression("Length", "<<Spreadsheet>>.alias1")771sheet.Label = "Params"772self.assertEqual(box.ExpressionEngine[0][1], "Spreadsheet.alias1")773self.assertEqual(box2.ExpressionEngine[0][1], "<<Params>>.alias1")774
775def testAlias(self):776"""Playing with aliases"""777sheet = self.doc.addObject("Spreadsheet::Sheet", "Calc")778sheet.setAlias("A1", "Test")779self.assertEqual(sheet.getAlias("A1"), "Test")780
781sheet.set("A1", "4711")782self.doc.recompute()783self.assertEqual(sheet.get("Test"), 4711)784self.assertEqual(sheet.get("Test"), sheet.get("A1"))785
786def testAmbiguousAlias(self):787"""Try to set the same alias twice (bug #2402)"""788sheet = self.doc.addObject("Spreadsheet::Sheet", "Calc")789sheet.setAlias("A1", "Test")790try:791sheet.setAlias("A2", "Test")792self.fail("An ambiguous alias was set which shouldn't be allowed")793except Exception:794self.assertEqual(sheet.getAlias("A2"), None)795
796def testClearAlias(self):797"""This was causing a crash"""798sheet = self.doc.addObject("Spreadsheet::Sheet", "Calc")799sheet.setAlias("A1", "Test")800sheet.setAlias("A1", "")801self.assertEqual(sheet.getAlias("A1"), None)802
803def testSetInvalidAlias(self):804"""Try to use a cell address as alias name"""805sheet = self.doc.addObject("Spreadsheet::Sheet", "Calc")806try:807sheet.setAlias("A1", "B1")808except Exception:809self.assertEqual(sheet.getAlias("A1"), None)810else:811self.fail("A cell address was used as alias which shouldn't be allowed")812
813def testSetInvalidAlias2(self):814"""Try to use a unit (reserved word) as alias name"""815sheet = self.doc.addObject("Spreadsheet::Sheet", "Calc")816try:817sheet.setAlias("A1", "mA")818except Exception:819self.assertEqual(sheet.getAlias("A1"), None)820else:821self.fail("A unit (reserved word) was used as alias which shouldn't be allowed")822
823def testPlacementName(self):824"""Object name is equal to property name (bug #2389)"""825if not FreeCAD.GuiUp:826return827
828import FreeCADGui829
830o = self.doc.addObject("Part::FeaturePython", "Placement")831FreeCADGui.Selection.addSelection(o)832
833def testInvoluteGear(self):834"""Support of boolean or integer values"""835try:836import InvoluteGearFeature837except ImportError:838return839InvoluteGearFeature.makeInvoluteGear("InvoluteGear")840self.doc.recompute()841sketch = self.doc.addObject("Sketcher::SketchObject", "Sketch")842sketch.addGeometry(Part.LineSegment(v(0, 0, 0), v(10, 10, 0)), False)843sketch.addConstraint(Sketcher.Constraint("Distance", 0, 65.285388))844sketch.setExpression("Constraints[0]", "InvoluteGear.NumberOfTeeth")845self.doc.recompute()846self.assertIn("Up-to-date", sketch.State)847
848def testSketcher(self):849"""Mixup of Label and Name (bug #2407)"""850sketch = self.doc.addObject("Sketcher::SketchObject", "Sketch")851sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")852sheet.setAlias("A1", "Length")853self.doc.recompute()854sheet.set("A1", "47,11")855self.doc.recompute()856
857index = sketch.addGeometry(Part.LineSegment(v(0, 0, 0), v(10, 10, 0)), False)858sketch.addConstraint(Sketcher.Constraint("Distance", index, 14.0))859self.doc.recompute()860sketch.setExpression("Constraints[0]", "<<Spreadsheet>>.Length")861self.doc.recompute()862sheet.Label = "Calc"863self.doc.recompute()864self.assertEqual(sketch.ExpressionEngine[0][1], "<<Calc>>.Length")865self.assertIn("Up-to-date", sketch.State)866
867def testCrossDocumentLinks(self):868"""Expressions across files are not saved (bug #2442)"""869
870# Create a box871box = self.doc.addObject("Part::Box", "Box")872
873# Create a second document with a cylinder874doc2 = FreeCAD.newDocument()875cylinder = doc2.addObject("Part::Cylinder", "Cylinder")876cylinder.setExpression("Radius", "cube#Cube.Height")877
878# Save and close first document879self.doc.saveAs(self.TempPath + os.sep + "cube.fcstd")880FreeCAD.closeDocument(self.doc.Name)881
882# Save and close second document883doc2.saveAs(self.TempPath + os.sep + "cylinder.fcstd")884FreeCAD.closeDocument(doc2.Name)885
886# Open both documents again887self.doc = FreeCAD.openDocument(self.TempPath + os.sep + "cube.fcstd")888doc2 = FreeCAD.openDocument(self.TempPath + os.sep + "cylinder.fcstd")889
890# Check reference between them891self.assertEqual(doc2.getObject("Cylinder").ExpressionEngine[0][1], "cube#Cube.Height")892
893# Close second document894FreeCAD.closeDocument(doc2.Name)895
896def testMatrix(self):897"""Test Matrix/Vector/Placement/Rotation operations"""898
899def plm_equal(plm1, plm2):900from math import sqrt901
902qpair = zip(plm1.Rotation.Q, plm2.Rotation.Q)903qdiff1 = sqrt(sum([(v1 - v2) ** 2 for v1, v2 in qpair]))904qdiff2 = sqrt(sum([(v1 + v2) ** 2 for v1, v2 in qpair]))905return (plm1.Base - plm2.Base).Length < 1e-7 and (qdiff1 < 1e-12 or dqiff2 < 1e-12)906
907sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")908
909mat = FreeCAD.Matrix()910mat.scale(2, 1, 2)911imat = mat.inverse()912
913vec = FreeCAD.Vector(2, 1, 2)914
915rot = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), 45)916irot = rot.inverted()917
918pla = FreeCAD.Placement(vec, rot)919ipla = pla.inverse()920
921sheet.set("A1", "=vector(2, 1, 2)")922
923# different ways of calling mscale()924sheet.set("B1", "=mscale(create(<<matrix>>), A1)")925sheet.set("C1", "=mscale(create(<<matrix>>), tuple(2, 1, 2))")926sheet.set("A2", "=mscale(create(<<matrix>>), 2, 1, 2)")927
928# test matrix power operation929sheet.set("B2", "=A2^-2")930sheet.set("C2", "=A2^-1")931sheet.set("D2", "=A2^0")932sheet.set("E2", "=A2^1")933sheet.set("F2", "=A2^2")934sheet.set("G2", "=matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)")935sheet.set("H2", "=G2^-1")936
937sheet.set("A3", "=rotation(vector(0, 1, 0), 45)")938
939# test rotation power operation940sheet.set("B3", "=A3^-2")941sheet.set("C3", "=A3^-1")942sheet.set("D3", "=A3^0")943sheet.set("E3", "=A3^1")944sheet.set("F3", "=A3^2")945
946sheet.set("A4", "=placement(A1, A3)")947
948# test placement power operation949sheet.set("B4", "=A4^-2")950sheet.set("C4", "=A4^-1")951sheet.set("D4", "=A4^0")952sheet.set("E4", "=A4^1")953sheet.set("F4", "=A4^2")954
955# vector transformation with mixing matrix and placement and rotation956sheet.set("A5", "=A2*A3*A4*A1")957sheet.set("B5", "=B2*B4*B3*A1")958sheet.set("C5", "=C3*C2*C4*A1")959sheet.set("D5", "=D3*D4*D2*A1")960sheet.set("E5", "=E4*E2*E3*A1")961sheet.set("F5", "=F3*F4*F2*A1")962
963# inverse of the above transformation with power -1 and minvert()964sheet.set("A6", "=A4^-1 * minvert(A3) * A2^-1 * A5")965sheet.set("B6", "=minvert(B3) * B4^-1 * minvert(B2) * B5")966sheet.set("C6", "=C4^-1 * C2^-1 * C3^-1 * C5")967sheet.set("D6", "=minvert(D4*D2) * minvert(D3) * D5")968sheet.set("E6", "=(E2 * E3)^-1 * E4^-1 * E5")969sheet.set("F6", "=(F3*F4*F2)^-1 * F5")970
971# Rotate and translate.972sheet.set("A7", "=placement(vector(1; 2; 3), vector(1; 0; 0); 0)")973sheet.set("B7", "=mrotate(A7; vector(1; 0; 0); 90)")974sheet.set("C7", "=mrotatex(A7; 90)")975sheet.set("D7", "=mrotatey(A7; 90)")976sheet.set("E7", "=mrotatez(A7; 90)")977sheet.set("F7", "=mtranslate(A7; vector(1; 2; 3))")978sheet.set("G7", "=mtranslate(A7; 1; 2; 3)")979
980# Compatibility with old syntax.981sheet.set("A8", "=create(<<vector>>, 2, 1, 2)")982sheet.set("B8", "=create(<<rotation>>, create(<<vector>>, 0, 1, 0), 45)")983sheet.set("C8", "=create(<<placement>>, A8, B8)")984
985self.doc.recompute()986
987self.assertEqual(sheet.A1, vec)988
989self.assertEqual(sheet.B1, mat)990self.assertEqual(sheet.C1, mat)991self.assertEqual(sheet.A2, mat)992
993self.assertEqual(sheet.B2, imat * imat)994self.assertEqual(sheet.B2, mat**-2)995self.assertEqual(sheet.C2, imat)996self.assertEqual(sheet.C2, mat**-1)997self.assertEqual(sheet.D2, FreeCAD.Matrix())998self.assertEqual(sheet.D2, mat**0)999self.assertEqual(sheet.E2, mat)1000self.assertEqual(sheet.E2, mat**1)1001self.assertEqual(sheet.F2, mat * mat)1002self.assertEqual(sheet.F2, mat**2)1003
1004self.assertTrue(sheet.H2.startswith("ERR: Cannot invert singular matrix"))1005
1006self.assertEqual(sheet.A3, rot)1007
1008rtol = 1e-121009self.assertTrue(sheet.B3.isSame(irot * irot, rtol))1010self.assertTrue(sheet.B3.isSame(rot**-2, rtol))1011self.assertTrue(sheet.C3.isSame(irot, rtol))1012self.assertTrue(sheet.C3.isSame(rot**-1, rtol))1013self.assertTrue(sheet.D3.isSame(FreeCAD.Rotation(), rtol))1014self.assertTrue(sheet.D3.isSame(rot**0, rtol))1015self.assertTrue(sheet.E3.isSame(rot, rtol))1016self.assertTrue(sheet.E3.isSame(rot**1, rtol))1017self.assertTrue(sheet.F3.isSame(rot * rot, rtol))1018self.assertTrue(sheet.F3.isSame(rot**2, rtol))1019
1020self.assertEqual(sheet.A4, pla)1021
1022self.assertTrue(plm_equal(sheet.B4, ipla * ipla))1023self.assertTrue(plm_equal(sheet.B4, pla**-2))1024self.assertTrue(plm_equal(sheet.C4, ipla))1025self.assertTrue(plm_equal(sheet.C4, pla**-1))1026self.assertTrue(plm_equal(sheet.D4, FreeCAD.Placement()))1027self.assertTrue(plm_equal(sheet.D4, pla**0))1028self.assertTrue(plm_equal(sheet.E4, pla))1029self.assertTrue(plm_equal(sheet.E4, pla**1))1030self.assertTrue(plm_equal(sheet.F4, pla * pla))1031self.assertTrue(plm_equal(sheet.F4, pla**2))1032
1033tol = 1e-101034
1035self.assertLess(1036sheet.A5.distanceToPoint(1037sheet.A2.multiply(sheet.A3.Matrix).multiply(sheet.A4.Matrix).multVec(vec)1038),1039tol,1040)1041self.assertLess(1042sheet.B5.distanceToPoint(1043sheet.B2.multiply(sheet.B4.Matrix).multiply(sheet.B3.Matrix).multVec(vec)1044),1045tol,1046)1047self.assertLess(1048sheet.C5.distanceToPoint(1049sheet.C3.Matrix.multiply(sheet.C2).multiply(sheet.C4.Matrix).multVec(vec)1050),1051tol,1052)1053self.assertLess(1054sheet.D5.distanceToPoint(1055sheet.D3.Matrix.multiply(sheet.D4.Matrix).multiply(sheet.D2).multVec(vec)1056),1057tol,1058)1059self.assertLess(1060sheet.E5.distanceToPoint(1061sheet.E4.Matrix.multiply(sheet.E2).multiply(sheet.E3.Matrix).multVec(vec)1062),1063tol,1064)1065self.assertLess(1066sheet.F5.distanceToPoint(1067sheet.F3.Matrix.multiply(sheet.F4.Matrix).multiply(sheet.F2).multVec(vec)1068),1069tol,1070)1071
1072self.assertLess(sheet.A6.distanceToPoint(vec), tol)1073self.assertLess(sheet.B6.distanceToPoint(vec), tol)1074self.assertLess(sheet.C6.distanceToPoint(vec), tol)1075self.assertLess(sheet.D6.distanceToPoint(vec), tol)1076self.assertLess(sheet.E6.distanceToPoint(vec), tol)1077self.assertLess(sheet.F6.distanceToPoint(vec), tol)1078
1079self.assertTrue(sheet.A7.Base.isEqual(FreeCAD.Vector(1, 2, 3), tol))1080self.assertTrue(sheet.B7.Base.isEqual(FreeCAD.Vector(1, -3, 2), tol))1081self.assertTrue(sheet.C7.Base.isEqual(FreeCAD.Vector(1, -3, 2), tol))1082self.assertTrue(sheet.D7.Base.isEqual(FreeCAD.Vector(3, 2.0, -1), tol))1083self.assertTrue(sheet.E7.Base.isEqual(FreeCAD.Vector(-2, 1, 3.0), tol))1084self.assertTrue(sheet.F7.Base.isEqual(FreeCAD.Vector(2, 4, 6), tol))1085self.assertTrue(sheet.G7.Base.isEqual(FreeCAD.Vector(2, 4, 6), tol))1086
1087self.assertEqual(sheet.A8, vec)1088self.assertEqual(sheet.B8, rot)1089self.assertEqual(sheet.C8, pla)1090
1091def testIssue3128(self):1092"""Regression test for issue 3128; mod should work with arbitrary units for both arguments"""1093sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1094sheet.set("A1", "=mod(7mm;3mm)")1095sheet.set("A2", "=mod(7kg;3mm)")1096self.doc.recompute()1097self.assertEqual(sheet.A1, Units.Quantity("1"))1098self.assertEqual(sheet.A2, Units.Quantity("1 kg/mm"))1099
1100def testIssue3363(self):1101"""Regression test for issue 3363; Nested conditionals statement fails with additional conditional statement in false-branch"""1102sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1103sheet.set("A1", "1")1104sheet.set("B1", "=A1==1?11:(A1==2?12:13)")1105sheet.set("C1", "=A1==1?(A1==2?12:13) : 11")1106self.doc.recompute()1107
1108# Save and close first document1109self.doc.saveAs(self.TempPath + os.sep + "conditionals.fcstd")1110FreeCAD.closeDocument(self.doc.Name)1111
1112# Open documents again1113self.doc = FreeCAD.openDocument(self.TempPath + os.sep + "conditionals.fcstd")1114
1115sheet = self.doc.getObject("Spreadsheet")1116self.assertEqual(sheet.getContents("B1"), "=A1 == 1 ? 11 : (A1 == 2 ? 12 : 13)")1117self.assertEqual(sheet.getContents("C1"), "=A1 == 1 ? (A1 == 2 ? 12 : 13) : 11")1118
1119def testIssue3432(self):1120"""Regression test for issue 3432; numbers with units are ignored from aggregates"""1121sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1122sheet.set("A1", "1mm")1123sheet.set("B1", "2mm")1124sheet.set("C1", "=max(A1:B1;3mm)")1125self.doc.recompute()1126self.assertEqual(sheet.get("C1"), Units.Quantity("3 mm"))1127
1128def testIssue4156(self):1129"""Regression test for issue 4156; necessarily use of leading '=' to enter an expression, creates inconsistent behavior depending on the spreadsheet state"""1130sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1131sheet.set("A3", "A1")1132sheet.set("A1", "1000")1133self.doc.recompute()1134sheet.set("A3", "")1135sheet.set("A3", "A1")1136self.assertEqual(sheet.getContents("A3"), "'A1")1137
1138def testInsertRowsAlias(self):1139"""Regression test for issue 4429; insert rows to sheet with aliases"""1140sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1141sheet.set("A3", "1")1142sheet.setAlias("A3", "alias1")1143sheet.set("A4", "=alias1 + 1")1144sheet.setAlias("A4", "alias2")1145sheet.set("A5", "=alias2 + 1")1146self.doc.recompute()1147sheet.insertRows("1", 1)1148self.doc.recompute()1149self.assertEqual(sheet.A6, 3)1150
1151def testInsertColumnsAlias(self):1152"""Regression test for issue 4429; insert columns to sheet with aliases"""1153sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1154sheet.set("C1", "1")1155sheet.setAlias("C1", "alias1")1156sheet.set("D1", "=alias1 + 1")1157sheet.setAlias("D1", "alias2")1158sheet.set("E1", "=alias2 + 1")1159self.doc.recompute()1160sheet.insertColumns("A", 1)1161self.doc.recompute()1162self.assertEqual(sheet.F1, 3)1163
1164def testRemoveRowsAlias(self):1165"""Regression test for issue 4429; remove rows from sheet with aliases"""1166sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1167sheet.set("A3", "1")1168sheet.setAlias("A3", "alias1")1169sheet.set("A5", "=alias1 + 1")1170sheet.setAlias("A5", "alias2")1171sheet.set("A4", "=alias2 + 1")1172self.doc.recompute()1173sheet.removeRows("1", 1)1174self.doc.recompute()1175self.assertEqual(sheet.A3, 3)1176
1177def testRemoveRowsAliasReuseName(self):1178"""Regression test for issue 4492; deleted aliases remains in database"""1179sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1180sheet.setAlias("B2", "test")1181self.doc.recompute()1182sheet.removeRows("2", 1)1183sheet.setAlias("B3", "test")1184
1185def testRemoveColumnsAlias(self):1186"""Regression test for issue 4429; remove columns from sheet with aliases"""1187sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1188sheet.set("C1", "1")1189sheet.setAlias("C1", "alias1")1190sheet.set("E1", "=alias1 + 1")1191sheet.setAlias("E1", "alias2")1192sheet.set("D1", "=alias2 + 1")1193self.doc.recompute()1194sheet.removeColumns("A", 1)1195self.doc.recompute()1196self.assertEqual(sheet.C1, 3)1197
1198def testRemoveColumnsAliasReuseName(self):1199"""Regression test for issue 4492; deleted aliases remains in database"""1200sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1201sheet.setAlias("B2", "test")1202self.doc.recompute()1203sheet.removeColumns("B", 1)1204sheet.setAlias("C3", "test")1205
1206def testUndoAliasCreationReuseName(self):1207"""Test deleted aliases by undo remains in database"""1208sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1209
1210self.doc.UndoMode = 11211self.doc.openTransaction("create alias")1212sheet.setAlias("B2", "test")1213self.doc.commitTransaction()1214self.doc.recompute()1215
1216self.doc.undo()1217self.doc.recompute()1218sheet.setAlias("C3", "test")1219
1220def testCrossLinkEmptyPropertyName(self):1221# https://forum.freecad.org/viewtopic.php?f=3&t=586031222base = FreeCAD.newDocument("base")1223sheet = base.addObject("Spreadsheet::Sheet", "Spreadsheet")1224sheet.setAlias("A1", "x")1225sheet.set("x", "42mm")1226base.recompute()1227
1228square = FreeCAD.newDocument("square")1229body = square.addObject("PartDesign::Body", "Body")1230box = square.addObject("PartDesign::AdditiveBox", "Box")1231body.addObject(box)1232box.Length = 10.001233box.Width = 10.001234box.Height = 10.001235square.recompute()1236
1237basePath = self.TempPath + os.sep + "base.FCStd"1238base.saveAs(basePath)1239squarePath = self.TempPath + os.sep + "square.FCStd"1240square.saveAs(squarePath)1241
1242base.save()1243square.save()1244
1245FreeCAD.closeDocument(square.Name)1246FreeCAD.closeDocument(base.Name)1247
1248##1249## preparation done1250base = FreeCAD.openDocument(basePath)1251square = FreeCAD.openDocument(squarePath)1252
1253square.Box.setExpression("Length", "base#Spreadsheet.x")1254square.recompute()1255
1256square.save()1257base.save()1258FreeCAD.closeDocument(square.Name)1259FreeCAD.closeDocument(base.Name)1260
1261def testExpressionWithAlias(self):1262# https://forum.freecad.org/viewtopic.php?p=564502#p5645021263ss1 = self.doc.addObject("Spreadsheet::Sheet", "Input")1264ss1.setAlias("A1", "one")1265ss1.setAlias("A2", "two")1266ss1.set("A1", "1")1267ss1.set("A2", "2")1268self.doc.recompute()1269
1270ss2 = self.doc.addObject("Spreadsheet::Sheet", "Output")1271ss2.set("A1", "=Input.A1 + Input.A2")1272ss2.set("A2", "=Input.one + Input.two")1273ss2.set("A3", "=<<Input>>.A1 + <<Input>>.A2")1274ss2.set("A4", "=<<Input>>.one + <<Input>>.two")1275self.doc.recompute()1276
1277self.assertEqual(ss2.get("A1"), 3)1278self.assertEqual(ss2.get("A2"), 3)1279self.assertEqual(ss2.get("A3"), 3)1280self.assertEqual(ss2.get("A4"), 3)1281
1282project_path = self.TempPath + os.sep + "alias.FCStd"1283self.doc.saveAs(project_path)1284FreeCAD.closeDocument(self.doc.Name)1285self.doc = FreeCAD.openDocument(project_path)1286ss1 = self.doc.Input1287ss2 = self.doc.Output1288
1289self.assertEqual(ss2.get("A1"), 3)1290self.assertEqual(ss2.get("A2"), 3)1291self.assertEqual(ss2.get("A3"), 3)1292self.assertEqual(ss2.get("A4"), 3)1293
1294ss1.set("A1", "2")1295self.doc.recompute()1296
1297self.assertEqual(ss1.get("A1"), 2)1298self.assertEqual(ss1.get("one"), 2)1299
1300self.assertEqual(ss2.get("A1"), 4)1301self.assertEqual(ss2.get("A2"), 4)1302self.assertEqual(ss2.get("A3"), 4)1303self.assertEqual(ss2.get("A4"), 4)1304
1305def testIssue6844(self):1306body = self.doc.addObject("App::FeaturePython", "Body")1307body.addProperty("App::PropertyEnumeration", "Configuration")1308body.Configuration = ["Item1", "Item2", "Item3"]1309
1310sheet = self.doc.addObject("Spreadsheet::Sheet", "Sheet")1311sheet.addProperty("App::PropertyString", "A2")1312sheet.A2 = "Item2"1313sheet.addProperty("App::PropertyEnumeration", "body")1314sheet.body = ["Item1", "Item2", "Item3"]1315
1316sheet.setExpression(".body.Enum", "cells[<<A2:|>>]")1317sheet.setExpression(1318".cells.Bind.B1.ZZ1",1319"tuple(.cells; <<B>> + str(hiddenref(Body.Configuration) + 2); <<ZZ>> + str(hiddenref(Body.Configuration) + 2))",1320)1321
1322self.doc.recompute()1323self.doc.UndoMode = 01324self.doc.removeObject("Body")1325sheet.clearAll()1326
1327def testIssue6840(self):1328body = self.doc.addObject("App::FeaturePython", "Body")1329body.addProperty("App::PropertyEnumeration", "Configuration")1330body.Configuration = ["Item1", "Item2", "Item3"]1331
1332sheet = self.doc.addObject("Spreadsheet::Sheet", "Sheet")1333sheet.addProperty("App::PropertyString", "A2")1334sheet.A2 = "Item2"1335sheet.addProperty("App::PropertyEnumeration", "body")1336sheet.body = ["Item1", "Item2", "Item3"]1337
1338sheet.setExpression(".body.Enum", "cells[<<A2:|>>]")1339sheet.setExpression(1340".cells.Bind.B1.ZZ1",1341"tuple(.cells; <<B>> + str(hiddenref(Body.Configuration) + 2); <<ZZ>> + str(hiddenref(Body.Configuration) + 2))",1342)1343
1344self.doc.recompute()1345self.doc.clearDocument()1346
1347def testFixPR6843(self):1348sheet = self.doc.addObject("Spreadsheet::Sheet", "Sheet")1349sheet.set("A5", "a")1350sheet.set("A6", "b")1351self.doc.recompute()1352sheet.insertRows("6", 1)1353self.doc.recompute()1354self.assertEqual(sheet.A5, "a")1355self.assertEqual(sheet.A7, "b")1356with self.assertRaises(AttributeError):1357self.assertEqual(sheet.A6, "")1358
1359def testBindAcrossSheets(self):1360ss1 = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet1")1361ss2 = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet2")1362ss2.set("B1", "B1")1363ss2.set("B2", "B2")1364ss2.set("C1", "C1")1365ss2.set("C2", "C2")1366ss2.set("D1", "D1")1367ss2.set("D2", "D2")1368
1369ss1.setExpression(".cells.Bind.A3.C4", "tuple(Spreadsheet2.cells, <<B1>>, <<D2>>)")1370self.doc.recompute()1371
1372self.assertEqual(ss1.A3, ss2.B1)1373self.assertEqual(ss1.A4, ss2.B2)1374self.assertEqual(ss1.B3, ss2.C1)1375self.assertEqual(ss1.B4, ss2.C2)1376self.assertEqual(ss1.C3, ss2.D1)1377self.assertEqual(ss1.C4, ss2.D2)1378
1379self.assertEqual(len(ss1.ExpressionEngine), 1)1380ss1.setExpression(".cells.Bind.A3.C4", None)1381self.doc.recompute()1382
1383def testBindHiddenRefAcrossSheets(self):1384ss1 = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet1")1385ss2 = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet2")1386ss2.set("B1", "B1")1387ss2.set("B2", "B2")1388ss2.set("C1", "C1")1389ss2.set("C2", "C2")1390ss2.set("D1", "D1")1391ss2.set("D2", "D2")1392
1393self.doc.recompute()1394ss1.setExpression(".cells.Bind.A3.C4", None)1395ss1.setExpression(1396".cells.BindHiddenRef.A3.C4", "hiddenref(tuple(Spreadsheet2.cells, <<B1>>, <<D2>>))"1397)1398self.doc.recompute()1399
1400ss1.recompute() # True1401self.assertEqual(ss1.A3, ss2.B1)1402
1403ss1.setExpression(".cells.Bind.A3.C4", None)1404ss1.setExpression(".cells.BindHiddenRef.A3.C4", None)1405self.doc.recompute()1406self.assertEqual(len(ss1.ExpressionEngine), 0)1407
1408def testMergeCells(self):1409ss1 = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet1")1410ss1.mergeCells("A1:B4")1411ss1.mergeCells("C1:D4")1412self.doc.recompute()1413ss1.set("B1", "fail")1414self.doc.recompute()1415with self.assertRaises(AttributeError):1416self.assertEqual(ss1.B1, "fail")1417
1418def testMergeCellsAndBind(self):1419ss1 = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet1")1420ss1.mergeCells("A1:B1")1421ss1.setExpression(".cells.Bind.A1.A1", "tuple(.cells, <<A2>>, <<A2>>)")1422ss1.set("A2", "test")1423self.doc.recompute()1424self.assertEqual(ss1.A1, ss1.A2)1425ss1.set("B1", "fail")1426self.doc.recompute()1427with self.assertRaises(AttributeError):1428self.assertEqual(ss1.B1, "fail")1429
1430def testGetUsedCells(self):1431sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1432test_cells = ["B13", "C14", "D15"]1433for i, cell in enumerate(test_cells):1434sheet.set(cell, str(i))1435
1436used_cells = sheet.getUsedCells()1437self.assertEqual(len(used_cells), len(test_cells))1438for cell in test_cells:1439self.assertTrue(cell in used_cells)1440
1441for cell in test_cells:1442sheet.set(cell, "")1443sheet.setAlignment(cell, "center")1444non_empty_cells = sheet.getUsedCells()1445self.assertEqual(len(non_empty_cells), len(test_cells)) # Alignment counts as "used"1446
1447def testGetUsedRange(self):1448sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1449test_cells = ["C5", "Z3", "D10", "E20"]1450for i, cell in enumerate(test_cells):1451sheet.set(cell, str(i))1452used_range = sheet.getUsedRange()1453self.assertEqual(used_range, ("C3", "Z20"))1454
1455for i, cell in enumerate(test_cells):1456sheet.set(cell, "")1457sheet.setAlignment(cell, "center")1458used_range = sheet.getUsedRange()1459self.assertEqual(used_range, ("C3", "Z20"))1460
1461def testGetNonEmptyCells(self):1462sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1463test_cells = ["B13", "C14", "D15"]1464for i, cell in enumerate(test_cells):1465sheet.set(cell, str(i))1466
1467non_empty_cells = sheet.getNonEmptyCells()1468self.assertEqual(len(non_empty_cells), len(test_cells))1469for cell in test_cells:1470self.assertTrue(cell in non_empty_cells)1471
1472for cell in test_cells:1473sheet.set(cell, "")1474sheet.setAlignment(cell, "center")1475non_empty_cells = sheet.getNonEmptyCells()1476self.assertEqual(len(non_empty_cells), 0) # Alignment does not count as "non-empty"1477
1478def testGetNonEmptyRange(self):1479sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1480test_cells = ["C5", "Z3", "D10", "E20"]1481for i, cell in enumerate(test_cells):1482sheet.set(cell, str(i))1483non_empty_range = sheet.getNonEmptyRange()1484self.assertEqual(non_empty_range, ("C3", "Z20"))1485
1486for i, cell in enumerate(test_cells):1487sheet.set(cell, "")1488sheet.setAlignment(cell, "center")1489more_cells = ["D10", "X5", "E10", "K15"]1490for i, cell in enumerate(more_cells):1491sheet.set(cell, str(i))1492non_empty_range = sheet.getNonEmptyRange()1493self.assertEqual(non_empty_range, ("D5", "X15"))1494
1495def testAliasEmptyCell(self):1496# https://github.com/FreeCAD/FreeCAD/issues/78411497sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1498sheet.setAlias("A1", "aliasOfEmptyCell")1499self.assertEqual(sheet.getCellFromAlias("aliasOfEmptyCell"), "A1")1500
1501def testParensAroundCondition(self):1502"""Parens around a condition should be accepted"""1503sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1504
1505sheet.set("A1", "=(1 == 1) ? 1 : 0")1506self.doc.recompute()1507self.assertEqual(sheet.getContents("A1"), "=1 == 1 ? 1 : 0")1508self.assertEqual(sheet.A1, 1)1509
1510def testIssue6395(self):1511"""Testing strings are correctly saved and restored"""1512sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1513sheet.set("A1", "'36C") # Use a string that may be parsed as a quantity1514self.doc.recompute()1515
1516self.doc.saveAs(self.TempPath + os.sep + "string.fcstd")1517FreeCAD.closeDocument(self.doc.Name)1518
1519self.doc = FreeCAD.openDocument(self.TempPath + os.sep + "string.fcstd")1520
1521sheet = self.doc.getObject("Spreadsheet")1522self.assertEqual(sheet.getContents("A1"), "'36C")1523self.assertEqual(sheet.get("A1"), "36C")1524
1525def testVectorFunctions(self):1526sheet = self.doc.addObject("Spreadsheet::Sheet", "Spreadsheet")1527
1528sheet.set("A1", "=vcross(vector(1; 2; 3); vector(1; 5; 7))")1529
1530sheet.set("B1", "=vdot(vector(1; 2; 3); vector(4; -5; 6))")1531
1532sheet.set("C1", "=vangle(vector(1; 0; 0); vector(0; 1; 0))")1533
1534sheet.set("D1", "=vnormalize(vector(1; 0; 0))")1535sheet.set("D2", "=vnormalize(vector(1; 1; 1))")1536
1537sheet.set("E1", "=vscale(vector(1; 2; 3); 2; 3; 4)")1538sheet.set("E2", "=vscalex(vector(1; 2; 3); -2)")1539sheet.set("E3", "=vscaley(vector(1; 2; 3); -2)")1540sheet.set("E4", "=vscalez(vector(1; 2; 3); -2)")1541
1542sheet.set("F1", "=vlinedist(vector(1; 2; 3); vector(2; 3; 4); vector(3; 4; 5))")1543sheet.set("F2", "=vlinesegdist(vector(1; 2; 3); vector(2; 3; 4); vector(3; 4; 5))")1544sheet.set("F3", "=vlineproj(vector(1; 2; 3); vector(2; 3; 4); vector(3; 4; 5))")1545sheet.set("F4", "=vplanedist(vector(1; 2; 3); vector(2; 3; 4); vector(3; 4; 5))")1546sheet.set("F5", "=vplaneproj(vector(1; 2; 3); vector(2; 3; 4); vector(3; 4; 5))")1547
1548self.doc.recompute()1549
1550tolerance = 1e-101551
1552self.assertEqual(sheet.A1, FreeCAD.Vector(-1, -4, 3))1553
1554self.assertEqual(sheet.B1, 12)1555
1556self.assertEqual(sheet.C1, 90)1557
1558self.assertEqual(sheet.D1, FreeCAD.Vector(1, 0, 0))1559self.assertLess(1560sheet.D2.distanceToPoint(FreeCAD.Vector(1 / sqrt(3), 1 / sqrt(3), 1 / sqrt(3))),1561tolerance,1562)1563
1564self.assertEqual(sheet.E1, FreeCAD.Vector(2, 6, 12))1565self.assertEqual(sheet.E2, FreeCAD.Vector(-2, 2, 3))1566self.assertEqual(sheet.E3, FreeCAD.Vector(1, -4, 3))1567self.assertEqual(sheet.E4, FreeCAD.Vector(1, 2, -6))1568
1569self.assertLess(abs(sheet.F1.Value - 0.3464), 0.0001)1570self.assertEqual(sheet.F2, FreeCAD.Vector(1, 1, 1))1571self.assertLess(sheet.F3.distanceToPoint(FreeCAD.Vector(0.28, 0.04, -0.2)), tolerance)1572self.assertLess(abs(sheet.F4.Value - -1.6971), 0.0001)1573self.assertEqual(sheet.F5, FreeCAD.Vector(1.72, 2.96, 4.2))1574
1575def tearDown(self):1576# closing doc1577FreeCAD.closeDocument(self.doc.Name)1578