1
/***************************************************************************
2
* Copyright (c) 2013 Jürgen Riegel <juergen.riegel@web.de> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library 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. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
25
// inclusion of the generated files (generated out of QuantityPy.xml)
26
#include "QuantityPy.h"
28
#include "QuantityPy.cpp"
33
// returns a string which represents the object e.g. when printed in python
34
std::string QuantityPy::representation() const
36
std::stringstream ret;
38
double val = getQuantityPtr()->getValue();
39
Unit unit = getQuantityPtr()->getUnit();
41
// Use Python's implementation to repr() a float
43
ret << static_cast<std::string>(flt.repr());
44
if (!unit.isEmpty()) {
45
ret << " " << unit.getString().toUtf8().constData();
51
PyObject* QuantityPy::toStr(PyObject* args)
53
int prec = getQuantityPtr()->getFormat().precision;
54
if (!PyArg_ParseTuple(args, "|i", &prec)) {
58
double val = getQuantityPtr()->getValue();
59
Unit unit = getQuantityPtr()->getUnit();
61
std::stringstream ret;
63
ret.setf(std::ios::fixed, std::ios::floatfield);
65
if (!unit.isEmpty()) {
66
ret << " " << unit.getString().toUtf8().constData();
69
return Py_BuildValue("s", ret.str().c_str());
72
PyObject* QuantityPy::PyMake(PyTypeObject* /*unused*/, PyObject* /*unused*/, PyObject* /*unused*/)
74
// create a new instance of QuantityPy and the Twin object
75
return new QuantityPy(new Quantity);
79
int QuantityPy::PyInit(PyObject* args, PyObject* /*kwd*/)
81
Quantity* self = getQuantityPtr();
83
PyErr_Clear(); // set by PyArg_ParseTuple()
85
if (PyArg_ParseTuple(args, "O!", &(Base::QuantityPy::Type), &object)) {
86
*self = *(static_cast<Base::QuantityPy*>(object)->getQuantityPtr());
90
PyErr_Clear(); // set by PyArg_ParseTuple()
91
double f = DOUBLE_MAX;
92
if (PyArg_ParseTuple(args, "dO!", &f, &(Base::UnitPy::Type), &object)) {
93
*self = Quantity(f, *(static_cast<Base::UnitPy*>(object)->getUnitPtr()));
97
PyErr_Clear(); // set by PyArg_ParseTuple()
98
if (PyArg_ParseTuple(args, "dO!", &f, &(Base::QuantityPy::Type), &object)) {
99
PyErr_SetString(PyExc_TypeError, "Second argument must be a Unit not a Quantity");
111
PyErr_Clear(); // set by PyArg_ParseTuple()
112
if (PyArg_ParseTuple(args, "|diiiiiiii", &f, &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) {
113
if (f < DOUBLE_MAX) {
115
Unit {static_cast<int8_t>(i1),
116
static_cast<int8_t>(i2),
117
static_cast<int8_t>(i3),
118
static_cast<int8_t>(i4),
119
static_cast<int8_t>(i5),
120
static_cast<int8_t>(i6),
121
static_cast<int8_t>(i7),
122
static_cast<int8_t>(i8)});
127
PyErr_Clear(); // set by PyArg_ParseTuple()
129
if (PyArg_ParseTuple(args, "et", "utf-8", &string)) {
130
QString qstr = QString::fromUtf8(string);
133
*self = Quantity::parse(qstr);
135
catch (const Base::ParserError& e) {
136
PyErr_SetString(PyExc_ValueError, e.what());
143
PyErr_Clear(); // set by PyArg_ParseTuple()
144
if (PyArg_ParseTuple(args, "det", &f, "utf-8", &string)) {
145
QString unit = QString::fromUtf8(string);
148
*self = Quantity(f, unit);
150
catch (const Base::ParserError& e) {
151
PyErr_SetString(PyExc_ValueError, e.what());
158
PyErr_SetString(PyExc_TypeError, "Either quantity, float with units or string expected");
162
PyObject* QuantityPy::getUserPreferred(PyObject* /*args*/)
168
QString uss = getQuantityPtr()->getUserString(factor, uus);
170
res[0] = Py::String(uss.toUtf8(), "utf-8");
171
res[1] = Py::Float(factor);
172
res[2] = Py::String(uus.toUtf8(), "utf-8");
174
return Py::new_reference_to(res);
177
PyObject* QuantityPy::getValueAs(PyObject* args)
182
// first try Quantity
183
if (!quant.isValid()) {
185
if (PyArg_ParseTuple(args, "O!", &(Base::QuantityPy::Type), &object)) {
186
quant = *static_cast<Base::QuantityPy*>(object)->getQuantityPtr();
190
if (!quant.isValid()) {
193
if (PyArg_ParseTuple(args, "O!", &(Base::UnitPy::Type), &object)) {
194
quant.setUnit(*static_cast<Base::UnitPy*>(object)->getUnitPtr());
199
if (!quant.isValid()) {
203
if (PyArg_ParseTuple(args, "dO!", &value, &(Base::UnitPy::Type), &object)) {
204
quant.setUnit(*static_cast<Base::UnitPy*>(object)->getUnitPtr());
205
quant.setValue(value);
209
if (!quant.isValid()) {
210
double f = DOUBLE_MAX;
220
if (PyArg_ParseTuple(args, "d|iiiiiiii", &f, &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8)) {
221
if (f < DOUBLE_MAX) {
223
Unit {static_cast<int8_t>(i1),
224
static_cast<int8_t>(i2),
225
static_cast<int8_t>(i3),
226
static_cast<int8_t>(i4),
227
static_cast<int8_t>(i5),
228
static_cast<int8_t>(i6),
229
static_cast<int8_t>(i7),
230
static_cast<int8_t>(i8)});
235
if (!quant.isValid()) {
238
if (PyArg_ParseTuple(args, "et", "utf-8", &string)) {
239
QString qstr = QString::fromUtf8(string);
241
quant = Quantity::parse(qstr);
245
if (!quant.isValid()) {
246
PyErr_SetString(PyExc_TypeError, "Either quantity, string, float or unit expected");
250
if (getQuantityPtr()->getUnit() != quant.getUnit() && quant.isQuantity()) {
251
PyErr_SetString(PyExc_ValueError, "Unit mismatch");
255
quant = Quantity(getQuantityPtr()->getValueAs(quant));
256
return new QuantityPy(new Quantity(quant));
259
PyObject* QuantityPy::__round__(PyObject* args)
261
double val = getQuantityPtr()->getValue();
262
Unit unit = getQuantityPtr()->getUnit();
264
Py::Callable func(flt.getAttr("__round__"));
265
double rnd = static_cast<double>(Py::Float(func.apply(args)));
267
return new QuantityPy(new Quantity(rnd, unit));
270
PyObject* QuantityPy::number_float_handler(PyObject* self)
272
if (!PyObject_TypeCheck(self, &(QuantityPy::Type))) {
273
PyErr_SetString(PyExc_TypeError, "Arg must be Quantity");
277
QuantityPy* q = static_cast<QuantityPy*>(self);
278
return PyFloat_FromDouble(q->getValue());
281
PyObject* QuantityPy::number_int_handler(PyObject* self)
283
if (!PyObject_TypeCheck(self, &(QuantityPy::Type))) {
284
PyErr_SetString(PyExc_TypeError, "Arg must be Quantity");
288
QuantityPy* q = static_cast<QuantityPy*>(self);
289
return PyLong_FromLong(long(q->getValue()));
292
PyObject* QuantityPy::number_negative_handler(PyObject* self)
294
if (!PyObject_TypeCheck(self, &(QuantityPy::Type))) {
295
PyErr_SetString(PyExc_TypeError, "Arg must be Quantity");
299
Base::Quantity* a = static_cast<QuantityPy*>(self)->getQuantityPtr();
301
return new QuantityPy(new Quantity(*a * b));
304
PyObject* QuantityPy::number_positive_handler(PyObject* self)
306
if (!PyObject_TypeCheck(self, &(QuantityPy::Type))) {
307
PyErr_SetString(PyExc_TypeError, "Arg must be Quantity");
311
Base::Quantity* a = static_cast<QuantityPy*>(self)->getQuantityPtr();
312
return new QuantityPy(new Quantity(*a));
315
PyObject* QuantityPy::number_absolute_handler(PyObject* self)
317
if (!PyObject_TypeCheck(self, &(QuantityPy::Type))) {
318
PyErr_SetString(PyExc_TypeError, "Arg must be Quantity");
322
Base::Quantity* a = static_cast<QuantityPy*>(self)->getQuantityPtr();
323
return new QuantityPy(new Quantity(fabs(a->getValue()), a->getUnit()));
326
static Quantity& pyToQuantity(Quantity& q, PyObject* pyobj)
328
if (PyObject_TypeCheck(pyobj, &Base::QuantityPy::Type)) {
329
q = *static_cast<Base::QuantityPy*>(pyobj)->getQuantityPtr();
331
else if (PyFloat_Check(pyobj)) {
332
q = Quantity(PyFloat_AsDouble(pyobj));
334
else if (PyLong_Check(pyobj)) {
335
q = Quantity(PyLong_AsLong(pyobj));
338
PyErr_Format(PyExc_TypeError, "Cannot convert %s to Quantity", Py_TYPE(pyobj)->tp_name);
339
throw Py::Exception();
344
PyObject* QuantityPy::number_add_handler(PyObject* self, PyObject* other)
346
Quantity* pa = nullptr;
347
Quantity* pb = nullptr;
352
if (PyObject_TypeCheck(self, &(QuantityPy::Type))) {
353
pa = static_cast<QuantityPy*>(self)->getQuantityPtr();
356
pa = &pyToQuantity(a, self);
359
if (PyObject_TypeCheck(other, &(QuantityPy::Type))) {
360
pb = static_cast<QuantityPy*>(other)->getQuantityPtr();
363
pb = &pyToQuantity(b, other);
365
return new QuantityPy(new Quantity(*pa + *pb));
370
PyObject* QuantityPy::number_subtract_handler(PyObject* self, PyObject* other)
372
Quantity* pa = nullptr;
373
Quantity* pb = nullptr;
378
if (PyObject_TypeCheck(self, &(QuantityPy::Type))) {
379
pa = static_cast<QuantityPy*>(self)->getQuantityPtr();
382
pa = &pyToQuantity(a, self);
385
if (PyObject_TypeCheck(other, &(QuantityPy::Type))) {
386
pb = static_cast<QuantityPy*>(other)->getQuantityPtr();
389
pb = &pyToQuantity(b, other);
391
return new QuantityPy(new Quantity(*pa - *pb));
396
PyObject* QuantityPy::number_multiply_handler(PyObject* self, PyObject* other)
398
Quantity* pa = nullptr;
399
Quantity* pb = nullptr;
404
if (PyObject_TypeCheck(self, &(QuantityPy::Type))) {
405
pa = static_cast<QuantityPy*>(self)->getQuantityPtr();
408
pa = &pyToQuantity(a, self);
411
if (PyObject_TypeCheck(other, &(QuantityPy::Type))) {
412
pb = static_cast<QuantityPy*>(other)->getQuantityPtr();
415
pb = &pyToQuantity(b, other);
417
return new QuantityPy(new Quantity(*pa * *pb));
422
PyObject* QuantityPy::number_divide_handler(PyObject* self, PyObject* other)
424
Quantity* pa = nullptr;
425
Quantity* pb = nullptr;
430
if (PyObject_TypeCheck(self, &(QuantityPy::Type))) {
431
pa = static_cast<QuantityPy*>(self)->getQuantityPtr();
434
pa = &pyToQuantity(a, self);
437
if (PyObject_TypeCheck(other, &(QuantityPy::Type))) {
438
pb = static_cast<QuantityPy*>(other)->getQuantityPtr();
441
pb = &pyToQuantity(b, other);
443
return new QuantityPy(new Quantity(*pa / *pb));
448
PyObject* QuantityPy::number_remainder_handler(PyObject* self, PyObject* other)
450
if (!PyObject_TypeCheck(self, &(QuantityPy::Type))) {
451
PyErr_SetString(PyExc_TypeError, "First arg must be Quantity");
457
Base::Quantity* a = static_cast<QuantityPy*>(self)->getQuantityPtr();
460
if (PyObject_TypeCheck(other, &(QuantityPy::Type))) {
461
Base::Quantity* b = static_cast<QuantityPy*>(other)->getQuantityPtr();
464
else if (PyFloat_Check(other)) {
465
d2 = PyFloat_AsDouble(other);
467
else if (PyLong_Check(other)) {
468
d2 = (double)PyLong_AsLong(other);
471
PyErr_SetString(PyExc_TypeError, "Expected quantity or number");
475
PyObject* p1 = PyFloat_FromDouble(d1);
476
PyObject* p2 = PyFloat_FromDouble(d2);
477
PyObject* r = PyNumber_Remainder(p1, p2);
483
double q = PyFloat_AsDouble(r);
485
return new QuantityPy(new Quantity(q, a->getUnit()));
488
PyObject* QuantityPy::number_divmod_handler(PyObject* /*self*/, PyObject* /*other*/)
490
// PyNumber_Divmod();
491
PyErr_SetString(PyExc_NotImplementedError, "Not implemented");
495
PyObject* QuantityPy::number_power_handler(PyObject* self, PyObject* other, PyObject* /*modulo*/)
497
if (!PyObject_TypeCheck(self, &(QuantityPy::Type))) {
498
PyErr_SetString(PyExc_TypeError, "First arg must be Quantity");
504
if (PyObject_TypeCheck(other, &(QuantityPy::Type))) {
505
Base::Quantity* a = static_cast<QuantityPy*>(self)->getQuantityPtr();
506
Base::Quantity* b = static_cast<QuantityPy*>(other)->getQuantityPtr();
507
Base::Quantity q(a->pow(*b)); // to prevent memory leak in case of exception
509
return new QuantityPy(new Quantity(q));
511
if (PyFloat_Check(other)) {
512
Base::Quantity* a = static_cast<QuantityPy*>(self)->getQuantityPtr();
513
double b = PyFloat_AsDouble(other);
514
return new QuantityPy(new Quantity(a->pow(b)));
516
if (PyLong_Check(other)) {
517
Base::Quantity* a = static_cast<QuantityPy*>(self)->getQuantityPtr();
518
double b = (double)PyLong_AsLong(other);
519
return new QuantityPy(new Quantity(a->pow(b)));
521
PyErr_SetString(PyExc_TypeError, "Expected quantity or number");
527
int QuantityPy::number_nonzero_handler(PyObject* self)
529
if (!PyObject_TypeCheck(self, &(QuantityPy::Type))) {
533
Base::Quantity* a = static_cast<QuantityPy*>(self)->getQuantityPtr();
534
return a->getValue() != 0.0;
537
PyObject* QuantityPy::richCompare(PyObject* v, PyObject* w, int op)
539
if (PyObject_TypeCheck(v, &(QuantityPy::Type)) && PyObject_TypeCheck(w, &(QuantityPy::Type))) {
540
const Quantity* u1 = static_cast<QuantityPy*>(v)->getQuantityPtr();
541
const Quantity* u2 = static_cast<QuantityPy*>(w)->getQuantityPtr();
543
PyObject* res = nullptr;
546
res = (!(*u1 == *u2)) ? Py_True : Py_False;
550
res = (*u1 < *u2) ? Py_True : Py_False;
554
res = (*u1 < *u2) || (*u1 == *u2) ? Py_True : Py_False;
558
res = (!(*u1 < *u2)) && (!(*u1 == *u2)) ? Py_True : Py_False;
562
res = (!(*u1 < *u2)) ? Py_True : Py_False;
566
res = (*u1 == *u2) ? Py_True : Py_False;
571
else if (PyNumber_Check(v) && PyNumber_Check(w)) {
572
// Try to get floating numbers
573
double u1 = PyFloat_AsDouble(v);
574
double u2 = PyFloat_AsDouble(w);
575
PyObject* res = nullptr;
578
res = (u1 != u2) ? Py_True : Py_False;
582
res = (u1 < u2) ? Py_True : Py_False;
586
res = (u1 <= u2) ? Py_True : Py_False;
590
res = (u1 > u2) ? Py_True : Py_False;
594
res = (u1 >= u2) ? Py_True : Py_False;
598
res = (u1 == u2) ? Py_True : Py_False;
604
// This always returns False
605
Py_INCREF(Py_NotImplemented);
606
return Py_NotImplemented;
609
Py::Float QuantityPy::getValue() const
611
return Py::Float(getQuantityPtr()->getValue());
614
void QuantityPy::setValue(Py::Float arg)
616
getQuantityPtr()->setValue(arg);
619
Py::Object QuantityPy::getUnit() const
621
return Py::asObject(new UnitPy(new Unit(getQuantityPtr()->getUnit())));
624
void QuantityPy::setUnit(Py::Object arg)
626
Py::Type UnitType(Base::getTypeAsObject(&Base::UnitPy::Type));
627
if (!arg.isType(UnitType)) {
628
throw Py::AttributeError("Not yet implemented");
631
getQuantityPtr()->setUnit(*static_cast<Base::UnitPy*>((*arg))->getUnitPtr());
634
Py::String QuantityPy::getUserString() const
636
return {getQuantityPtr()->getUserString().toUtf8(), "utf-8"};
639
Py::Dict QuantityPy::getFormat() const
641
QuantityFormat fmt = getQuantityPtr()->getFormat();
644
dict.setItem("Precision", Py::Int(fmt.precision));
645
dict.setItem("NumberFormat", Py::Char(fmt.toFormat()));
646
dict.setItem("Denominator", Py::Int(fmt.denominator));
650
void QuantityPy::setFormat(Py::Dict arg)
652
QuantityFormat fmt = getQuantityPtr()->getFormat();
654
if (arg.hasKey("Precision")) {
655
Py::Int prec(arg.getItem("Precision"));
656
fmt.precision = static_cast<int>(prec);
659
if (arg.hasKey("NumberFormat")) {
660
Py::Object item = arg.getItem("NumberFormat");
661
if (item.isNumeric()) {
662
int format = static_cast<int>(Py::Int(item));
663
if (format < 0 || format > QuantityFormat::Scientific) {
664
throw Py::ValueError("Invalid format value");
666
fmt.format = static_cast<QuantityFormat::NumberFormat>(format);
670
std::string fmtstr = static_cast<std::string>(Py::String(form));
671
if (fmtstr.size() != 1) {
672
throw Py::ValueError("Invalid format character");
676
fmt.format = Base::QuantityFormat::toFormat(fmtstr[0], &ok);
678
throw Py::ValueError("Invalid format character");
683
if (arg.hasKey("Denominator")) {
684
Py::Int denom(arg.getItem("Denominator"));
685
int fracInch = static_cast<int>(denom);
686
// check that the value is positive and a power of 2
688
throw Py::ValueError("Denominator must be higher than zero");
691
if (fracInch & (fracInch - 1)) {
692
throw Py::ValueError("Denominator must be a power of two");
694
fmt.denominator = fracInch;
697
getQuantityPtr()->setFormat(fmt);
700
PyObject* QuantityPy::getCustomAttributes(const char* attr) const
702
QuantityPy* py = nullptr;
703
if (strcmp(attr, "Torr") == 0) {
704
py = new QuantityPy(new Quantity(Quantity::Torr));
706
else if (strcmp(attr, "mTorr") == 0) {
707
py = new QuantityPy(new Quantity(Quantity::mTorr));
709
else if (strcmp(attr, "yTorr") == 0) {
710
py = new QuantityPy(new Quantity(Quantity::yTorr));
712
else if (strcmp(attr, "PoundForce") == 0) {
713
py = new QuantityPy(new Quantity(Quantity::PoundForce));
715
else if (strcmp(attr, "AngularMinute") == 0) {
716
py = new QuantityPy(new Quantity(Quantity::AngMinute));
718
else if (strcmp(attr, "AngularSecond") == 0) {
719
py = new QuantityPy(new Quantity(Quantity::AngSecond));
723
py->setNotTracking();
729
int QuantityPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
734
PyObject* QuantityPy::number_invert_handler(PyObject* /*self*/)
736
PyErr_SetString(PyExc_TypeError, "bad operand type for unary ~");
740
PyObject* QuantityPy::number_lshift_handler(PyObject* /*self*/, PyObject* /*other*/)
742
PyErr_SetString(PyExc_TypeError, "unsupported operand type(s) for <<");
746
PyObject* QuantityPy::number_rshift_handler(PyObject* /*self*/, PyObject* /*other*/)
748
PyErr_SetString(PyExc_TypeError, "unsupported operand type(s) for >>");
752
PyObject* QuantityPy::number_and_handler(PyObject* /*self*/, PyObject* /*other*/)
754
PyErr_SetString(PyExc_TypeError, "unsupported operand type(s) for &");
758
PyObject* QuantityPy::number_xor_handler(PyObject* /*self*/, PyObject* /*other*/)
760
PyErr_SetString(PyExc_TypeError, "unsupported operand type(s) for ^");
764
PyObject* QuantityPy::number_or_handler(PyObject* /*self*/, PyObject* /*other*/)
766
PyErr_SetString(PyExc_TypeError, "unsupported operand type(s) for |");