123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- from .interpolatableHelpers import *
- def test_starting_point(glyph0, glyph1, ix, tolerance, matching):
- if matching is None:
- matching = list(range(len(glyph0.isomorphisms)))
- contour0 = glyph0.isomorphisms[ix]
- contour1 = glyph1.isomorphisms[matching[ix]]
- m0Vectors = glyph0.greenVectors
- m1Vectors = [glyph1.greenVectors[i] for i in matching]
- c0 = contour0[0]
- # Next few lines duplicated below.
- costs = [vdiff_hypot2_complex(c0[0], c1[0]) for c1 in contour1]
- min_cost_idx, min_cost = min(enumerate(costs), key=lambda x: x[1])
- first_cost = costs[0]
- proposed_point = contour1[min_cost_idx][1]
- reverse = contour1[min_cost_idx][2]
- if min_cost < first_cost * tolerance:
- # c0 is the first isomorphism of the m0 master
- # contour1 is list of all isomorphisms of the m1 master
- #
- # If the two shapes are both circle-ish and slightly
- # rotated, we detect wrong start point. This is for
- # example the case hundreds of times in
- # RobotoSerif-Italic[GRAD,opsz,wdth,wght].ttf
- #
- # If the proposed point is only one off from the first
- # point (and not reversed), try harder:
- #
- # Find the major eigenvector of the covariance matrix,
- # and rotate the contours by that angle. Then find the
- # closest point again. If it matches this time, let it
- # pass.
- num_points = len(glyph1.points[ix])
- leeway = 3
- if not reverse and (
- proposed_point <= leeway or proposed_point >= num_points - leeway
- ):
- # Try harder
- # Recover the covariance matrix from the GreenVectors.
- # This is a 2x2 matrix.
- transforms = []
- for vector in (m0Vectors[ix], m1Vectors[ix]):
- meanX = vector[1]
- meanY = vector[2]
- stddevX = vector[3] * 0.5
- stddevY = vector[4] * 0.5
- correlation = vector[5]
- if correlation:
- correlation /= abs(vector[0])
- # https://cookierobotics.com/007/
- a = stddevX * stddevX # VarianceX
- c = stddevY * stddevY # VarianceY
- b = correlation * stddevX * stddevY # Covariance
- delta = (((a - c) * 0.5) ** 2 + b * b) ** 0.5
- lambda1 = (a + c) * 0.5 + delta # Major eigenvalue
- lambda2 = (a + c) * 0.5 - delta # Minor eigenvalue
- theta = atan2(lambda1 - a, b) if b != 0 else (pi * 0.5 if a < c else 0)
- trans = Transform()
- # Don't translate here. We are working on the complex-vector
- # that includes more than just the points. It's horrible what
- # we are doing anyway...
- # trans = trans.translate(meanX, meanY)
- trans = trans.rotate(theta)
- trans = trans.scale(sqrt(lambda1), sqrt(lambda2))
- transforms.append(trans)
- trans = transforms[0]
- new_c0 = (
- [complex(*trans.transformPoint((pt.real, pt.imag))) for pt in c0[0]],
- ) + c0[1:]
- trans = transforms[1]
- new_contour1 = []
- for c1 in contour1:
- new_c1 = (
- [
- complex(*trans.transformPoint((pt.real, pt.imag)))
- for pt in c1[0]
- ],
- ) + c1[1:]
- new_contour1.append(new_c1)
- # Next few lines duplicate from above.
- costs = [
- vdiff_hypot2_complex(new_c0[0], new_c1[0]) for new_c1 in new_contour1
- ]
- min_cost_idx, min_cost = min(enumerate(costs), key=lambda x: x[1])
- first_cost = costs[0]
- if min_cost < first_cost * tolerance:
- # Don't report this
- # min_cost = first_cost
- # reverse = False
- # proposed_point = 0 # new_contour1[min_cost_idx][1]
- pass
- this_tolerance = min_cost / first_cost if first_cost else 1
- log.debug(
- "test-starting-point: tolerance %g",
- this_tolerance,
- )
- return this_tolerance, proposed_point, reverse
|