diff --git a/getchar.py b/getchar.py index c9850ab2..27df1630 100755 --- a/getchar.py +++ b/getchar.py @@ -13,7 +13,7 @@ SVG_DIR = 'derived' TRANSFORM = 'scale({0:.2g}, -{0:0.2g}) translate(0, -900)'.format(SCALE) # Constants controlling our stroke extraction algorithm. -MAX_CROSSING_DISTANCE = 64 +MAX_CROSSING_DISTANCE = 128 MAX_CUSP_MERGE_DISTANCE = 15 MIN_CUSP_ANGLE = 0.1*math.pi @@ -33,7 +33,7 @@ class Cusp(object): return False if other.index[0] == self.index[0]: return self._try_connect(other) - return self._try_connect(other) or self._try_connect(other, True) + return max(self._try_connect(other), self._try_connect(other, True)) def merge(self, other): # Returns true if this cusp point is close enough to the next one that @@ -82,7 +82,7 @@ class Cusp(object): return True diff = other.point - self.point length = abs(diff) - if length > 2*MAX_CROSSING_DISTANCE: + if length > MAX_CROSSING_DISTANCE: return False (other1, other2) = (other.tangent1, other.tangent2) if reverse: @@ -96,9 +96,20 @@ class Cusp(object): ) # TODO(skishore): Replace this set of inequalities with a machine-learned # classifier such as a neural net. - result = (features[2]*features[3] > 0 and - 2*(abs(features[0]) + abs(features[1])) + abs(abs(features[2]) + abs(features[3]) - math.pi) < 0.4*MAX_CROSSING_DISTANCE*math.pi/length) - print (self.point, other.point, features, int(result)) + alignment = abs(features[0]) + abs(features[1]) + incidence = abs(abs(features[2]) + abs(features[3]) - math.pi) + short = length < MAX_CROSSING_DISTANCE/2 + clean = alignment < 0.1*math.pi or alignment + incidence < 0.2*math.pi + cross = all([ + features[0]*features[1] > 0, + features[0]*features[2] < 0, + alignment < math.pi, + abs(features[2]) + abs(features[3]) > math.pi, + ]) + result = 0 + if features[2]*features[3] > 0 and (clean or (short and cross)): + result = (1 if short else 0.75) if clean else 0.5 + print (self.point, other.point, features, result) return result @@ -111,21 +122,21 @@ def augment_glyph(glyph): assert path, 'Got empty path for glyph:\n{0}'.format(glyph) paths = break_path(path) cusps = get_cusps(paths) + edges = get_edges(cusps) # Actually augment the glyph with stroke-aligned cuts. result = [] - for cusp in cusps: + for cusp in cusps.itervalues(): result.append( ''.format( int(cusp.point.real), int(cusp.point.imag), cusp.angle)) - for cusp in cusps: - for other in cusps: - if cusp.connect(other): - result.append( - ''.format( - int(cusp.point.real), int(cusp.point.imag), - int(other.point.real), int(other.point.imag), - 'stroke:white;stroke-width:8')) + for (index1, index2) in edges: + if index1 < index2: + result.append( + ''.format( + int(cusps[index1].point.real), int(cusps[index1].point.imag), + int(cusps[index2].point.real), int(cusps[index2].point.imag), + 'stroke:white;stroke-width:8')) return result def break_path(path): @@ -137,7 +148,7 @@ def break_path(path): return [svg.path.Path(*subpath) for subpath in subpaths] def get_cusps(paths): - result = [] + result = {} for i, path in enumerate(paths): cusps = [] for j, element in enumerate(path): @@ -150,7 +161,26 @@ def get_cusps(paths): cusps.pop(j) else: j += 1 - result.extend(cusps) + for cusp in cusps: + result[cusp.index] = cusp + return result + +def get_edges(cusps): + edges = [] + for cusp in cusps.itervalues(): + for other in cusps.itervalues(): + confidence = cusp.connect(other) + if confidence > 0: + edges.append((confidence, cusp.index, other.index)) + edges.sort(reverse=True) + result = set() + for (confidence, index1, index2) in edges: + other1 = set(b for (a, b) in result if a == index1) + other2 = set(b for (a, b) in result if a == index2) + if other1.intersection(other2): + continue + result.add((index1, index2)) + result.add((index2, index1)) return result def get_svg_path_data(glyph):