Compose *self* with the inverse of *other*, cancelling identical terms if any:: # In general: A - B == A + B.inverted() # (but see note regarding frozen transforms below). # If A "ends with" B (i.e. A == A' + B for some A') we can ca
(self, other)
| 1473 | return self.contains_branch_separately(other_transform) |
| 1474 | |
| 1475 | def __sub__(self, other): |
| 1476 | """ |
| 1477 | Compose *self* with the inverse of *other*, cancelling identical terms |
| 1478 | if any:: |
| 1479 | |
| 1480 | # In general: |
| 1481 | A - B == A + B.inverted() |
| 1482 | # (but see note regarding frozen transforms below). |
| 1483 | |
| 1484 | # If A "ends with" B (i.e. A == A' + B for some A') we can cancel |
| 1485 | # out B: |
| 1486 | (A' + B) - B == A' |
| 1487 | |
| 1488 | # Likewise, if B "starts with" A (B = A + B'), we can cancel out A: |
| 1489 | A - (A + B') == B'.inverted() == B'^-1 |
| 1490 | |
| 1491 | Cancellation (rather than naively returning ``A + B.inverted()``) is |
| 1492 | important for multiple reasons: |
| 1493 | |
| 1494 | - It avoids floating-point inaccuracies when computing the inverse of |
| 1495 | B: ``B - B`` is guaranteed to cancel out exactly (resulting in the |
| 1496 | identity transform), whereas ``B + B.inverted()`` may differ by a |
| 1497 | small epsilon. |
| 1498 | - ``B.inverted()`` always returns a frozen transform: if one computes |
| 1499 | ``A + B + B.inverted()`` and later mutates ``B``, then |
| 1500 | ``B.inverted()`` won't be updated and the last two terms won't cancel |
| 1501 | out anymore; on the other hand, ``A + B - B`` will always be equal to |
| 1502 | ``A`` even if ``B`` is mutated. |
| 1503 | """ |
| 1504 | # we only know how to do this operation if other is a Transform. |
| 1505 | if not isinstance(other, Transform): |
| 1506 | return NotImplemented |
| 1507 | for remainder, sub_tree in self._iter_break_from_left_to_right(): |
| 1508 | if sub_tree == other: |
| 1509 | return remainder |
| 1510 | for remainder, sub_tree in other._iter_break_from_left_to_right(): |
| 1511 | if sub_tree == self: |
| 1512 | if not remainder.has_inverse: |
| 1513 | raise ValueError( |
| 1514 | "The shortcut cannot be computed since 'other' " |
| 1515 | "includes a non-invertible component") |
| 1516 | return remainder.inverted() |
| 1517 | # if we have got this far, then there was no shortcut possible |
| 1518 | if other.has_inverse: |
| 1519 | return self + other.inverted() |
| 1520 | else: |
| 1521 | raise ValueError('It is not possible to compute transA - transB ' |
| 1522 | 'since transB cannot be inverted and there is no ' |
| 1523 | 'shortcut possible.') |
| 1524 | |
| 1525 | def __array__(self, *args, **kwargs): |
| 1526 | """Array interface to get at this Transform's affine matrix.""" |
nothing calls this directly
no test coverage detected