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