1""" 2Contributed by Frederik Berlaen. 3""" 4 5from math import sqrt, atan2 6 7def _distance(coordinates1, coordinates2): 8 (x1, y1) = coordinates1 9 (x2, y2) = coordinates2 10 return sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) 11 12def joinSegments(onCoords1, offCoords1, offCoords2, onCoords2, offCoords3, offCoords4, onCoords3): 13 """ 14 >>> joinSegments( 15 ... (0, 0), 16 ... (0, 138), (112, 250), (250, 250), 17 ... (250, 388), (500, 138), (500, 0) 18 ... ) 19 ((0.0, 276.0), (500.0, 276.0), (500, 0)) 20 """ 21 (on1X, on1Y) = onCoords1 22 (off1X, off1Y) = offCoords1 23 (off2X, off2Y) = offCoords2 24 (on2X, on2Y) = onCoords2 25 (off3X, off3Y) = offCoords3 26 (off4X, off4Y) = offCoords4 27 (on3X, on3Y) = onCoords3 28 if (on1X, on1Y) == (off1X, off1Y) and (off2X, off2Y) == (on2X, on2Y) == (off3X, off3Y) and (off4X, off4Y) == (on3X, on3Y): 29 ## a two line segments 30 return (on1X, on1Y), (off4X, off4Y), (on3X, on3Y) 31 if (on1X, on1Y) == (off1X, off1Y) and (off2X, off2Y) == (on2X, on2Y): 32 ## first is a line segement 33 d1 = _distance((on1X, on1Y), (off2X, off2Y)) 34 d2 = d1 + _distance((on2X, on2Y), (off3X, off3Y)) 35 if d1 == 0: 36 x, y = off3X, off3Y 37 else: 38 factor = d2 / d1 39 x = on1X + (off2X - on1X) * factor 40 y = on1Y + (off2Y - on1Y) * factor 41 return (x, y), (off4X, off4Y), (on3X, on3Y) 42 43 if (on2X, on2Y) == (off3X, off3Y) and (off4X, off4Y) == (on3X, on3Y): 44 ## last is a line segment 45 d1 = _distance((on3X, on3Y), (off3X, off3Y)) 46 d2 = d1 + _distance((on2X, on2Y), (off2X, off2Y)) 47 if d1 == 0: 48 x, y = off2X, off2Y 49 else: 50 factor = d2 / d1 51 x = on3X + (off3X - on3X) * factor 52 y = on3Y + (off3Y - on3Y) * factor 53 return (off1X, off1Y), (x, y), (on3X, on3Y) 54 55 if (off2X, off2Y) == (on2X, on2Y) == (off3X, off3Y) or (off2X, off2Y) == (on2X, on2Y) or (on2X, on2Y) == (off3X, off3Y): 56 ## one or more bcps are on the joined point 57 return (off1X, off1Y), (off4X, off4Y), (on3X, on3Y) 58 59 if (on1X, on1Y) == (off1X, off1Y): 60 off1X = off1X + (off2X - off1X) * .1 61 off1Y = off1Y + (off2Y - off1Y) * .1 62 63 if (on3X, on3Y) == (off4X, off4Y): 64 off4X = off4X + (off3X - off4X) * .1 65 off4Y = off4Y + (off3Y - off4Y) * .1 66 67 smooth = False 68 if (off2X, off2Y) != (on2X, on2Y) and (off3X, off3Y) != (on2X, on2Y): 69 dx1, dy1 = on2X - off2X, on2Y - off2Y 70 dx2, dy2 = off3X - on2X, off3Y - on2Y 71 a1 = atan2(dx1, dy1) 72 a2 = atan2(dx2, dy2) 73 if abs(a1 - a2) < 0.05: 74 smooth = True 75 76 # first calculate an aproximaly t 77 d1 = _distance((on2X, on2Y), (off2X, off2Y)) 78 d2 = d1 + _distance((off3X, off3Y), (on2X, on2Y)) 79 80 if d2 == 0: 81 t = 0 82 else: 83 t = d1 / d2 84 85 # cut of the extreme t values 86 error = .15 87 if smooth: 88 error = 0 89 if t < error: 90 t = error 91 elif t > 1 - error: 92 t = 1 - error 93 94 # just multiply the first handle of the first curve by t 95 p2X = on1X + (off1X - on1X) * (1 / t) 96 p2Y = on1Y + (off1Y - on1Y) * (1 / t) 97 # and the last handle of the last curve by t 98 99 p3X = on3X + (off4X - on3X) * (1 / (1 - t)) 100 p3Y = on3Y + (off4Y - on3Y) * (1 / (1 - t)) 101 102 return (p2X, p2Y), (p3X, p3Y), (on3X, on3Y) 103 104if __name__ == "__main__": 105 import doctest 106 doctest.testmod() 107