0001:
0002:
0003:
0004:
0005:
0006:
0007:
0008:
0009:
0010:
0011:
0012:
0013:
0014:
0015:
0016:
0017:
0018:
0019:
0020:
0021:
0022:
0023:
0024:
0025:
0026:
0027:
0028:
0029:
0030:
0031:
0032:
0033:
0034:
0035:
0036:
0037:
0038:
0039:
0040:
0041:
0042:
0043:
0044:
0045:
0046:
0047:
0048:
0049:
0050:
0051:
0052:
0053:
0054:
0055:
0056:
0057:
0058:
0059:
0060:
0061:
0062:
0063:
0064:
0065:
0066:
0067:
0068:
0069:
0070:
0071:
0072:
0073:
0074:
0075:
0076:
0077:
0078:
0079:
0080:
0081:
0082:
0083:
0084:
0085:
0086:
0087:
0088:
0089:
0090:
0091:
0092:
0093:
0094:
0095:
0096:
0097:
0098:
0099:
0100:
0101:
0102:
0103:
0104:
0105:
0106:
0107:
0108:
0109:
0110:
0111:
0112:
0113:
0114:
0115:
0116:
0117:
0118:
0119:
0120:
0121:
0122:
0123:
0124:
0125:
0126:
0127:
0128:
0129:
0130:
0131:
0132:
0133:
0134:
0135:
0136:
0137:
0138:
0139:
0140:
0141:
0142:
0143:
0144:
0145:
0146:
0147:
0148:
0149:
0150:
0151:
0152:
0153:
0154:
0155:
0156:
0157:
0158:
0159:
0160:
0161:
0162:
0163:
0164:
0165:
0166:
0167:
0168:
0169:
0170:
0171:
0172:
0173:
0174:
0175:
0176:
0177:
0178:
0179:
0180:
0181:
0182:
0183:
0184:
0185:
0186:
0187:
0188:
0189:
0190:
0191:
0192:
0193:
0194:
0195:
0196:
0197:
0198:
0199:
0200:
0201:
0202:
0203:
0204:
0205:
0206:
0207:
0208:
0209:
0210:
0211:
0212:
0213:
0214:
0215:
0216:
0217:
0218:
0219:
0220:
0221:
0222:
0223:
0224:
0225:
0226:
0227:
0228:
0229:
0230:
0231:
0232:
0233:
0234:
0235:
0236:
0237:
0238:
0239:
0240:
0241:
0242:
0243:
0244:
0245:
0246:
0247:
0248:
0249:
0250:
0251:
0252:
0253:
0254:
0255:
0256:
0257:
0258:
0259:
0260:
0261:
0262:
0263:
0264:
0265:
0266:
0267:
0268:
0269:
0270:
0271:
0272:
0273:
0274:
0275:
0276:
0277:
0278:
0279:
0280:
0281:
0282:
0283:
0284:
0285:
0286:
0287:
0288:
0289:
0290:
0291:
0292:
0293:
0294:
0295:
0296:
0297:
0298:
0299:
0300:
0301:
0302:
0303:
0304:
0305:
0306:
0307:
0308:
0309:
0310:
0311:
0312:
0313:
0314:
0315:
0316:
0317:
0318:
0319:
0320:
0321:
0322:
0323:
0324:
0325:
0326:
0327:
0328:
0329:
0330:
0331:
0332:
0333:
0334:
0335:
0336:
0337:
0338:
0339:
0340:
0341:
0342:
0343:
0344:
0345:
0346:
0347:
0348:
0349:
0350:
0351:
0352:
0353:
0354:
0355:
0356:
0357:
0358:
0359:
0360:
0361:
0362:
0363:
0364:
0365:
0366:
0367:
0368:
0369:
0370:
0371:
0372:
0373:
0374:
0375:
0376:
0377:
0378:
0379:
0380:
0381:
0382:
0383:
0384:
0385:
0386:
0387:
0388:
0389:
0390:
0391:
0392:
0393:
0394:
0395:
0396:
0397:
0398:
0399:
0400:
0401:
0402:
0403:
0404:
0405:
0406:
0407:
0408:
0409:
0410:
0411:
0412:
0413:
0414:
0415:
0416:
0417:
0418:
0419:
0420:
0421:
0422:
0423:
0424:
0425:
0426:
0427:
0428:
0429:
0430:
0431:
0432:
0433:
0434:
0435:
0436:
0437:
0438:
0439:
0440:
0441:
0442:
0443:
0444:
0445:
0446:
0447:
0448:
0449:
0450:
0451:
0452:
0453:
0454:
0455:
0456:
0457:
0458:
0459:
0460:
0461:
0462:
0463:
0464:
0465:
0466:
0467:
0468:
0469:
0470:
0471:
0472:
0473:
0474:
0475:
0476:
0477:
0478:
0479:
0480:
0481:
0482:
0483:
0484:
0485:
0486:
0487:
0488:
0489:
0490:
0491:
0492:
0493:
0494:
0495:
0496:
0497:
0498:
0499:
0500:
0501:
0502:
0503:
0504:
0505:
0506:
0507:
0508:
0509:
0510:
0511:
0512:
0513:
0514:
0515:
0516:
0517:
0518:
0519:
0520:
0521:
0522:
0523:
0524:
0525:
0526:
0527:
0528:
0529:
0530:
0531:
0532:
0533:
0534:
0535:
0536:
0537:
0538:
0539:
0540:
0541:
0542:
0543:
0544:
0545:
0546:
0547:
0548:
0549:
0550:
0551:
0552:
0553:
0554:
0555:
0556:
0557:
0558:
0559:
0560:
0561:
0562:
0563:
0564:
0565:
0566:
0567:
0568:
0569:
0570:
0571:
0572:
0573:
0574:
0575:
0576:
0577:
0578:
0579:
0580:
0581:
0582:
0583:
0584:
0585:
0586:
0587:
0588:
0589:
0590:
0591:
0592:
0593:
0594:
0595:
0596:
0597:
0598:
0599:
0600:
0601:
0602:
0603:
0604:
0605:
0606:
0607:
0608:
0609:
0610:
0611:
0612:
0613:
0614:
0615:
0616:
0617:
0618:
0619:
0620:
0621:
0622:
0623:
0624:
0625:
0626:
0627:
0628:
0629:
0630:
0631:
0632:
0633:
0634:
0635:
0636:
0637:
0638:
0639:
0640:
0641:
0642:
0643:
0644:
0645:
0646:
0647:
0648:
0649:
0650:
0651:
0652:
0653:
0654:
0655:
0656:
0657:
0658:
0659:
0660:
0661:
0662:
0663:
0664:
0665:
0666:
0667:
0668:
0669:
0670:
0671:
0672:
0673:
0674:
0675:
0676:
0677:
0678:
0679:
0680:
0681:
0682:
0683:
0684:
0685:
0686:
0687:
0688:
0689:
0690:
0691:
0692:
0693:
0694:
0695:
0696:
0697:
0698:
0699:
0700:
0701:
0702:
0703:
0704:
0705:
0706:
0707:
0708:
0709:
0710:
0711:
0712:
0713:
0714:
0715:
0716:
0717:
0718:
0719:
0720:
0721:
0722:
0723:
0724:
0725:
0726:
0727:
0728:
0729:
0730:
0731:
0732:
0733:
0734:
0735:
0736:
0737:
0738:
0739:
0740:
0741:
0742:
0743:
0744:
0745:
0746:
0747:
0748:
0749:
0750:
0751:
0752:
0753:
0754:
0755:
0756:
0757:
0758:
0759:
0760:
0761:
0762:
0763:
0764:
0765:
0766:
0767:
0768:
0769:
0770:
0771:
0772:
0773:
0774:
0775:
0776:
0777:
0778:
0779:
0780:
0781:
0782:
0783:
0784:
0785:
0786:
0787:
0788:
0789:
0790:
0791:
0792:
0793:
0794:
0795:
0796:
0797:
0798:
0799:
0800:
0801:
0802:
0803:
0804:
0805:
0806:
0807:
0808:
0809:
0810:
0811:
0812:
0813:
0814:
0815:
0816:
0817:
0818:
0819:
0820:
0821:
0822:
0823:
0824:
0825:
0826:
0827:
0828:
0829:
0830:
0831:
0832:
0833:
0834:
0835:
0836:
0837:
0838:
0839:
0840:
0841:
0842:
0843:
0844:
0845:
0846:
0847:
0848:
0849:
0850:
0851:
0852:
0853:
0854:
0855:
0856:
0857:
0858:
0859:
0860:
0861:
0862:
0863:
0864:
0865:
0866:
0867:
0868:
0869:
0870:
0871:
0872:
0873:
0874:
0875:
0876:
0877:
0878:
0879:
0880:
0881:
0882:
0883:
0884:
0885:
0886:
0887:
0888:
0889:
0890:
0891:
0892:
0893:
0894:
0895:
0896:
0897:
0898:
0899:
0900:
0901:
0902:
0903:
0904:
0905:
0906:
0907:
0908:
0909:
0910:
0911:
0912:
0913:
0914:
0915:
0916:
0917:
0918:
0919:
0920:
0921:
0922:
0923:
0924:
0925:
0926:
0927:
0928:
0929:
0930:
0931:
0932:
0933:
0934:
0935:
0936:
0937:
0938:
0939:
0940:
0941:
0942:
0943:
0944:
0945:
0946:
0947:
0948:
0949:
0950:
0951:
0952:
0953:
0954:
0955:
0956:
0957:
0958:
0959:
0960:
0961:
0962:
0963:
0964:
0965:
0966:
0967:
0968:
0969:
0970:
0971:
0972:
0973:
0974:
0975:
0976:
0977:
0978:
0979:
0980:
0981:
0982:
0983:
0984:
0985:
0986:
0987:
0988:
0989:
0990:
0991:
0992:
0993:
0994:
0995:
0996:
0997:
0998:
0999:
1000:
1001:
1002:
1003:
1004:
1005:
1006:
1007:
1008:
1009:
1010:
1011:
1012:
1013:
1014:
1015:
1016:
1017:
1018:
1019:
1020:
1021:
1022:
1023:
1024:
1025:
1026:
1027:
1028:
1029:
1030:
1031:
1032:
1033:
1034:
1035:
1036:
1037:
1038:
1039:
1040:
1041:
1042:
1043:
1044:
1045:
1046:
1047:
1048:
1049:
1050:
1051:
1052:
1053:
1054:
1055:
1056:
1057:
1058:
1059:
1060:
1061:
1062:
1063:
1064:
1065:
1066:
1067:
1068:
1069:
1070:
1071:
1072:
1073:
1074:
1075:
1076:
1077:
1078:
1079:
1080:
1081:
1082:
1083:
1084:
1085:
1086:
1087:
1088:
1089:
1090:
1091:
1092:
1093:
1094:
1095:
1096:
1097:
1098:
1099:
1100:
1101:
1102:
1103:
1104:
1105:
1106:
1107:
1108:
1109:
1110:
1111:
1112:
1113:
1114:
1115:
1116:
1117:
1118:
1119:
1120:
1121:
1122:
1123:
1124:
1125:
1126:
1127:
1128:
1129:
1130:
1131:
1132:
1133:
1134:
1135:
1136:
1137:
1138:
1139:
1140:
1141:
1142:
1143:
1144:
1145:
1146:
1147:
1148:
1149:
1150:
1151:
1152:
1153:
1154:
1155:
1156:
1157:
1158:
1159:
1160:
1161:
1162:
1163:
1164:
1165:
1166:
1167:
1168:
1169:
1170:
1171:
1172:
1173:
1174:
1175:
1176:
1177:
1178:
1179:
1180:
1181:
1182:
1183:
1184:
1185:
1186:
1187:
1188:
1189:
1190:
1191:
1192:
1193:
1194:
1195:
1196:
1197:
1198:
1199:
1200:
1201:
1202:
1203:
1204:
1205:
1206:
1207:
1208:
1209:
1210:
1211:
1212:
1213:
1214:
1215:
1216:
1217:
1218:
1219:
1220:
1221:
1222:
1223:
1224:
1225:
1226:
1227:
1228:
1229:
1230:
1231:
1232:
1233:
1234:
1235:
1236:
1237:
1238:
1239:
1240:
1241:
1242:
1243:
1244:
1245:
1246:
1247:
1248:
1249:
1250:
1251:
1252:
1253:
1254:
1255:
1256:
1257:
1258:
1259:
1260:
1261:
1262:
1263:
1264:
1265:
1266:
1267:
1268:
1269:
1270:
1271:
1272:
1273:
1274:
1275:
1276:
1277:
1278:
1279:
1280:
1281:
1282:
1283:
1284:
1285:
1286:
1287:
1288:
1289:
1290:
1291:
1292:
1293:
1294:
1295:
1296:
1297:
1298:
1299:
1300:
1301:
1302:
1303:
1304:
1305:
1306:
1307:
1308:
1309:
1310:
1311:
1312:
1313:
1314:
1315:
1316:
1317:
1318:
1319:
1320:
1321:
1322:
1323:
1324:
1325:
1326:
1327:
1328:
1329:
1330:
1331:
1332:
1333:
1334:
1335:
1336:
1337:
1338:
1339:
1340:
1341:
1342:
1343:
1344:
1345:
1346:
1347:
1348:
1349:
1350:
1351:
1352:
1353:
1354:
1355:
1356:
1357:
1358:
1359:
1360:
1361:
1362:
1363:
1364:
1365:
1366:
1367:
1368:
1369:
1370:
1371:
1372:
1373:
1374:
1375:
1376:
1377:
1378:
1379:
1380:
1381:
1382:
1383:
1384:
1385:
1386:
1387:
1388:
1389:
1390:
1391:
1392:
1393:
1394:
1395:
1396:
1397:
1398:
1399:
1400:
1401:
1402:
1403:
1404:
1405:
1406:
1407:
1408:
1409:
1410:
1411:
1412:
1413:
1414:
1415:
1416:
1417:
1418:
1419:
1420:
1421:
1422:
1423:
1424:
1425:
1426:
1427:
1428:
1429:
1430:
1431:
1432:
1433:
1434:
1435:
1436:
1437:
1438:
1439:
1440:
1441:
1442:
1443:
1444:
1445:
1446:
1447:
1448:
1449:
1450:
1451:
1452:
1453:
1454:
1455:
1456:
1457:
1458:
1459:
1460:
1461:
1462:
1463:
1464:
1465:
1466:
1467:
1468:
1469:
1470:
1471:
1472:
1473:
1474:
1475:
1476:
1477:
1478:
1479:
1480:
1481:
1482:
1483:
1484:
1485:
1486:
1487:
1488:
1489:
1490:
1491:
1492:
1493:
1494:
1495:
1496:
1497:
1498:
1499:
1500:
1501:
1502:
1503:
1504:
1505:
1506:
1507:
1508:
1509:
1510:
1511:
1512:
1513:
1514:
1515:
1516:
1517:
1518:
1519:
1520:
1521:
1522:
1523:
1524:
1525:
1526:
1527:
1528:
1529:
1530:
1531:
1532:
1533:
1534:
1535:
1536:
1537:
1538:
1539:
1540:
1541:
1542:
1543:
1544:
1545:
1546:
1547:
1548:
1549:
1550:
1551:
1552:
1553:
1554:
1555:
1556:
1557:
1558:
1559:
1560:
1561:
1562:
1563:
1564:
1565:
1566:
1567:
1568:
1569:
1570:
1571:
1572:
1573:
1574:
1575:
1576:
1577:
1578:
1579:
1580:
1581:
1582:
1583:
1584:
1585:
1586:
1587:
1588:
1589:
1590:
1591:
1592:
1593:
1594:
1595:
1596:
1597:
1598:
1599:
1600:
1601:
1602:
1603:
1604:
1605:
1606:
1607:
1608:
1609:
1610:
1611:
1612:
1613:
1614:
1615:
1616:
1617:
1618:
1619:
1620:
1621:
1622:
1623:
1624:
1625:
1626:
1627:
1628:
1629:
1630:
1631:
1632:
1633:
1634:
1635:
1636:
1637:
1638:
1639:
1640:
1641:
1642:
1643:
1644:
1645:
1646:
1647:
1648:
1649:
1650:
1651:
1652:
1653:
1654:
1655:
1656:
1657:
1658:
1659:
1660:
1661:
1662:
1663:
1664:
1665:
1666:
1667:
1668:
1669:
1670:
1671:
1672:
1673:
1674:
1675:
1676:
1677:
1678:
1679:
1680:
1681:
1682:
1683:
1684:
1685:
1686:
1687:
1688:
1689:
1690:
1691:
1692:
1693:
1694:
1695:
1696:
1697:
1698:
1699:
1700:
1701:
1702:
1703:
1704:
1705:
1706:
1707:
1708:
1709:
1710:
1711:
1712:
1713:
1714:
1715:
1716:
1717:
1718:
1719:
1720:
1721:
1722:
1723:
1724:
1725:
1726:
1727:
1728:
1729:
1730:
1731:
1732:
1733:
1734:
1735:
1736:
1737:
1738:
1739:
1740:
1741:
1742:
1743:
1744:
1745:
1746:
1747:
1748:
1749:
1750:
1751:
1752:
1753:
1754:
1755:
1756:
1757:
1758:
1759:
1760:
1761:
1762:
1763:
1764:
1765:
1766:
1767:
1768:
1769:
1770:
1771:
1772:
1773:
1774:
1775:
1776:
1777:
1778:
1779:
1780:
1781:
1782:
1783:
1784:
1785:
1786:
1787:
1788:
1789:
1790:
1791:
1792:
1793:
1794:
1795:
1796:










/*
* Copyright (c), Zeriph Enterprises
* All rights reserved.
*
* Contributor(s):
* Zechariah Perez, omni (at) zeriph (dot) com
*
* THIS SOFTWARE IS PROVIDED BY ZERIPH AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ZERIPH AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if !defined(OMNI_MATH_HPP)
#define OMNI_MATH_HPP 1
#include <omni/types/math_t.hpp>
#include <omni/delegate/1.hpp>
#include <omni/delegate/2.hpp>
/*
TODO: finish this .. what's marked as C++11, add a function that does the math along with the OMNI_USE_CXX_MATH that returns the C++11 version
BigMul(Int32, Int32) - Produces the full product of two 32-bit numbers.
int64_t big_mul(int32_t x, int32_t y) { return static_cast<int64_t>(x) + static_cast<int64_t>(y); }
BigMul(Int64, Int64, Int64) - Produces the full product of two 64-bit numbers.
BigMul(UInt64, UInt64, UInt64) - Produces the full product of two unsigned 64-bit numbers.
--see https://stackoverflow.com/questions/28868367/getting-the-high-part-of-64-bit-integer-multiplication
BitDecrement(Double) - Returns the next smallest value that compares less than x.
double d = X;
while (d >= X) {
d -= std::numeric_limits(double)::epsilon();
}
return d;
BitIncrement(Double) - Returns the next largest value that compares greater than x.
double d = X;
while (d >= X) {
d += std::numeric_limits(double)::epsilon();
}
return d;
CopySign(Double, Double) - Returns a value with the magnitude of x and the sign of y.
template < typename T > inline T copy_sign(const T& x, const T& y)
(y>0) & (x<0) -> (-x)
(y<0) & (x>0) -> (-x)
{x,y} >= 0 -OR- {x,y} =< 0 -> x
DivRem(Int32, Int32, Int32) - Calculates the quotient of two 32-bit signed integers and also returns the remainder in an output parameter.
DivRem(Int64, Int64, Int64) - Calculates the quotient of two 64-bit signed integers and also returns the remainder in an output parameter.
FusedMultiplyAdd(Double, Double, Double) - Returns (x * y) + z, rounded as one ternary operation.
IEEERemainder(Double, Double) - Returns the remainder resulting from the division of a specified number by another specified number.
ILogB(Double) - Returns the base 2 integer logarithm of a specified number.
std::ilogb - C++11 {also just (int)(log(x)/log(2))}
Log2(Double) - Returns the base 2 logarithm of a specified number.
std::log2 - C++11
MaxMagnitude(Double, Double) - Returns the larger magnitude of two double-precision floating-point numbers.
MinMagnitude(Double, Double) - Returns the smaller magnitude of two double-precision floating-point numbers.
Round(X) - Rounds a double-precision floating-point value to the nearest integral value, and rounds midpoint values to the nearest even number.
std::round - C++11
Round(Double, Int32) - Rounds a double-precision floating-point value to a specified number of fractional digits, and rounds midpoint values to the nearest even number.
Round(Double, Int32, MidpointRounding) - Rounds a double-precision floating-point value to a specified number of fractional digits, and uses the specified rounding convention for midpoint values.
Round(Double, MidpointRounding) - Rounds a double-precision floating-point value to the nearest integer, and uses the specified rounding convention for midpoint values.
*/
// DEV_NOTE: since these are specifically helper functions, no overflow checks are done
namespace omni {
namespace math {
// Returns the angle whose hyperbolic cosine is the specified number. (as rad's)
inline float acosh(float x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::acosh(x);
#else
return std::log(x + std::sqrt((x * x) - 1));
#endif
}
inline double acosh(double x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::acosh(x);
#else
return std::log(x + std::sqrt((x * x) - 1));
#endif
}
inline long double acosh(long double x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::acosh(x);
#else
return std::log(x + std::sqrt((x * x) - 1));
#endif
}
inline double acosh(int8_t x) { return omni::math::acosh(static_cast<double>(x)); }
inline double acosh(uint8_t x) { return omni::math::acosh(static_cast<double>(x)); }
inline double acosh(int16_t x) { return omni::math::acosh(static_cast<double>(x)); }
inline double acosh(uint16_t x) { return omni::math::acosh(static_cast<double>(x)); }
inline double acosh(int32_t x) { return omni::math::acosh(static_cast<double>(x)); }
inline double acosh(uint32_t x) { return omni::math::acosh(static_cast<double>(x)); }
inline double acosh(int64_t x) { return omni::math::acosh(static_cast<double>(x)); }
inline double acosh(uint64_t x) { return omni::math::acosh(static_cast<double>(x)); }
// Returns the angle whose hyperbolic sine is the specified number. (as rad's)
inline float asinh(float x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::asinh(x);
#else
return std::log(x + std::sqrt((x * x) + 1));
#endif
}
inline double asinh(double x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::asinh(x);
#else
return std::log(x + std::sqrt((x * x) + 1));
#endif
}
inline long double asinh(long double x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::asinh(x);
#else
return std::log(x + std::sqrt((x * x) + 1));
#endif
}
inline double asinh(int8_t x) { return omni::math::asinh(static_cast<double>(x)); }
inline double asinh(uint8_t x) { return omni::math::asinh(static_cast<double>(x)); }
inline double asinh(int16_t x) { return omni::math::asinh(static_cast<double>(x)); }
inline double asinh(uint16_t x) { return omni::math::asinh(static_cast<double>(x)); }
inline double asinh(int32_t x) { return omni::math::asinh(static_cast<double>(x)); }
inline double asinh(uint32_t x) { return omni::math::asinh(static_cast<double>(x)); }
inline double asinh(int64_t x) { return omni::math::asinh(static_cast<double>(x)); }
inline double asinh(uint64_t x) { return omni::math::asinh(static_cast<double>(x)); }
// Returns the angle whose hyperbolic tangent is the specified number. (as rad's)
inline float atanh(float x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::atanh(x);
#else
// TODO: add some error handling here??
return static_cast<float>(std::log((1.0 + x) / (1.0 - x)) / 2);
#endif
}
inline double atanh(double x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::atanh(x);
#else
return (std::log((1.0 + x) / (1.0 - x)) / 2);
#endif
}
inline long double atanh(long double x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::atanh(x);
#else
return (std::log((1.0 + x) / (1.0 - x)) / 2);
#endif
}
inline double atanh(int8_t x) { return omni::math::atanh(static_cast<double>(x)); }
inline double atanh(uint8_t x) { return omni::math::atanh(static_cast<double>(x)); }
inline double atanh(int16_t x) { return omni::math::atanh(static_cast<double>(x)); }
inline double atanh(uint16_t x) { return omni::math::atanh(static_cast<double>(x)); }
inline double atanh(int32_t x) { return omni::math::atanh(static_cast<double>(x)); }
inline double atanh(uint32_t x) { return omni::math::atanh(static_cast<double>(x)); }
inline double atanh(int64_t x) { return omni::math::atanh(static_cast<double>(x)); }
inline double atanh(uint64_t x) { return omni::math::atanh(static_cast<double>(x)); }
// Returns the cube root of a number
inline float cbrt(float x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::cbrt(x);
#else
if (x < 0) {
return static_cast<float>(-std::pow(-x, (1.0 / 3.0)));
}
return static_cast<float>(std::pow(x, (1.0 / 3.0)));
#endif
}
inline double cbrt(double x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::cbrt(x);
#else
if (x < 0) {
return -std::pow(-x, (1.0 / 3.0));
}
return std::pow(x, (1.0 / 3.0));
#endif
}
inline long double cbrt(long double x)
{
#if defined(OMNI_USE_CXX_MATH)
return std::cbrt(x);
#else
if (x < 0) {
return -std::pow(-x, (1.0 / 3.0));
}
return std::pow(x, (1.0 / 3.0));
#endif
}
inline double cbrt(int8_t x) { return omni::math::cbrt(static_cast<double>(x)); }
inline double cbrt(uint8_t x) { return omni::math::cbrt(static_cast<double>(x)); }
inline double cbrt(int16_t x) { return omni::math::cbrt(static_cast<double>(x)); }
inline double cbrt(uint16_t x) { return omni::math::cbrt(static_cast<double>(x)); }
inline double cbrt(int32_t x) { return omni::math::cbrt(static_cast<double>(x)); }
inline double cbrt(uint32_t x) { return omni::math::cbrt(static_cast<double>(x)); }
inline double cbrt(int64_t x) { return omni::math::cbrt(static_cast<double>(x)); }
inline double cbrt(uint64_t x) { return omni::math::cbrt(static_cast<double>(x)); }
template < typename T >
inline double calculate_angle_radians(T x, T y, T bx, T by, T cx, T cy)
{
double c = OMNI_DISTANCE_2POINTS_2D_FW(x, y, bx, by);
double b = OMNI_DISTANCE_2POINTS_2D_FW(x, y, cx, cy);
if (omni::math::are_equal(b, 0.0) || omni::math::are_equal(c, 0.0)) {
return 0.0;
}
double cb = OMNI_DISTANCE_2POINTS_2D_FW(bx, by, cx, cy);
return std::acos(((b * b) + (c * c) - (cb * cb)) / (2.0 * b * c));
}
template < typename T >
inline double calculate_angle_radians(const omni::math::dimensional<T, 2>& a, const omni::math::dimensional<T, 2>& b, const omni::math::dimensional<T, 2>& c)
{
return omni::math::calculate_angle_radians(a[0], a[1], b[0], b[1], c[0], c[1]);
}
template < typename T >
inline double calculate_angle(T x, T y, T bx, T by, T cx, T cy)
{
return OMNI_RAD_TO_DEG(omni::math::calculate_angle_radians(x, y, bx, by, cx, cy));
}
template < typename T >
inline double calculate_angle(const omni::math::dimensional<T, 2>& a, const omni::math::dimensional<T, 2>& b, const omni::math::dimensional<T, 2>& c)
{
return omni::math::calculate_angle(a[0], a[1], b[0], b[1], c[0], c[1]);
}
template < typename T >
inline void point_on_circle_unsafe(double degrees, double radius, T center_x, T center_y, T& out_x, T& out_y)
{
out_x = static_cast<T>(OMNI_MATH_GET_POINT_X_FW(T, center_x, radius, degrees));
out_y = static_cast<T>(OMNI_MATH_GET_POINT_Y_FW(T, center_y, radius, degrees));
}
template < typename T >
inline void rotate_point(double degrees, const omni::math::rotation_direction& dir, T center_x, T center_y, T& rotate_x, T& rotate_y)
{
if ((degrees < 0.0) || (degrees > 360.0)) {
OMNI_ERR_FW("angle must be between 0-360 degrees", omni::exceptions::overflow_error("angle must be between 0-360 degrees"))
}
if (omni::math::are_equal(degrees, 0.0) || omni::math::are_equal(degrees, 360.0)) {
return; // no rotation
}
// build the triangle to get the angle based on relative location to the center
// point. The center point is treated like the origin so we need the angle of
// inflection first, then we can calculate the new value
if (center_x < rotate_x) {
if (center_y < rotate_y) {
// quad 1
/*
{cx,ry}| / rx,ry
|a/
|/
----------------
cx,cy
*/
degrees = (omni::math::calculate_angle(center_x, center_y, rotate_x, rotate_y, center_x, rotate_y)) +
((dir == omni::math::rotation_direction::CLOCKWISE) ? degrees : (-degrees));
} else if (center_y > rotate_y) {
// quad 2
/*
cx,cy {rx,cy}
----------------
|\a
| \
| \ rx,ry
*/
degrees = (omni::math::calculate_angle(center_x, center_y, rotate_x, rotate_y, rotate_x, center_y) + 90.0) +
((dir == omni::math::rotation_direction::CLOCKWISE) ? degrees : (-degrees));
} else { // center_y == rotate_y
// quad 1
/*
|
---cx,cy----------rx,ry---
|
*/
degrees = 90.0 + ((dir == omni::math::rotation_direction::CLOCKWISE) ? degrees : (-degrees));
}
} else if (center_x > rotate_x) {
if (center_y > rotate_y) {
// quad 3
/*
cx,cy
-----------------
/a|
/ |
rx,ry / |{cx,ry}
*/
degrees = (omni::math::calculate_angle(center_x, center_y, rotate_x, rotate_y, center_x, rotate_y) + 180.0) +
((dir == omni::math::rotation_direction::CLOCKWISE) ? degrees : (-degrees));
} else if (center_y < rotate_y) {
// quad 4
/*
rx,ry \ |
\ |
a\|
----------------
{rx,cy} cx,cy
*/
degrees = (omni::math::calculate_angle(center_x, center_y, rotate_x, rotate_y, rotate_x, center_y) + 270.0) +
((dir == omni::math::rotation_direction::CLOCKWISE) ? degrees : (-degrees));
} else { // center_y == rotate_y
// quad 3
/*
|
---rx,ry----------cx,cy---
|
*/
degrees = 270.0 + ((dir == omni::math::rotation_direction::CLOCKWISE) ? degrees : (-degrees));
}
} else { // center_x == rotate_x
if (center_y < rotate_y) {
// quad 1
/*
|
rx,ry
|
|
|
---cx,cy--
|
*/
degrees = ((dir == omni::math::rotation_direction::CLOCKWISE) ? degrees : (-degrees));
} else if (center_y > rotate_y) {
// quad 3
/*
|
---cx,cy--
|
|
|
rx,ry
|
*/
degrees = 180.0 + ((dir == omni::math::rotation_direction::CLOCKWISE) ? degrees : (-degrees));
} else { // center_y == rotate_y
// cx == rx && cy == ry .. same points, no rotation
return;
}
}
// rotate the point now based on the new angle of inflection
if (degrees > 360.0) { degrees = degrees - 360; }
else if (degrees < 0.0) { degrees = degrees + 360; }
omni::math::point_on_circle_unsafe(
degrees,
static_cast<double>(OMNI_DISTANCE_2POINTS_2D_FW(center_x, center_y, rotate_x, rotate_y)),
center_x, center_y,
rotate_x, rotate_y);
}
template < typename T >
inline void rotate_point(double degrees, const omni::math::rotation_direction& dir, T center_x, T center_y, omni::math::dimensional<T, 2>& rotate)
{
omni::math::rotate_point(degrees, dir, center_x, center_y, rotate[0], rotate[1]);
}
template < typename T >
inline void rotate_point(double degrees, const omni::math::rotation_direction& dir, const omni::math::dimensional<T, 2>& center, omni::math::dimensional<T, 2>& rotate)
{
omni::math::rotate_point(degrees, dir, center[0], center[1], rotate[0], rotate[1]);
}
template < typename T >
inline bool angles_are_coterminal(T x, T y)
{
T a = std::abs(x);
T b = std::abs(y);
if (a > static_cast<T>(360)) {
a = a % static_cast<T>(360);
}
if (b > static_cast<T>(360)) {
b = b % static_cast<T>(360);
}
return omni::math::are_equal(a, b);
}
template < typename T >
inline double area_of_circle(T radius)
{
return OMNI_PI * static_cast<double>(radius * radius);
}
template < typename T >
inline double area_of_circle(T value, const omni::math::circle_area& type)
{
switch (type) {
case omni::math::circle_area::RADIUS:
return OMNI_PI * static_cast<double>(value * value);
case omni::math::circle_area::DIAMETER:
return OMNI_PI_4 * (value * value);
case omni::math::circle_area::CIRCUMFERENCE:
return static_cast<double>(value * value) / OMNI_PI_X4;
default:
OMNI_ERR_FW("invalid area type specified", omni::exceptions::index_out_of_range("invalid area type specified"))
break;
}
return 0.0;
}
template < typename T >
inline double area_of_circle_sector(T radius, double degrees)
{
return ((degrees * OMNI_PI) / 360.0) * (radius * radius);
}
template < typename T >
inline double area_of_circle_segment(T radius, double degrees)
{
return ((((degrees * OMNI_PI) / 360.0) - (std::sin(OMNI_DEG_TO_RAD(degrees)) / 2.0)) * (static_cast<double>(radius) * radius));
}
template < typename T >
inline T area_of_rectangle(T width, T height)
{
return width * height;
}
template < typename T >
inline double area_of_triangle(T base, T height)
{
return 0.5 * (base * height);
}
template < typename T >
inline double area_of_triangle_sss(T a, T b, T c)
{
// Heron's formula
double s = static_cast<double>(a + b + c) * 0.5; // (s = (a+b+c+) / 2.0;
return std::sqrt( (s*(s - a)) * (s*(s - b)) * (s*(s - c)) );
}
template < typename T >
inline double area_of_triangle_sas(double degrees, T side1, T side2)
{
return (0.5 * side1 * side2) * std::sin(OMNI_DEG_TO_RAD(degrees));
}
template < typename T >
inline double area_of_triangle(double degrees, T side1, T side2)
{
return omni::math::area_of_triangle_sas(degrees, side1, side2);
}
template < typename T >
inline double area_of_triangle(T ax, T ay, T bx, T by, T cx, T cy)
{
return omni::math::area_of_triangle(
omni::math::calculate_angle(ax, ay, bx, by, cx, cy),
OMNI_DISTANCE_2POINTS_2D_FW(ax, ay, bx, by),
OMNI_DISTANCE_2POINTS_2D_FW(ax, ay, cx, cy)
);
}
template < typename T >
inline double area_of_quadrilateral(T a, T b, T c, T d, double angle_a_degrees, double angle_c_degrees)
{
// Bretschneider's formula
double s = static_cast<double>(a + b + c + d) / 2.0;
return std::sqrt(
((s - a) * (s - b) * (s - c) * (s - d)) -
(
0.5 * (a * b * c * d) *
(
1 + std::cos(OMNI_DEG_TO_RAD((angle_a_degrees + angle_c_degrees)))
)
)
);
}
inline double arc_length(double radius, double degrees)
{
return radius * OMNI_PI_180 * degrees;
}
template < typename T >
inline T delta(T a, T b)
{
return a - b;
}
template < typename T >
inline T delta_squared(T a, T b)
{
return (a - b) * (a - b);
}
template < typename T >
inline void calculate_point_zero_distance(T start_x, T start_y, T end_x, T end_y, T distance_from_start, T& out_x, T& out_y)
{
out_x = static_cast<T>((distance_from_start * static_cast<double>(end_x)) + ((1.0 - distance_from_start) * static_cast<double>(start_x)));
out_y = static_cast<T>((distance_from_start * static_cast<double>(end_y)) + ((1.0 - distance_from_start) * static_cast<double>(start_y)));
}
template < typename T >
inline void calculate_point(T start_x, T start_y, T end_x, T end_y, T distance_from_start, T known_distance, T& out_x, T& out_y)
{
double seg_rat = (static_cast<double>(distance_from_start) / known_distance);
out_x = static_cast<T>((seg_rat * static_cast<double>(end_x)) + ((1.0 - seg_rat) * static_cast<double>(start_x)));
out_y = static_cast<T>((seg_rat * static_cast<double>(end_y)) + ((1.0 - seg_rat) * static_cast<double>(start_y)));
}
template < typename T >
inline void calculate_point(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, T known_distance, omni::math::dimensional<T, 2>& out)
{
omni::math::calculate_point(start[0], start[1], end[0], end[1], (OMNI_DISTANCE_2POINTS_2D_FW(start[0], start[1], end[0], end[0])), known_distance, out[0], out[1]);
}
template < typename T >
inline omni::math::dimensional<T, 2> calculate_point(T start_x, T start_y, T end_x, T end_y, T distance_from_start, T known_distance)
{
omni::math::dimensional<T, 2> ret;
omni::math::calculate_point<T>(start_x, start_y, end_x, end_y, distance_from_start, known_distance, ret[0], ret[1]);
return ret;
}
template < typename T >
inline omni::math::dimensional<T, 2> calculate_point(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, T distance_from_start, T known_distance)
{
return omni::math::calculate_point(start[0], start[1], end[0], end[1], distance_from_start, known_distance);
}
template < typename T >
inline void calculate_point(T start_x, T start_y, T end_x, T end_y, T distance_from_start, T& out_x, T& out_y)
{
double point_dist = OMNI_DISTANCE_2POINTS_2D_FW(start_x, start_y, end_x, end_y);
double seg_rat = (omni::math::are_equal(static_cast<double>(distance_from_start), 0.0) ? static_cast<double>(distance_from_start) : (static_cast<double>(distance_from_start) / point_dist));
out_x = static_cast<T>((seg_rat * static_cast<double>(end_x)) + ((1.0 - seg_rat) * static_cast<double>(start_x)));
out_y = static_cast<T>((seg_rat * static_cast<double>(end_y)) + ((1.0 - seg_rat) * static_cast<double>(start_y)));
}
template < typename T >
inline void calculate_point(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, omni::math::dimensional<T, 2>& out)
{
omni::math::calculate_point(start[0], start[1], end[0], end[1], (OMNI_DISTANCE_2POINTS_2D_FW(start[0], start[1], end[0], end[0])), out[0], out[1]);
}
template < typename T >
inline omni::math::dimensional<T, 2> calculate_point(T start_x, T start_y, T end_x, T end_y, T distance_from_start)
{
omni::math::dimensional<T, 2> ret;
omni::math::calculate_point<T>(start_x, start_y, end_x, end_y, distance_from_start, ret[0], ret[1]);
return ret;
}
template < typename T >
inline omni::math::dimensional<T, 2> calculate_point(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, T distance_from_start)
{
return omni::math::calculate_point(start[0], start[1], end[0], end[1], distance_from_start);
}
template < typename T >
inline double circle_circumference(T radius)
{
return OMNI_PI * static_cast<double>(radius) * 2.0;
}
template < typename T >
inline bool circle_contains_point_on_edge(T center_x, T center_y, double radius, T x, T y)
{
return omni::math::are_equal(
static_cast<double>(((x - center_x) * (x - center_x)) + ((y - center_y) * (y - center_y))),
(radius * radius)
);
}
template < typename T >
inline bool circle_contains_point_on_edge(T center_x, T center_y, double radius, const omni::math::dimensional<T, 2>& point)
{
return omni::math::circle_contains_point_on_edge(center_x, center_y, radius, point[0], point[1]);
}
template < typename T >
inline bool circle_contains_point(T center_x, T center_y, double radius, T x, T y, bool include_edge)
{
return OMNI_CIRCLE_CONTAINS_POINT_FW(center_x, center_y, radius, x, y, include_edge);
}
template < typename T >
inline bool circle_contains_point(T center_x, T center_y, double radius, T x, T y)
{
return OMNI_CIRCLE_CONTAINS_POINT_FW(center_x, center_y, radius, x, y, true);
}
template < typename T >
inline bool circle_contains_point(T center_x, T center_y, double radius, const omni::math::dimensional<T, 2>& point, bool include_edge)
{
return OMNI_CIRCLE_CONTAINS_POINT_FW(center_x, center_y, radius, point[0], point[1], include_edge);
}
template < typename T >
inline bool circle_contains_point(T center_x, T center_y, double radius, const omni::math::dimensional<T, 2>& point)
{
return OMNI_CIRCLE_CONTAINS_POINT_FW(center_x, center_y, radius, point[0], point[1], true);
}
template < typename T >
inline bool circles_intersect(T x1, T y1, double r1, T x2, T y2, double r2, bool include_edge)
{
return (include_edge ?
(OMNI_DISTANCE_2POINTS_2D_FW(x1, y1, x2, y2) <= (r1 + r2)) :
(OMNI_DISTANCE_2POINTS_2D_FW(x1, y1, x2, y2) < (r1 + r2))
);
}
template < typename T >
inline bool circles_intersect(T x1, T y1, double r1, T x2, T y2, double r2)
{
return omni::math::circles_intersect<T>(x1, y1, r1, x2, y2, r2, true);
}
template < typename T >
inline const T& clamp(const T& value, const T& min_val, const T& max_val)
{
#if defined(OMNI_USE_CXX_MATH) && (OMNI_LANGUAGE_CPP_VERSION >= 17)
return std::clamp<T>(value, min_val, max_val);
#else
if (min_val > max_val) {
OMNI_ERR_FW("max value must be greater than or equal to min value", omni::exceptions::invalid_range("max value must be greater than or equal to min value"))
}
if (value < min_val) { return min_val; }
if (value > max_val) { return max_val; }
return value;
#endif
}
template < typename T >
inline const T& clamp(const T& value, const T& min_val, const T& max_val, const omni::delegate2<bool, T, T>& comp)
{
#if defined(OMNI_USE_CXX_MATH) && (OMNI_LANGUAGE_CPP_VERSION >= 17)
// TODO: need to pass along the method for comp .. maybe do an auto lambda??
return std::clamp<T>(value, min_val, max_val, [value, min_val, max_val, comp] {
if (comp(value, min_val)) { return min_val; }
if (comp(value, max_val)) { return max_val; }
return value;
});
#else
if (min_val > max_val) {
OMNI_ERR_FW("max value must be greater than or equal to min value", omni::exceptions::invalid_range("max value must be greater than or equal to min value"))
}
if (comp(value, min_val)) { return min_val; }
if (comp(value, max_val)) { return max_val; }
return value;
#endif
}
inline double deg_to_rad(double deg)
{
return OMNI_DEG_TO_RAD(deg); // radians = degrees * 180 / π
}
inline double degrees_to_radians(double deg)
{
return OMNI_DEG_TO_RAD(deg); // radians = degrees * 180 / π
}
template < typename T >
inline double distance_between_2_points(T start_x, T start_y, T end_x, T end_y)
{
return OMNI_DISTANCE_2POINTS_2D_FW(start_x, start_y, end_x, end_y);
}
template < typename T >
inline double distance_between_2_points(T start_x, T start_y, T start_z, T end_x, T end_y, T end_z)
{
return OMNI_DISTANCE_2POINTS_3D_FW(start_x, start_y, start_z, end_x, end_y, end_z);
}
template < typename T >
inline double distance_between_2_points(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end)
{
return omni::math::distance_between_2_points(start[0], start[1], end[0], end[1]);
}
template < typename T >
inline double distance_between_2_points(const omni::math::dimensional<T, 3>& start, const omni::math::dimensional<T, 3>& end)
{
return omni::math::distance_between_2_points(start[0], start[1], start[2], end[0], end[1], end[2]);
}
template < typename T >
inline void extend_line(T start_x, T start_y, T end_x, T end_y, T length, T& out_x, T& out_y)
{
omni::math::calculate_point(start_x, start_y, end_x, end_y, length, out_x, out_y);
}
template < typename T >
inline void extend_line(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, T length, omni::math::dimensional<T, 2>& out)
{
return omni::math::extend_line(start[0], start[1], end[0], end[1], length, out[0], out[1]);
}
inline bool is_nan(long double val)
{
#if defined(OMNI_USE_CXX_MATH)
return std::isnan(val);
#else
#if defined(OMNI_OS_WIN)
#if defined(isnan)
return ::isnan(val) != 0;
#else
return ::isnan-isnan-isnanf?view=vs-2019">_isnan(val) != 0;
#endif
#else
#if defined(isnan)
return ::isnan(val) == 1;
#else
return val != val;
#endif
#endif
#endif
}
inline bool is_nan(double val)
{
#if defined(OMNI_USE_CXX_MATH)
return std::isnan(val);
#else
#if defined(OMNI_OS_WIN)
#if defined(isnan)
return ::isnan(val) != 0;
#else
return ::isnan-isnan-isnanf?view=vs-2019">_isnan(val) != 0;
#endif
#else
#if defined(isnan)
return ::isnan(val) == 1;
#else
return val != val;
#endif
#endif
#endif
}
inline bool is_nan(float val)
{
#if defined(OMNI_USE_CXX_MATH)
return std::isnan(val);
#else
#if defined(OMNI_OS_WIN)
#if defined(isnan)
return ::isnan(val) != 0;
#else
return ::isnan-isnan-isnanf?view=vs-2019">_isnan(val) != 0;
#endif
#else
#if defined(isnan)
return ::isnan(val) == 1;
#else
return val != val;
#endif
#endif
#endif
}
inline bool is_odd(int8_t val) { return (val & 1); }
inline bool is_odd(uint8_t val) { return (val & 1); }
inline bool is_odd(int16_t val) { return (val & 1); }
inline bool is_odd(uint16_t val) { return (val & 1); }
inline bool is_odd(int32_t val) { return (val & 1); }
inline bool is_odd(uint32_t val) { return (val & 1); }
inline bool is_odd(int64_t val) { return (val & 1); }
inline bool is_odd(uint64_t val) { return (val & 1); }
template < typename T >
inline bool is_even(T val)
{
return !omni::math::is_odd(val);
}
template < typename T >
inline double lerp_y(T x1, T y1, T x2, T y2, T x)
{
if (omni::math::are_equal(x2, x1)) {
return static_cast<double>(y1 + y2) / 2;
}
return (((x - x1) * (y2 - y1) / (x2 - x1)) + y1);
}
template < typename T >
inline double linear_interpolation_y(T x1, T y1, T x2, T y2, T x)
{
return omni::math::lerp_y<T>(x1, y1, x2, y2, x);
}
template < typename T >
inline double lerp_y(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, T x)
{
return omni::math::lerp_y(start[0], start[1], end[0], end[1], x);
}
template < typename T >
inline double linear_interpolation_y(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, T x)
{
return omni::math::lerp_y(start[0], start[1], end[0], end[1], x);
}
template < typename T >
inline double lerp_x(T x1, T y1, T x2, T y2, T y)
{
if (omni::math::are_equal(y2, y1)) {
return static_cast<double>(x1 + x2) / 2;
}
return (((y - y1) * (x2 - x1) / (y2 - y1)) + x1);
}
template < typename T >
inline double linear_interpolation_x(T x1, T y1, T x2, T y2, T y)
{
return omni::math::lerp_x<T>(x1, y1, x2, y2, y);
}
template < typename T >
inline double lerp_x(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, T y)
{
return omni::math::lerp_x(start[0], start[1], end[0], end[1], y);
}
template < typename T >
inline double linear_interpolation_x(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, T y)
{
return omni::math::lerp_x(start[0], start[1], end[0], end[1], y);
}
template < typename T >
inline bool lines_intersect(T line1_x1, T line1_y1, T line1_x2, T line1_y2, T line2_x1, T line2_y1, T line2_x2, T line2_y2)
{
T det = ((line1_x1 - line1_x2) * (line2_y1 - line2_y2)) - ((line1_y1 - line1_y2) * (line2_x1 - line2_x2));
if (omni::math::are_equal(det, static_cast<T>(0))) { return false; }
double t = ((line1_x1 - line2_x1) * (line2_y1 - line2_y2)) - ((line1_y1 - line2_y1) * (line2_x1 - line2_x2));
double u = ((line1_x1 - line1_x2) * (line1_y1 - line2_y1)) - ((line1_y1 - line1_y2) * (line1_x1 - line2_x1));
if (((0.0 <= t) && (t <= 1.0)) || ((0.0 <= u) && (u <= 1.0))) { return true; }
double px = ((((line1_x1 * line1_y2) - (line1_y1 * line1_x2)) * (line2_x1 - line2_x2)) - ((line1_x1 - line1_x2) * ((line2_x1 * line2_y2) - (line2_y1 * line2_x2)))) / det;
double py = ((((line1_x1 * line1_y2) - (line1_y1 * line1_x2)) * (line2_y1 - line2_y2)) - ((line1_y1 - line1_y2) * ((line2_x1 * line2_y2) - (line2_y1 * line2_x2)))) / det;
if (omni::math::are_equal(px, 0.0) || omni::math::are_equal(py, 0.0)) { return false; }
return true;
}
template < typename T >
inline bool lines_intersect(const omni::math::dimensional<T, 2>& line1_start, const omni::math::dimensional<T, 2>& line1_end, const omni::math::dimensional<T, 2>& line2_start, const omni::math::dimensional<T, 2>& line2_end)
{
return omni::math::lines_intersect(
line1_start[0], line1_start[1],
line1_end[0], line1_end[1],
line2_start[0], line2_start[1],
line2_end[0], line2_end[1]
);
}
template < typename T >
inline bool lines_intersect(const omni::math::dimensional<T, 4>& line1, const omni::math::dimensional<T, 4>& line2)
{
return omni::math::lines_intersect(
line1[0], line1[1], line1[2], line1[3],
line2[0], line2[1], line2[2], line2[3]
);
}
template < typename T >
inline void midpoint(T start_x, T start_y, T end_x, T end_y, T& mid_x, T& mid_y)
{
mid_x = ((start_x + end_x) / 2);
mid_y = ((start_y + end_y) / 2);
}
template < typename T >
inline void midpoint(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end, omni::math::dimensional<T, 2>& out)
{
omni::math::midpoint(start[0], start[1], end[0], end[1], out[0], out[1]);
}
template < typename T >
inline void midpoint3d(T start_x, T start_y, T start_z, T end_x, T end_y, T end_z, T& mid_x, T& mid_y, T& mid_z)
{
mid_x = ((start_x + end_x) / 2);
mid_y = ((start_y + end_y) / 2);
mid_z = ((start_z + end_z) / 2);
}
template < typename T >
inline void midpoint3d(const omni::math::dimensional<T, 3>& start, const omni::math::dimensional<T, 3>& end, omni::math::dimensional<T, 3>& out)
{
omni::math::midpoint3d(start[0], start[1], start[2], end[0], end[1], end[2], out[0], out[1], out[2]);
}
template < typename T >
inline omni::math::dimensional<T, 2> midpoint(T start_x, T start_y, T end_x, T end_y)
{
omni::math::dimensional<T, 2> ret;
omni::math::midpoint<T>(start_x, start_y, end_x, end_y, ret[0], ret[1]);
return ret;
}
template < typename T >
inline omni::math::dimensional<T, 2> midpoint(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end)
{
omni::math::dimensional<T, 2> ret;
omni::math::midpoint<T>(start[0], start[1], end[0], end[1], ret[0], ret[1]);
return ret;
}
template < typename T >
inline omni::math::dimensional<T, 3> midpoint3d(T start_x, T start_y, T start_z, T end_x, T end_y, T end_z)
{
omni::math::dimensional<T, 3> ret;
omni::math::midpoint3d<T>(start_x, start_y, start_z, end_x, end_y, end_z, ret[0], ret[1], ret[2]);
return ret;
}
template < typename T >
inline omni::math::dimensional<T, 3> midpoint3d(const omni::math::dimensional<T, 3>& start, const omni::math::dimensional<T, 3>& end)
{
omni::math::dimensional<T, 3> ret;
omni::math::midpoint3d<T>(start[0], start[1], start[2], end[0], end[1], end[2], ret[0], ret[1], ret[2]);
return ret;
}
template < typename T >
inline omni::math::ordinal_name octant(T x, T y, T z)
{
// x+ = 001000 = 8, x- = 000100 = 4
// y+ = 000010 = 2, y- = 000001 = 1
// z+ = 100000 = 32, z- = 010000 = 16
switch (
((z > 0) ? (1<<5) : ((z < 0) ? (1<<4) : 0)) |
((x > 0) ? (1<<3) : ((x < 0) ? (1<<2) : 0)) |
((y > 0) ? (1<<1) : ((y < 0) ? 1 : 0))
)
{
case 42: case 10: return omni::math::ordinal_name::I; // +++ = 101010 = 42/10 = quad 1
case 38: case 6: return omni::math::ordinal_name::II; // -++ = 100110 = 38/6 = quad 2
case 37: case 5: return omni::math::ordinal_name::III; // --+ = 100101 = 37/5 = quad 3
case 41: case 9: return omni::math::ordinal_name::IV; // +-+ = 101001 = 41/9 = quad 4
case 26: return omni::math::ordinal_name::V; // ++- = 011010 = 26 = quad 5
case 22: return omni::math::ordinal_name::VI; // -+- = 010110 = 22 = quad 6
case 21: return omni::math::ordinal_name::VII; // --- = 010101 = 21 = quad 7
case 25: return omni::math::ordinal_name::VIII; // +-- = 011001 = 25 = quad 8
// x,0,z
case 40: return omni::math::ordinal_name::I_IV; // +0+ = 101000 = 40 = quads 1 & 4
case 24: return omni::math::ordinal_name::V_VIII; // +0- = 011000 = 24 = quads 5 & 8
case 36: return omni::math::ordinal_name::II_III; // -0+ = 100100 = 36 = quads 2 & 3
case 20: return omni::math::ordinal_name::VI_VII; // -0- = 010100 = 20 = quads 6 & 7
// 0,y,z
case 34: return omni::math::ordinal_name::I_II; // 0++ = 100010 = 34 = quads 1 & 2
case 18: return omni::math::ordinal_name::V_VI; // 0+- = 010010 = 18 = quads 5 & 6
case 33: return omni::math::ordinal_name::III_IV; // 0-+ = 100001 = 33 = quads 3 & 4
case 17: return omni::math::ordinal_name::VII_VIII; // 0-- = 010001 = 17 = quads 7 & 8
// 0,0,0
case 8: case 4: return omni::math::ordinal_name::X_AXIS; // 001000/000100 = 8/4 = X
case 2: case 1: return omni::math::ordinal_name::Y_AXIS; // 000010/000001 = 2/1 = Y
case 32: case 16: return omni::math::ordinal_name::Z_AXIS;// 100000/010000 = 32/16 = Z
default: break;
}
return omni::math::ordinal_name::ORIGIN;
}
template < typename T >
inline omni::math::ordinal_name octant(const omni::math::dimensional<T, 3>& point)
{
return omni::math::octant<T>(point[0], point[1], point[2]);
}
template < typename T >
inline bool point_is_on_circle_edge(T center_x, T center_y, double radius, T x, T y)
{
return omni::math::circle_contains_point_on_edge(center_x, center_y, radius, x, y);
}
template < typename T >
inline bool point_is_on_circle_edge(T center_x, T center_y, double radius, const omni::math::dimensional<T, 2>& point)
{
return omni::math::circle_contains_point_on_edge(center_x, center_y, radius, point[0], point[1]);
}
template < typename T >
inline void point_on_circle(double degrees, double radius, T center_x, T center_y, T& out_x, T& out_y)
{
if ((degrees < 0.0) || (degrees > 360.0)) {
OMNI_ERR_FW("angle must be between 0-360 degrees", omni::exceptions::overflow_error("angle must be between 0-360 degrees"))
}
omni::math::point_on_circle_unsafe(degrees, radius, center_x, center_y, out_x, out_y);
}
template <> inline void point_on_circle<int64_t>(double degrees, double radius, int64_t center_x, int64_t center_y, int64_t& out_x, int64_t& out_y) { OMNI_MATH_GET_POINT_INT_FW(int64_t, out_x, out_y, center_x, center_y, radius, degrees); }
template <> inline void point_on_circle<uint64_t>(double degrees, double radius, uint64_t center_x, uint64_t center_y, uint64_t& out_x, uint64_t& out_y) { OMNI_MATH_GET_POINT_INT_FW(uint64_t, out_x, out_y, center_x, center_y, radius, degrees); }
template <> inline void point_on_circle<int32_t>(double degrees, double radius, int32_t center_x, int32_t center_y, int32_t& out_x, int32_t& out_y) { OMNI_MATH_GET_POINT_INT_FW(int32_t, out_x, out_y, center_x, center_y, radius, degrees); }
template <> inline void point_on_circle<uint32_t>(double degrees, double radius, uint32_t center_x, uint32_t center_y, uint32_t& out_x, uint32_t& out_y) { OMNI_MATH_GET_POINT_INT_FW(uint32_t, out_x, out_y, center_x, center_y, radius, degrees); }
template <> inline void point_on_circle<int16_t>(double degrees, double radius, int16_t center_x, int16_t center_y, int16_t& out_x, int16_t& out_y) { OMNI_MATH_GET_POINT_INT_FW(int16_t, out_x, out_y, center_x, center_y, radius, degrees); }
template <> inline void point_on_circle<uint16_t>(double degrees, double radius, uint16_t center_x, uint16_t center_y, uint16_t& out_x, uint16_t& out_y) { OMNI_MATH_GET_POINT_INT_FW(uint16_t, out_x, out_y, center_x, center_y, radius, degrees); }
template <> inline void point_on_circle<int8_t>(double degrees, double radius, int8_t center_x, int8_t center_y, int8_t& out_x, int8_t& out_y) { out_x = OMNI_MATH_GET_POINT_X_INT_FW(int8_t, center_x, radius, degrees); out_y = OMNI_MATH_GET_POINT_Y_INT_FW(int8_t, center_y, radius, degrees); }
template <> inline void point_on_circle<uint8_t>(double degrees, double radius, uint8_t center_x, uint8_t center_y, uint8_t& out_x, uint8_t& out_y) { out_x = OMNI_MATH_GET_POINT_X_INT_FW(uint8_t, center_x, radius, degrees); out_y = OMNI_MATH_GET_POINT_Y_INT_FW(uint8_t, center_y, radius, degrees); }
template < typename T >
inline void point_on_circle(double degrees, T radius, const omni::math::dimensional<T, 2>& center, omni::math::dimensional<T, 2>& out)
{
return omni::math::point_on_circle(degrees, radius, center[0], center[1], out[0], out[1]);
}
template < typename T >
inline void quadrant(T x, T y, omni::math::ordinal_name::enum_t& quad)
{
// x+ = 1000 = 8, x- = 0100 = 4
// y+ = 0010 = 2, y- = 0001 = 1
switch (
((x > 0) ? (1<<3) : ((x < 0) ? (1<<2) : 0)) |
((y > 0) ? (1<<1) : ((y < 0) ? 1 : 0))
)
{
case 10: quad = omni::math::ordinal_name::I; break; // ++ = 1010 = 10 = quad 1
case 6: quad = omni::math::ordinal_name::II; break; // -+ = 0110 = 6 = quad 2
case 5: quad = omni::math::ordinal_name::III; break; // -- = 0101 = 5 = quad 3
case 9: quad = omni::math::ordinal_name::IV; break; // +- = 1001 = 9 = quad 4
case 8: case 4: quad = omni::math::ordinal_name::X_AXIS; break; // 1000/0100 = 8/4 = X
case 2: case 1: quad = omni::math::ordinal_name::Y_AXIS; break; // 0010/0001 = 2/1 = Y
default: quad = omni::math::ordinal_name::ORIGIN; break;
}
}
template < typename T >
inline omni::math::ordinal_name quadrant(T x, T y)
{
omni::math::ordinal_name::enum_t ret;
omni::math::quadrant<T>(x, y, ret);
return ret;
}
template < typename T >
inline omni::math::ordinal_name quadrant(const omni::math::dimensional<T, 2>& point)
{
return omni::math::quadrant<T>(point[0], point[1]);
}
template < typename T >
inline void quadratic(T a, T b, T c, T& x_plus, T& x_minus)
{
if (omni::math::are_equal(a, T(0))) {
OMNI_ERR_FW("division by zero, a must not be 0", omni::exceptions::invalid_range("division by zero, a must not be 0"))
}
x_plus = ((-b) + std::sqrt(static_cast<double>((b * b) - (4 * a * c)))) / (2 * a);
x_minus = ((-b) - std::sqrt(static_cast<double>((b * b) - (4 * a * c)))) / (2 * a);
}
template < typename T >
inline omni::math::dimensional<T, 2> quadratic(T a, T b, T c)
{
omni::math::dimensional<T, 2> ret;
omni::math::quadratic<T>(a, b, c, ret[0], ret[1]);
return ret;
}
template < typename T >
inline bool quadrilateral_intersects(T ax1, T ay1, T bx1, T by1, T cx1, T cy1, T dx1, T dy1, T ax2, T ay2, T bx2, T by2, T cx2, T cy2, T dx2, T dy2)
{
return omni::math::lines_intersect(ax1, ay1, bx1, by1, ax2, ay2, bx2, by2) ||
omni::math::lines_intersect(ax1, ay1, bx1, by1, bx2, by2, cx2, cy2) ||
omni::math::lines_intersect(ax1, ay1, bx1, by1, cx2, cy2, dx2, dy2) ||
omni::math::lines_intersect(ax1, ay1, bx1, by1, dx2, dy2, ax2, ay2) ||
omni::math::lines_intersect(bx1, by1, cx1, cy1, ax2, ay2, bx2, by2) ||
omni::math::lines_intersect(bx1, by1, cx1, cy1, bx2, by2, cx2, cy2) ||
omni::math::lines_intersect(bx1, by1, cx1, cy1, cx2, cy2, dx2, dy2) ||
omni::math::lines_intersect(bx1, by1, cx1, cy1, dx2, dy2, ax2, ay2) ||
omni::math::lines_intersect(cx1, cy1, dx1, dy1, ax2, ay2, bx2, by2) ||
omni::math::lines_intersect(cx1, cy1, dx1, dy1, bx2, by2, cx2, cy2) ||
omni::math::lines_intersect(cx1, cy1, dx1, dy1, cx2, cy2, dx2, dy2) ||
omni::math::lines_intersect(cx1, cy1, dx1, dy1, dx2, dy2, ax2, ay2) ||
omni::math::lines_intersect(dx1, dy1, ax1, ay1, ax2, ay2, bx2, by2) ||
omni::math::lines_intersect(dx1, dy1, ax1, ay1, bx2, by2, cx2, cy2) ||
omni::math::lines_intersect(dx1, dy1, ax1, ay1, cx2, cy2, dx2, dy2) ||
omni::math::lines_intersect(dx1, dy1, ax1, ay1, dx2, dy2, ax2, ay2);
}
template < typename T >
inline void quadrilateral_reflect(T& ax, T& ay, T& bx, T& by, T& cx, T& cy, T& dx, T& dy)
{
ax = -ax; bx = -bx; cx = -cx; dx = -dx;
ay = -ay; by = -by; cy = -cy; dy = -dy;
}
template < typename T >
inline void quadrilateral_rotate_point(double degrees, T x, T y, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy, T& dx, T& dy)
{
omni::math::rotate_point(degrees, dir, x, y, ax, ay);
omni::math::rotate_point(degrees, dir, x, y, bx, by);
omni::math::rotate_point(degrees, dir, x, y, cx, cy);
omni::math::rotate_point(degrees, dir, x, y, dx, dy);
}
template < typename T >
inline void quadrilateral_rotate_origin(double degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy, T& dx, T& dy)
{
if ((degrees < 0.0) || (degrees > 360.0)) {
OMNI_ERR_FW("angle must be between 0-360 degrees", omni::exceptions::overflow_error("angle must be between 0-360 degrees"))
}
if (omni::math::are_equal(degrees, 0.0) || omni::math::are_equal(degrees, 360.0)) {
return; // no rotation
} else if (omni::math::are_equal(degrees, 180.0)) {
// (x, y) -> (-x, -y)
ax = -ax; bx = -bx; cx = -cx; dx = -dx;
ay = -ay; by = -by; cy = -cy; dy = -dy;
} else {
bool ts = omni::math::are_equal(degrees, 270.0);
bool nt = (!ts && omni::math::are_equal(degrees, 90.0));
bool cw = (dir == omni::math::rotation_direction::CLOCKWISE);
if ((nt && cw) || (ts && !cw)) {
// (x, y) -> (y, -x)
std::swap(ax, ay); ay = -ay;
std::swap(bx, by); by = -by;
std::swap(cx, cy); cy = -cy;
std::swap(dx, dy); dy = -dy;
} else if ((ts && cw) || (nt && !cw)) {
// (x, y) -> (-y, x)
std::swap(ax, ay); ax = -ax;
std::swap(bx, by); bx = -bx;
std::swap(cx, cy); cx = -cx;
std::swap(dx, dy); dx = -dx;
} else {
omni::math::quadrilateral_rotate_point<T>(degrees, 0, 0, dir, ax, ay, bx, by, cx, cy, dx, dy);
}
}
}
template < typename T >
inline void quadrilateral_translate_xy(T x, T y, T& ax, T& ay, T& bx, T& by, T& cx, T& cy, T& dx, T& dy)
{
ax += x; bx += x; cx += x; dx += x;
ay += y; by += y; cy += y; dy += y;
}
template < typename T >
inline void quadrilateral_translate_angle(double degrees, T distance, T& ax, T& ay, T& bx, T& by, T& cx, T& cy, T& dx, T& dy)
{
if ((degrees < 0.0) || (degrees > 360.0)) {
OMNI_ERR_FW("angle must be between 0-360 degrees", omni::exceptions::overflow_error("angle must be between 0-360 degrees"))
}
if (omni::math::are_equal(degrees, 0.0) || omni::math::are_equal(degrees, 360.0)) {
// no rotational translation, just add distance to all the y pairs
omni::math::quadrilateral_translate_xy(static_cast<T>(0), distance, ax, ay, bx, by, cx, cy, dx, dy);
} else {
omni::math::point_on_circle(degrees, distance, ax, ay, ax, ay);
omni::math::point_on_circle(degrees, distance, bx, by, bx, by);
omni::math::point_on_circle(degrees, distance, cx, cy, cx, cy);
omni::math::point_on_circle(degrees, distance, dx, dy, dx, dy);
}
}
inline double rad_to_deg(double rad)
{
return OMNI_RAD_TO_DEG(rad); // degrees = radians * π / 180
}
inline double radians_to_degrees(double rad)
{
return OMNI_RAD_TO_DEG(rad); // degrees = radians * π / 180
}
template < typename T >
inline double radius_from_area(T area)
{
return std::sqrt(static_cast<double>(area) / OMNI_PI);
}
template < typename T >
inline void rectangle_get_centroid(T ax, T ay, T bx, T by, T cx, T cy, T dx, T dy, T& out_x, T& out_y)
{
T w, x, y, z;
omni::math::midpoint(ax, ay, cx, cy, x, y);
omni::math::midpoint(bx, by, dx, dy, w, z);
out_x = (omni::math::are_equal(x, w) ? x : w);
out_y = (omni::math::are_equal(y, z) ? y : z);
}
template < typename T >
inline bool rectangle_xywh_contains_point(T x, T y, T w, T h, T x2, T y2)
{
return OMNI_RECT_XYWH_CONTAINS_FW(x, y, w, h, x2, y2);
}
template < typename T >
inline bool rectangle_xywh_contains_point(T x, T y, T w, T h, const omni::math::dimensional<T, 2>& point)
{
return OMNI_RECT_XYWH_CONTAINS_FW(x, y, w, h, point[0], point[1]);
}
template < typename T >
inline bool rectangle_ltrb_contains_point(T left, T top, T right, T bottom, T x, T y)
{
return OMNI_RECT_LTRB_CONTAINS_FW(left, top, right, bottom, x, y);
}
template < typename T >
inline bool rectangle_ltrb_contains_point(T left, T top, T right, T bottom, const omni::math::dimensional<T, 2>& point)
{
return OMNI_RECT_LTRB_CONTAINS_FW(left, top, right, bottom, point[0], point[1]);
}
template < typename T >
inline void rectangle_reflect(T& x, T& y, T w, T h)
{
x = -x; x -= w;
y = -y; y -= h;
}
template < typename T >
inline void rectangle_translate_xy(T x, T y, T& ax, T& ay)
{
ax += x;
ay += y;
}
template < typename T >
inline void rectangle_translate_angle(double degrees, T distance, T& ax, T& ay)
{
if ((degrees < 0.0) || (degrees > 360.0)) {
OMNI_ERR_FW("angle must be between 0-360 degrees", omni::exceptions::overflow_error("angle must be between 0-360 degrees"))
}
if (omni::math::are_equal(degrees, 0.0) || omni::math::are_equal(degrees, 360.0)) {
// no rotational translation, just add distance to all the x pairs
omni::math::rectangle_translate_xy(static_cast<T>(0), distance, ax, ay);
} else {
omni::math::point_on_circle(static_cast<T>(degrees), distance, ax, ay, ax, ay);
}
}
template < typename T >
inline void rectangle_rotate_point(const omni::math::rotation_angle& degrees, T x, T y, const omni::math::rotation_direction& dir, T& ax, T& ay, T& w, T& h)
{
if (!omni::math::rotation_angle::is_valid(static_cast<int>(degrees.value()))) {
OMNI_ERR_FW("angle must be between 0, 90, 180 or 270 degrees", omni::exceptions::overflow_error("angle must be between 0, 90, 180 or 270 degrees"))
}
if (degrees == omni::math::rotation_angle::ZERO) { return; } // no rotation
double deg = 0; double nx = 0; double ny = 0;
switch (degrees) {
case omni::math::rotation_angle::NINETY:
deg = ((dir == omni::math::rotation_direction::COUNTER_CLOCKWISE) ? 270.0 : 90.0);
std::swap(w, h);
break;
case omni::math::rotation_angle::ONE_EIGHTY: deg = 180.0; break;
case omni::math::rotation_angle::TWO_SEVENTY:
deg = ((dir == omni::math::rotation_direction::COUNTER_CLOCKWISE) ? 90.0 : 270.0);
std::swap(w, h);
break;
case omni::math::rotation_angle::ZERO:
default: break;
}
omni::math::point_on_circle<double>(deg, omni::math::distance_between_2_points<double>(x, y, static_cast<double>(ax), static_cast<double>(ay)), x, y, nx, ny);
ax = static_cast<T>(nx);
ay = static_cast<T>(ny);
}
template < typename T >
inline void rectangle_rotate_origin(const omni::math::rotation_angle& degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& w, T& h)
{
if (!omni::math::rotation_angle::is_valid(static_cast<int>(degrees.value()))) {
OMNI_ERR_FW("angle must be between 0, 90, 180 or 270 degrees", omni::exceptions::overflow_error("angle must be between 0, 90, 180 or 270 degrees"))
}
if (degrees == omni::math::rotation_angle::ZERO) { return; } // no rotation
if (degrees == omni::math::rotation_angle::ONE_EIGHTY) {
// (x, y) -> (-x, -y) (180 about origin)
ax = -ax; ay = -ay;
ax -= w;
} else {
std::swap(w, h);
bool ts = (degrees == omni::math::rotation_angle::TWO_SEVENTY);
bool nt = (!ts && (degrees == omni::math::rotation_angle::NINETY));
bool cw = (dir == omni::math::rotation_direction::CLOCKWISE);
if ((nt && cw) || (ts && !cw)) {
// (x, y) -> (y, -x) (270 about origin)
std::swap(ax, ay);
ay = -ay;
} else if ((ts && cw) || (nt && !cw)) {
// (x, y) -> (-y, x) (90 about origin)
std::swap(ax, ay);
ax = -ax;
ay += h;
} else {
omni::math::rectangle_rotate_point<T>(degrees, 0, 0, dir, ax, ay, w, h);
}
}
}
inline double round(double val, uint8_t digits, const omni::math::midpoint_rounding& direction)
{
if (digits > OMNI_MATH_MAX_ROUND_DIGITS_FW) {
OMNI_ERR_RETV_FW("Invalid range specified for rounding digit", omni::exceptions::invalid_range(), val);
}
if (std::fabs(val) < OMNI_MATH_ROUND_LIMIT_FW) {
double p = std::pow(10.0, static_cast<double>(digits));
val *= p;
double f = std::modf(val , &val);
if ((std::fabs(f) > 0.5) || omni::math::are_equal(std::fabs(f), 0.5)) {
switch (direction) {
case omni::math::midpoint_rounding::AWAY_FROM_ZERO:
//if (static_cast<int64_t>(val) % 2 != 0) {}
val += ((f < 0) ? -1 : 1);
break;
case omni::math::midpoint_rounding::TO_EVEN:
if (static_cast<int64_t>(val) % 2 != 0) {
val += ((f < 0) ? -1 : 1);
}
break;
case omni::math::midpoint_rounding::TO_ZERO:
val += ((f < 0) ? 1 : -1);
break;
case omni::math::midpoint_rounding::TO_NEGATIVE_INFINITY:
val += -1;
break;
case omni::math::midpoint_rounding::TO_POSITIVE_INFINITY:
val += 1;
break;
default: break;
}
}
val /= p;
}
return val;
}
inline double round(double val, uint8_t digits, std::float_round_style direction)
{
switch (direction) {
case std::round_to_nearest: // Rounding toward nearest representable value
return omni::math::round(val, digits, omni::math::midpoint_rounding::TO_EVEN);
case std::round_toward_zero: // Rounding toward zero
return omni::math::round(val, digits, omni::math::midpoint_rounding::TO_ZERO);
case std::round_toward_neg_infinity: // Rounding toward negative infinity
return omni::math::round(val, digits, omni::math::midpoint_rounding::TO_NEGATIVE_INFINITY);
case std::round_toward_infinity: // Rounding toward positive infinity
return omni::math::round(val, digits, omni::math::midpoint_rounding::TO_POSITIVE_INFINITY);
case std::round_indeterminate: // Rounding style cannot be determined
default: break;
}
return omni::math::round(val, digits, omni::math::midpoint_rounding::AWAY_FROM_ZERO);
}
inline double round(double val, uint8_t digits)
{
return omni::math::round(val, digits, omni::math::midpoint_rounding::TO_EVEN);
}
inline double round(double val)
{
return omni::math::round(val, 0, omni::math::midpoint_rounding::TO_EVEN);
}
template < typename T >
inline void rotate_point(double degrees, T center_x, T center_y, T& rotate_x, T& rotate_y)
{
omni::math::rotate_point(degrees, omni::math::rotation_direction::CLOCKWISE, center_x, center_y, rotate_x, rotate_y);
}
template < typename T >
inline void rotate_point(double degrees, T center_x, T center_y, omni::math::dimensional<T, 2>& rotate)
{
omni::math::rotate_point(degrees, omni::math::rotation_direction::CLOCKWISE, center_x, center_y, rotate[0], rotate[1]);
}
template < typename T >
inline void rotate_point(double degrees, const omni::math::dimensional<T, 2>& center, omni::math::dimensional<T, 2>& rotate)
{
omni::math::rotate_point(degrees, omni::math::rotation_direction::CLOCKWISE, center[0], center[1], rotate[0], rotate[1]);
}
template < typename T >
inline T sign(T val)
{
if (omni::math::are_equal(val, T(0))) {
return 0;
}
if (val < 1) {
return -1;
}
return 1;
}
template < typename T >
inline double slope(T start_x, T start_y, T end_x, T end_y)
{
if (!omni::math::are_equal(start_x, end_x)) {
return static_cast<double>(end_y - start_y) / static_cast<double>(end_x - start_x);
}
return 0.0;
}
template < typename T >
inline double slope(const omni::math::dimensional<T, 2>& start, const omni::math::dimensional<T, 2>& end)
{
return omni::math::slope(start[0], start[1], end[0], end[1]);
}
template < typename T >
inline T summation(T index, T end, const omni::delegate1<T, T>& sum)
{
T val;
if (sum) {
val = sum(index);
for (T i = index+1; i <= end; ++i) {
val += sum(i);
}
} else {
val = index;
for (T i = index+1; i <= end; ++i) {
val += i;
}
}
return val;
}
template < typename T >
inline T summation(T index, T end, const omni::delegate1<T, T>& sum, const omni::delegate1<bool, T>& break_cond)
{
T val;
if (sum) {
val = sum(index);
for (T i = index+1; i <= end; ++i) {
val += sum(i);
if (break_cond(val)) { break; }
}
} else {
val = index;
for (T i = index+1; i <= end; ++i) {
val += i;
if (break_cond(val)) { break; }
}
}
return val;
}
template < typename T >
inline bool triangle_contains_point(T x, T y, T ax, T ay, T bx, T by, T cx, T cy)
{
T s = (ay * cx) - (ax * cy) + ((cy - ay) * x) + ((ax - cx) * y);
T t = (ax * by) - (ay * bx) + ((ay - by) * x) + ((bx - ax) * y);
if ((s < 0) != (t < 0)) {
return false;
}
T A = (-by * cx) + (ay * (cx - bx)) + (ax * (by - cy)) + (bx * cy);
return (A < 0) ?
((s <= 0) && (s + t >= A)) :
((s >= 0) && (s + t <= A));
}
template < typename T >
inline bool triangle_intersects(T ax1, T ay1, T bx1, T by1, T cx1, T cy1, T ax2, T ay2, T bx2, T by2, T cx2, T cy2)
{
return omni::math::lines_intersect(ax1, ay1, bx1, by1, ax2, ay2, bx2, by2) || // AB.1 -> AB.2
omni::math::lines_intersect(ax1, ay1, bx1, by1, bx2, by2, cx2, cy2) || // AB.1 -> BC.2
omni::math::lines_intersect(ax1, ay1, bx1, by1, cx2, cy2, ax2, ay2) || // AB.1 -> CA.2
omni::math::lines_intersect(bx1, by1, cx1, cy1, ax2, ay2, bx2, by2) || // BC.1 -> AB.2
omni::math::lines_intersect(bx1, by1, cx1, cy1, bx2, by2, cx2, cy2) || // BC.1 -> BC.2
omni::math::lines_intersect(bx1, by1, cx1, cy1, cx2, cy2, ax2, ay2) || // BC.1 -> CA.2
omni::math::lines_intersect(cx1, cy1, ax1, ay1, ax2, ay2, bx2, by2) || // CA.1 -> AB.2
omni::math::lines_intersect(cx1, cy1, ax1, ay1, bx2, by2, cx2, cy2) || // CA.1 -> BC.2
omni::math::lines_intersect(cx1, cy1, ax1, ay1, cx2, cy2, ax2, ay2); // CA.1 -> CA.2
}
template < typename T >
inline void triangle_get_centroid(T ax, T ay, T bx, T by, T cx, T cy, T& out_x, T& out_y)
{
out_x = static_cast<T>(static_cast<double>(ax + bx + cx) / 3);
out_y = static_cast<T>(static_cast<double>(ay + by + cy) / 3);
}
template < typename T >
inline void triangle_get_incenter(T ax, T ay, T bx, T by, T cx, T cy, T& out_x, T& out_y)
{
double sA = ((bx + by) >= (cx + cy) ? omni::math::distance_between_2_points(bx, by, cx, cy) : omni::math::distance_between_2_points(cx, cy, bx, by));
double sB = ((ax + ay) >= (cx + cy) ? omni::math::distance_between_2_points(ax, ay, cx, cy) : omni::math::distance_between_2_points(cx, cy, ax, ay));
double sC = ((ax + ay) >= (bx + by) ? omni::math::distance_between_2_points(ax, ay, bx, by) : omni::math::distance_between_2_points(bx, by, ax, ay));
double per = sA + sB + sC;
if (omni::math::are_equal(per, 0.0)) { // a 0 perimiter?
out_x = static_cast<T>(static_cast<double>(ax + bx + cx) / 3);
out_y = static_cast<T>(static_cast<double>(ay + by + cy) / 3);
} else {
out_x = static_cast<T>(((sA * ax) + (sB * bx) + (sC * cx)) / per);
out_y = static_cast<T>(((sA * ay) + (sB * by) + (sC * cy)) / per);
}
}
template < typename T >
inline void triangle_get_circumcenter(T ax, T ay, T bx, T by, T cx, T cy, T& out_x, T& out_y)
{
double sinA = std::sin(OMNI_DEG_TO_RAD((2 * omni::math::calculate_angle(ax, ay, bx, by, cx, cy))));
double sinB = std::sin(OMNI_DEG_TO_RAD((2 * omni::math::calculate_angle(bx, by, ax, ay, cx, cy))));
double sinC = std::sin(OMNI_DEG_TO_RAD((2 * omni::math::calculate_angle(cx, cy, bx, by, ax, ay))));
double per = sinA + sinB + sinC;
if (omni::math::are_equal(per, 0.0)) { // a 0 perimiter?
out_x = static_cast<T>(static_cast<double>(ax + bx + cx) / 3);
out_y = static_cast<T>(static_cast<double>(ay + by + cy) / 3);
} else {
out_x = static_cast<T>(((ax * sinA) + (bx * sinB) + (cx * sinC)) / per);
out_y = static_cast<T>(((ay * sinA) + (by * sinB) + (cy * sinC)) / per);
}
}
template < typename T >
inline void triangle_reflect(T& ax, T& ay, T& bx, T& by, T& cx, T& cy)
{
ax = -ax; bx = -bx; cx = -cx;
ay = -ay; by = -by; cy = -cy;
}
template < typename T >
inline void triangle_rotate_point(double degrees, T x, T y, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy)
{
omni::math::rotate_point(degrees, dir, x, y, ax, ay);
omni::math::rotate_point(degrees, dir, x, y, bx, by);
omni::math::rotate_point(degrees, dir, x, y, cx, cy);
}
template < typename T >
inline void triangle_rotate_origin(double degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy)
{
if ((degrees < 0.0) || (degrees > 360.0)) {
OMNI_ERR_FW("angle must be between 0-360 degrees", omni::exceptions::overflow_error("angle must be between 0-360 degrees"))
}
if (omni::math::are_equal(degrees, 0.0) || omni::math::are_equal(degrees, 360.0)) {
return; // no rotation
} else if (omni::math::are_equal(degrees, 180.0)) {
// (x, y) -> (-x, -y)
ax = -ax; bx = -bx; cx = -cx;
ay = -ay; by = -by; cy = -cy;
} else {
bool ts = omni::math::are_equal(degrees, 270.0);
bool nt = (!ts && omni::math::are_equal(degrees, 90.0));
bool cw = (dir == omni::math::rotation_direction::CLOCKWISE);
if ((nt && cw) || (ts && !cw)) {
// (x, y) -> (y, -x)
std::swap(ax, ay); ay = -ay;
std::swap(bx, by); by = -by;
std::swap(cx, cy); cy = -cy;
} else if ((ts && cw) || (nt && !cw)) {
// (x, y) -> (-y, x)
std::swap(ax, ay); ax = -ax;
std::swap(bx, by); bx = -bx;
std::swap(cx, cy); cx = -cx;
} else {
omni::math::triangle_rotate_point<T>(degrees, 0, 0, dir, ax, ay, bx, by, cx, cy);
}
}
}
template < typename T >
inline void triangle_rotate_centroid(double degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy)
{
T ox, oy;
omni::math::triangle_get_centroid(ax, ay, bx, by, cx, cy, ox, oy);
omni::math::triangle_rotate_point<T>(degrees, ox, oy, dir, ax, ay, bx, by, cx, cy);
}
template < typename T >
inline void triangle_rotate_circumcenter(double degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy)
{
T ox, oy;
omni::math::triangle_get_circumcenter(ax, ay, bx, by, cx, cy, ox, oy);
omni::math::triangle_rotate_point<T>(degrees, ox, oy, dir, ax, ay, bx, by, cx, cy);
}
template < typename T >
inline void triangle_rotate_incenter(double degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy)
{
T ox, oy;
omni::math::triangle_get_incenter(ax, ay, bx, by, cx, cy, ox, oy);
omni::math::triangle_rotate_point<T>(degrees, ox, oy, dir, ax, ay, bx, by, cx, cy);
}
template < typename T >
inline void triangle_rotate_abc(double degrees, T x1, T y1, const omni::math::rotation_direction& dir, T& x2, T& y2, T& x3, T& y3)
{
omni::math::triangle_rotate_point<T>(degrees, x1, y1, dir, x1, y1, x2, y2, x3, y3);
}
template < typename T >
inline void triangle_translate_xy(T x, T y, T& ax, T& ay, T& bx, T& by, T& cx, T& cy)
{
ax += x; bx += x; cx += x;
ay += y; by += y; cy += y;
}
template < typename T >
inline void triangle_translate_angle(double degrees, T distance, T& ax, T& ay, T& bx, T& by, T& cx, T& cy)
{
if ((degrees < 0.0) || (degrees > 360.0)) {
OMNI_ERR_FW("angle must be between 0-360 degrees", omni::exceptions::overflow_error("angle must be between 0-360 degrees"))
}
if (omni::math::are_equal(degrees, 0.0) || omni::math::are_equal(degrees, 360.0)) {
// no rotational translation, just add distance to all the y pairs
omni::math::triangle_translate_xy(static_cast<T>(0), distance, ax, ay, bx, by, cx, cy);
} else {
omni::math::point_on_circle(degrees, distance, ax, ay, ax, ay);
omni::math::point_on_circle(degrees, distance, bx, by, bx, by);
omni::math::point_on_circle(degrees, distance, cx, cy, cx, cy);
}
}
inline double truncate(double val)
{
#if defined(OMNI_USE_CXX_MATH)
return std::trunc(val);
#else
std::modf(val, &val);
return val;
#endif
}
inline double trunc(double val)
{
return omni::math::truncate(val);
}
inline double integral_part(double val)
{
return omni::math::truncate(val);
}
inline double fractional_part(double val)
{
return std::modf(val, &val);
}
template < typename T >
inline double area_of_quadrilateral(T ax, T ay, T bx, T by, T cx, T cy, T dx, T dy)
{
return omni::math::area_of_quadrilateral(
(((ax >= bx) && (ay >= by)) ?
(OMNI_DISTANCE_2POINTS_2D_FW(ax, ay, bx, by)) :
(OMNI_DISTANCE_2POINTS_2D_FW(bx, by, ax, ay))),
(((bx >= cx) && (by >= cy)) ?
(OMNI_DISTANCE_2POINTS_2D_FW(bx, by, cx, cy)) :
(OMNI_DISTANCE_2POINTS_2D_FW(cx, cy, bx, by))),
(((cx >= dx) && (cy >= dy)) ?
(OMNI_DISTANCE_2POINTS_2D_FW(cx, cy, dx, dy)) :
(OMNI_DISTANCE_2POINTS_2D_FW(dx, dy, cx, cy))),
(((dx >= ax) && (dy >= ay)) ?
(OMNI_DISTANCE_2POINTS_2D_FW(dx, dy, ax, ay)) :
(OMNI_DISTANCE_2POINTS_2D_FW(ax, ay, dx, dy))),
omni::math::calculate_angle(ax, ay, bx, by, cx, cy),
omni::math::calculate_angle(cx, cy, dx, dy, ax, ay)
);
}
static double ieee_remainder(double x, double y)
{
if (omni::math::is_nan(x)) {
return x; // IEEE 754-2008: NaN payload must be preserved
}
if (omni::math::is_nan(y)) {
return y; // IEEE 754-2008: NaN payload must be preserved
}
if (omni::math::are_equal(y, 0.0)) {
if (x < 0.0000000000000000000) {
return -0.0;
}
return 0.0;
}
double divres = x / y;
double ares = 0;
double rmod = std::modf(divres, &ares);
if (omni::math::is_nan(rmod)) {
return OMNI_NAN;
}
if (omni::math::are_equal(rmod, 0.0)) {
if (x < 0.0000000000000000000) {
return -0.0;
}
}
ares = rmod - (std::fabs(y) * omni::math::sign(x));
if (omni::math::are_equal(std::fabs(ares), std::fabs(rmod))) {
if (std::fabs(omni::math::round(divres)) > std::fabs(divres)) {
return ares;
} else {
return rmod;
}
}
if (std::fabs(ares) < std::fabs(rmod)) {
return ares;
}
return rmod;
}
template < typename T >
inline bool quadrilateral_contains_point(T x, T y, T ax, T ay, T bx, T by, T cx, T cy, T dx, T dy)
{
return omni::math::triangle_contains_point(x, y, ax, ay, bx, by, cx, cy) ||
omni::math::triangle_contains_point(x, y, cx, cy, dx, dy, ax, ay);
}
template < typename T >
inline void quadrilateral_get_centroid(T ax, T ay, T bx, T by, T cx, T cy, T dx, T dy, T& out_x, T& out_y)
{
T x1, y1, x2, y2, x3, y3, x4, y4;
omni::math::triangle_get_centroid(ax, ay, bx, by, cx, cy, x1, y1); // ABC
omni::math::triangle_get_centroid(bx, by, cx, cy, dx, dy, x2, y2); // BCD
omni::math::triangle_get_centroid(ax, ay, cx, cy, dx, dy, x3, y3); // ACD
omni::math::triangle_get_centroid(ax, ay, bx, by, dx, dy, x4, y4); // ABD
omni::math::rectangle_get_centroid(x1, y1, x2, y2, x3, y3, x4, y4, out_x, out_y);
}
template < typename T >
inline void quadrilateral_get_circumcenter(T ax, T ay, T bx, T by, T cx, T cy, T dx, T dy, T& out_x, T& out_y)
{
omni::math::quadrilateral_get_centroid(ax, ay, bx, by, cx, cy, dx, dy, out_x, out_y);
}
template < typename T >
inline void quadrilateral_get_incenter(T ax, T ay, T bx, T by, T cx, T cy, T dx, T dy, T& out_x, T& out_y)
{
omni::math::quadrilateral_get_centroid(ax, ay, bx, by, cx, cy, dx, dy, out_x, out_y);
}
template < typename T >
inline void quadrilateral_rotate_centroid(double degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy, T& dx, T& dy)
{
T ox, oy;
omni::math::quadrilateral_get_centroid(ax, ay, bx, by, cx, cy, dx, dy, ox, oy);
omni::math::quadrilateral_rotate_point<T>(degrees, ox, oy, dir, ax, ay, bx, by, cx, cy, dx, dy);
}
template < typename T >
inline void quadrilateral_rotate_circumcenter(double degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy, T& dx, T& dy)
{
T ox, oy;
omni::math::quadrilateral_get_circumcenter(ax, ay, bx, by, cx, cy, dx, dy, ox, oy);
omni::math::quadrilateral_rotate_point<T>(degrees, ox, oy, dir, ax, ay, bx, by, cx, cy, dx, dy);
}
template < typename T >
inline void quadrilateral_rotate_incenter(double degrees, const omni::math::rotation_direction& dir, T& ax, T& ay, T& bx, T& by, T& cx, T& cy, T& dx, T& dy)
{
T ox, oy;
omni::math::quadrilateral_get_incenter(ax, ay, bx, by, cx, cy, dx, dy, ox, oy);
omni::math::quadrilateral_rotate_point<T>(degrees, ox, oy, dir, ax, ay, bx, by, cx, cy, dx, dy);
}
template < typename T >
inline void quadrilateral_rotate_abcd(double degrees, T x1, T y1, const omni::math::rotation_direction& dir, T& x2, T& y2, T& x3, T& y3, T& x4, T& y4)
{
omni::math::quadrilateral_rotate_point<T>(degrees, x1, y1, dir, x1, y1, x2, y2, x3, y3, x4, y4);
}
}
}
#endif // OMNI_MATH_HPP