Beispiel #1
0
        def loop_line(self, base, step, nowsector_names, stablepoints):
            global_time_b = None
            base_new = len(stablepoints)
            sector_name = none
            sign = step / abs(step)
            x, y = 0,0

            for i in range(len(nowsector_names)):
                l2 = cp.line(stablepoints[base].x.stablepoints[base].y.stablepoints[base+step].x.stablepoints[base+step].y)
                time_a = (stablepoints[base + step].timevalue) - (stablepoints[base].timevalue)
                polygon = self.sectorinfo[nowsector_names[i]]['geometry'].exterior.coords[:]

                x,y, time_b = cp.crosspoint(polygon, l2, time_a)
                global_time_t = stablepoints[base].timevalue + time_b

                for j in range(len(stablepoints)):
                    read_point = stablepoints[j]
                    base_temp = j
                    if real_point.timevalue > global_time_t:
                        break
                predict_line = cp.line(stablepoints[base].x, stablepoints[base].y, x, y)
                distance_now = predict_line.length()

                distance_min = 1e+10
                if distance_min > distance_now:
                    px = x
                    py = y
                    distance_min = distance_now
                    base_new = base_temp
                    global_time_b = global_time_t
                    sector_name =nowsector_names[i]

            return base_new, px, py, global_time_b, sector_name
Beispiel #2
0
 # vertex of n, an intersection will look very
 # likely but must actually be ignored.
 if vl1.face != placement.face and \
 placement.vpos.has_key(e1[0]): break
 if vl1.face != placement.face and \
 placement.vpos.has_key(e1[1]): break
 for e2 in faceedges[vl2.face]:
     if vl2.face != placement.face and \
     placement.vpos.has_key(e2[0]): break
     if vl2.face != placement.face and \
     placement.vpos.has_key(e2[1]): break
     xa1, ya1 = vl1.vpos[e1[0]]
     xa2, ya2 = vl1.vpos[e1[1]]
     xb1, yb1 = vl2.vpos[e2[0]]
     xb2, yb2 = vl2.vpos[e2[1]]
     ret = crosspoint(xa1,ya1,xa2,ya2,xb1,yb1,xb2,yb2)
     if ret == None: continue
     x, y = ret
     dxa, dya = xa2-xa1, ya2-ya1
     dxb, dyb = xb2-xb1, yb2-yb1
     # See if the crossing point is between the
     # ends of each line. This will be true if
     # the dot product (x-xa1,y-ya1).(dxa,dya)
     # divided by the squared length
     # (dxa,dya).(dxa,dya) is strictly between 0
     # and 1. Likewise for b1/b2.
     dp = ((x-xa1)*dxa+(y-ya1)*dya) / (dxa**2 + dya**2)
     if dp < 0 or dp > 1: continue
     dp = ((x-xb1)*dxb+(y-yb1)*dyb) / (dxb**2 + dyb**2)
     if dp < 0 or dp > 1: continue
     # We have an intersection.
Beispiel #3
0
    def set_params(self):
        x1, y1, dx1, dy1, x2, y2, dx2, dy2, mx = self.inparams
        try:
            # Normalise the direction vectors.
            dlen1 = sqrt(dx1 ** 2 + dy1 ** 2)
            dx1, dy1 = dx1 / dlen1, dy1 / dlen1
            dlen2 = sqrt(dx2 ** 2 + dy2 ** 2)
            dx2, dy2 = dx2 / dlen2, dy2 / dlen2

            self.inparams = (x1, y1, dx1, dy1, x2, y2, dx2, dy2, mx)

            # Transform into the squashed coordinate system.
            if mx != None:
                x1, y1 = squash(x1, y1, mx)
                dx1, dy1 = squash(dx1, dy1, mx)
                x2, y2 = squash(x2, y2, mx)
                dx2, dy2 = squash(dx2, dy2, mx)
                # And renormalise.
                dlen1 = sqrt(dx1 ** 2 + dy1 ** 2)
                dx1, dy1 = dx1 / dlen1, dy1 / dlen1
                dlen2 = sqrt(dx2 ** 2 + dy2 ** 2)
                dx2, dy2 = dx2 / dlen2, dy2 / dlen2

            # Find the normal vectors at each end by rotating the
            # direction vectors.
            nx1, ny1 = dy1, -dx1
            nx2, ny2 = dy2, -dx2

            # Find the crossing point of the normals.
            cx, cy = crosspoint(x1, y1, x1 + nx1, y1 + ny1, x2, y2, x2 + nx2, y2 + ny2)

            # Measure the distance from that crossing point to each
            # endpoint, and find the difference.
            #
            # The distance is obtained by taking the dot product with
            # the line's defining vector, so that it's signed.
            d1 = (cx - x1) * nx1 + (cy - y1) * ny1
            d2 = (cx - x2) * nx2 + (cy - y2) * ny2

            dd = d2 - d1

            # Find the angle between the two direction vectors. Since
            # they're already normalised to unit length, the magnitude
            # of this is just the inverse cosine of their dot product.
            # The sign must be chosen to reflect which way round they
            # are.
            theta = -acos(dx1 * dx2 + dy1 * dy2)
            if dx1 * dy2 - dx2 * dy1 > 0:
                theta = -theta

            # So we need a circular arc rotating through angle theta,
            # such that taking the involute of that arc with the right
            # length does the right thing.
            #
            # Suppose the circle has radius r. Then, when the circle
            # touches the line going to point 1, we need our string to
            # have length equal to d1 - r tan(theta/2). When it touches
            # the line going to point 2, the string length needs to be
            # d2 + r tan(theta/2). The difference between these numbers
            # must be equal to the arc length of the portion of the
            # circle in between, which is r*theta. Setting these equal
            # gives d2-d1 = r theta - 2 r tan(theta/2), which we solve
            # to get r = (d2-d1) / (theta - 2 tan (theta/2)).
            #
            # (In fact, we then flip the sign to take account of the way
            # we subsequently use r.)
            r = dd / (-theta + 2 * tan(theta / 2))

            # So how do we find the centre of a circle of radius r
            # tangent to both those lines? We shift the start point of
            # each line by r in the appropriate direction, and find
            # their crossing point again.
            cx2, cy2 = crosspoint(
                x1 - r * dx1,
                y1 - r * dy1,
                x1 - r * dx1 + nx1,
                y1 - r * dy1 + ny1,
                x2 - r * dx2,
                y2 - r * dy2,
                x2 - r * dx2 + nx2,
                y2 - r * dy2 + ny2,
            )

            # Now find the distance along each line to the centre of the
            # circle, which will be the string lengths at the endpoints.
            s1 = (cx2 - x1) * nx1 + (cy2 - y1) * ny1
            s2 = (cx2 - x2) * nx2 + (cy2 - y2) * ny2

            # Determine the starting angle.
            phi = atan2(dy1, dx1)

            # And that's it. We're involving a circle of radius r
            # centred at cx2,cy2; the centre of curvature proceeds from
            # angle phi to phi+theta, and the actual point on the curve
            # is displaced from that centre by an amount which changes
            # linearly with angle from s1 to s2. Store all that.
            self.params = (r, cx2, cy2, phi, theta, s1, s2 - s1, mx)
        except ZeroDivisionError, e:
            self.params = None  # it went pear-shaped
Beispiel #4
0
def drawfaces():
    global vertices

    # Draw each face of the polyhedron.
    #
    # Originally this function produced a PostScript diagram of
    # each plane, showing the intersection lines with all the other
    # planes, numbering which planes they were, and outlining the
    # central polygon. This gives enough information to construct a
    # net of the solid. However, it now seems more useful to output
    # a 3D model of the polygon, but the PS output option is still
    # available if required.

    psprint("%!PS-Adobe-1.0")
    psprint("%%Pages:", len(points))
    psprint("%%EndComments")
    psprint("%%BeginProlog")
    psprint("%%BeginResource: procset foo")
    psprint("/cshow {")
    psprint("    /s exch def /y exch def /x exch def")
    psprint("    gsave")
    psprint("        0 0 moveto s true charpath flattenpath pathbbox 3 -1 roll")
    psprint("    grestore")
    psprint("    add 2 div y exch sub 3 1 roll add 2 div x exch sub exch moveto")
    psprint("    s show")
    psprint("} def")
    psprint("%%EndResource")
    psprint("%%EndProlog")

    faces = []

    for i in range(len(points)):
        psprint("%%Page:", i+1)
        psprint("gsave")
        psprint("288 400 translate 150 dup scale 0.0025 setlinewidth")
        psprint("/Helvetica findfont 0.1 scalefont setfont")

        x, y, z = points[i]

        # Begin by rotating the point set so that this point
        # appears at (0,0,1). To do this we must first find the
        # point's polar coordinates...
        theta = atan2(y, x)
        phi = asin(z)
        # ... and construct a matrix which first rotates by -theta
        # about the z-axis, thus bringing the point to the
        # meridian, and then rotates by pi/2-phi about the y-axis
        # to bring the point to (0,0,1).
        #
        # That matrix is therefore
        #
        #  ( cos(pi/2-phi)  0 -sin(pi/2-phi) ) ( cos(-theta) -sin(-theta) 0 )
        #  (       0        1        0       ) ( sin(-theta)  cos(-theta) 0 )
        #  ( sin(pi/2-phi)  0  cos(pi/2-phi) ) (      0            0      1 )
        #
        # which comes to
        #
        #  ( cos(theta)*sin(phi)  sin(theta)*sin(phi)  -cos(phi) )
        #  (     -sin(theta)          cos(theta)           0     )
        #  ( cos(theta)*cos(phi)  sin(theta)*cos(phi)   sin(phi) )

        matrix = [
        [ cos(theta)*sin(phi),  sin(theta)*sin(phi),  -cos(phi) ],
        [     -sin(theta)    ,      cos(theta)     ,      0     ],
        [ cos(theta)*cos(phi),  sin(theta)*cos(phi),   sin(phi) ]]

        rpoints = []
        for j in range(len(points)):
            if j == i: continue
            xa, ya, za = points[j]
            xb = matrix[0][0] * xa + matrix[0][1] * ya + matrix[0][2] * za
            yb = matrix[1][0] * xa + matrix[1][1] * ya + matrix[1][2] * za
            zb = matrix[2][0] * xa + matrix[2][1] * ya + matrix[2][2] * za
            rpoints.append((j, xb, yb, zb))

        # Now. For each point in rpoints, we find the tangent plane
        # to the sphere at that point, and find the line where it
        # intersects the uppermost plane Z=1.
        edges = []
        for j, x, y, z in rpoints:
            # The equation of the plane is xX + yY + zZ = 1.
            # Combining this with the equation Z=1 is trivial, and
            # yields the linear equation xX + yY = (1-z). Two
            # obvious points on this line are those with X=0 and
            # Y=0, which have coordinates (0,(1-z)/y) and
            # ((1-z)/x,0).
            if x == 0 or y == 0:
                continue # this point must be diametrically opposite us
            x1, y1 = 0, (1-z)/y
            x2, y2 = (1-z)/x, 0

            # Find the point of closest approach between this line
            # and the origin. This is most easily done by returning
            # to the original equation xX+yY=(1-z); this clearly
            # shows the line to be perpendicular to the vector
            # (x,y), and so the closest-approach point is where X
            # and Y are in that ratio, i.e. X=kx and Y=ky. Thus
            # kx^2+ky^2=(1-z), whence k = (1-z)/(x^2+y^2).
            k = (1-z)/(x*x+y*y)
            xx = k*x
            yy = k*y

            # Store details of this line.
            edges.append((x1,y1, x2,y2, xx,yy, i, j))

            # Find the intersection points of this line with the
            # edges of the square [-2,2] x [-2,2].
            xyl = crosspoint(x1, y1, x2, y2, -2, -2, -2, +2)
            xyr = crosspoint(x1, y1, x2, y2, +2, -2, +2, +2)
            xyu = crosspoint(x1, y1, x2, y2, -2, +2, +2, +2)
            xyd = crosspoint(x1, y1, x2, y2, -2, -2, +2, -2)
            # Throw out any which don't exist, or which are beyond
            # the limits.
            xys = []
            for xy in [xyl, xyr, xyu, xyd]:
                if xy == None: continue
                if xy[0] < -2 or xy[0] > 2: continue
                if xy[1] < -2 or xy[1] > 2: continue
                xys.append(xy)
            if len(xys) != 2:
                psprint("% unable to draw", "%d-%d" % (i+1, j+1), "edge")
            else:
                psprint(xys[0][0], xys[0][1], "moveto",)
                psprint(xys[1][0], xys[1][1], "lineto stroke")
                # Move 0.1 beyond the point of closest approach and
                # print the number of the side.
                d = sqrt(xx*xx + yy*yy)
                xx = xx + (0.1*xx/d)
                yy = yy + (0.1*yy/d)
                psprint(xx, yy, "(%d)" % (j+1), "cshow")

        psprint("0 0", "(%d)" % (i+1), "cshow")

        # The diagram we have just drawn is going to be a complex
        # stellated thing, with many intersection lines shown that
        # aren't part of the actual face of the polyhedron because
        # they are beyond its edges. Now we narrow our focus to
        # find the actual edges of the polygon.

        # We begin by notionally growing a circle out from the
        # centre point until it touches one of the lines. This line
        # will be an edge of the polygon, and furthermore the point
        # of contact will be _on_ the edge of the polygon. In other
        # words, we pick the edge whose closest-approach point is
        # the shortest distance from the origin.
        best = None
        n = None
        for j in range(len(edges)):
            xx,yy = edges[j][4:6]
            d2 = xx * xx + yy * yy
            if best == None or d2 < best:
                best = d2
                n = j

        assert n != None
        e = edges[n]
        startn = n
        # We choose to look anticlockwise along the edge. This
        # means mapping the vector (xx,yy) into (-yy,xx).
        v = (-e[5],e[4])
        p = (e[4],e[5])
        omit = -1  # to begin with we omit the intersection with no other edge
        poly = []
        while 1:
            # Now we have an edge e, a point p on the edge, and a
            # direction v in which to look along the edge. Examine
            # this edge's intersection points with all other edges,
            # and pick the one which is closest to p in the
            # direction of v (discarding any which are _behind_ p).
            xa1, ya1, xa2, ya2 = e[0:4]
            best = None
            n2 = None
            xp = yp = None
            for j in range(len(edges)):
                if j == omit or j == n:
                    continue # ignore this one
                xb1, yb1, xb2, yb2 = edges[j][0:4]
                xcyc = crosspoint(xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2)
                if xcyc == None:
                    continue # this edge is parallel to e
                xc, yc = xcyc
                dotprod = (xc - p[0]) * v[0] + (yc - p[1]) * v[1]
                if dotprod < 0:
                    continue
                if best == None or dotprod < best:
                    best = dotprod
                    n2 = j
                    xp, yp = xc, yc
            assert n2 != None
            # Found a definite corner of the polygon. Save its
            # coordinates, and also save the numbers of the three
            # planes at whose intersection the point lies.
            poly.append((xp, yp, e[6], e[7], edges[n2][7]))
            # Now move on. We must now look along the new edge.
            e = edges[n2]
            p = xp, yp     # start looking from the corner we've found
            omit = n       # next time, ignore the corner we've just hit!
            n = n2
            # v is slightly tricky. We are moving anticlockwise
            # around the polygon; so we first rotate the previous v
            # 90 degrees left, and then we choose whichever
            # direction along the new edge has a positive dot
            # product with this vector.
            vtmp = (-v[1], v[0])
            v = (-e[5],e[4])
            if v[0] * vtmp[0] + v[1] * vtmp[1] < 0:
                v = (e[5], -e[4])
            # Terminate the loop if we have returned to our
            # starting edge.
            if n == startn:
                break

        # Draw round the polygon in thicker pen.
        #psprint("0.01 setlinewidth")
        #psprint("newpath")
        #cmd = "moveto"
        #for p in poly:
        #    psprint("   ", p[0], p[1], cmd)
        #    cmd = "lineto"
        #psprint("closepath stroke")
        psprint("showpage grestore")

        # Save everything we need to write out a 3D model later on.
        # In particular this involves keeping the coordinates of
        # the points, for which we will need to find the inverse of
        # the rotation matrix so as to put the points back where
        # they started.
        #
        # The inverse rotation matrix is
        #
        #  (  cos(-theta) sin(-theta) 0 ) (  cos(pi/2-phi)  0 sin(pi/2-phi) )
        #  ( -sin(-theta) cos(-theta) 0 ) (       0        1        0       )
        #  (      0            0      1 ) ( -sin(pi/2-phi)  0 cos(pi/2-phi) )
        #
        # which comes to
        #
        #  ( cos(theta)*sin(phi)  -sin(theta)  cos(theta)*cos(phi) )
        #  ( sin(theta)*sin(phi)   cos(theta)  sin(theta)*cos(phi) )
        #  (      -cos(phi)            0             sin(phi)      )
        
        imatrix = [
        [ cos(theta)*sin(phi),  -sin(theta),  cos(theta)*cos(phi) ],
        [ sin(theta)*sin(phi),   cos(theta),  sin(theta)*cos(phi) ],
        [      -cos(phi)     ,       0     ,        sin(phi)      ]]

        facelist = []
        for p in poly:
            xa, ya = p[0:2]
            za = 1
            xb = imatrix[0][0] * xa + imatrix[0][1] * ya + imatrix[0][2] * za
            yb = imatrix[1][0] * xa + imatrix[1][1] * ya + imatrix[1][2] * za
            zb = imatrix[2][0] * xa + imatrix[2][1] * ya + imatrix[2][2] * za
            planes = list(p[2:5])
            planes.sort()
            planes = tuple(planes)
            if not vertices.has_key(planes):
                vertices[planes] = []
            vertices[planes].append((xb, yb, zb))
            facelist.append(planes)

        faces.append((i, facelist))

    psprint("%%EOF")

    # Now output the polygon description.
    #
    # Each polygon has been prepared in its own frame of reference,
    # so the absolute coordinates of the vertices will vary
    # depending on which polygon they were prepared in. For this
    # reason I have kept _every_ version of the coordinates of each
    # vertex, so we can now average them into a single canonical value.
    for key, value in vertices.items():
        xt = yt = zt = n = 0
        xxt = yyt = zzt = 0
        vlabel = pointlabel(key)
        for x, y, z in value:
            xt = xt + x
            yt = yt + y
            zt = zt + z
            xxt = xxt + x*x
            yyt = yyt + y*y
            zzt = zzt + z*z
            n = n + 1
        polyprint("point", vlabel, xt/n, yt/n, zt/n)

    for i, vlist in faces:
        flabel = "face_" + str(i)
        for key in vlist:
            vlabel = pointlabel(key)
            polyprint("face", flabel, vlabel)
        # And the surface normal (pointing outwards), which is
        # simply the position vector of the original point i.
        polyprint("normal", flabel, points[i][0], points[i][1], points[i][2])
Beispiel #5
0
def drawfaces():
    global vertices

    # Draw each face of the polyhedron.
    #
    # Originally this function produced a PostScript diagram of
    # each plane, showing the intersection lines with all the other
    # planes, numbering which planes they were, and outlining the
    # central polygon. This gives enough information to construct a
    # net of the solid. However, it now seems more useful to output
    # a 3D model of the polygon, but the PS output option is still
    # available if required.

    psprint("%!PS-Adobe-1.0")
    psprint("%%Pages:", len(points))
    psprint("%%EndComments")
    psprint("%%BeginProlog")
    psprint("%%BeginResource: procset foo")
    psprint("/cshow {")
    psprint("    /s exch def /y exch def /x exch def")
    psprint("    gsave")
    psprint(
        "        0 0 moveto s true charpath flattenpath pathbbox 3 -1 roll")
    psprint("    grestore")
    psprint(
        "    add 2 div y exch sub 3 1 roll add 2 div x exch sub exch moveto")
    psprint("    s show")
    psprint("} def")
    psprint("%%EndResource")
    psprint("%%EndProlog")

    faces = []

    for i in range(len(points)):
        psprint("%%Page:", i + 1)
        psprint("gsave")
        psprint("288 400 translate 150 dup scale 0.0025 setlinewidth")
        psprint("/Helvetica findfont 0.1 scalefont setfont")

        x, y, z = points[i]

        # Begin by rotating the point set so that this point
        # appears at (0,0,1). To do this we must first find the
        # point's polar coordinates...
        theta = atan2(y, x)
        phi = asin(z)
        # ... and construct a matrix which first rotates by -theta
        # about the z-axis, thus bringing the point to the
        # meridian, and then rotates by pi/2-phi about the y-axis
        # to bring the point to (0,0,1).
        #
        # That matrix is therefore
        #
        #  ( cos(pi/2-phi)  0 -sin(pi/2-phi) ) ( cos(-theta) -sin(-theta) 0 )
        #  (       0        1        0       ) ( sin(-theta)  cos(-theta) 0 )
        #  ( sin(pi/2-phi)  0  cos(pi/2-phi) ) (      0            0      1 )
        #
        # which comes to
        #
        #  ( cos(theta)*sin(phi)  sin(theta)*sin(phi)  -cos(phi) )
        #  (     -sin(theta)          cos(theta)           0     )
        #  ( cos(theta)*cos(phi)  sin(theta)*cos(phi)   sin(phi) )

        matrix = [[cos(theta) * sin(phi),
                   sin(theta) * sin(phi), -cos(phi)],
                  [-sin(theta), cos(theta), 0],
                  [cos(theta) * cos(phi),
                   sin(theta) * cos(phi),
                   sin(phi)]]

        rpoints = []
        for j in range(len(points)):
            if j == i: continue
            xa, ya, za = points[j]
            xb = matrix[0][0] * xa + matrix[0][1] * ya + matrix[0][2] * za
            yb = matrix[1][0] * xa + matrix[1][1] * ya + matrix[1][2] * za
            zb = matrix[2][0] * xa + matrix[2][1] * ya + matrix[2][2] * za
            rpoints.append((j, xb, yb, zb))

        # Now. For each point in rpoints, we find the tangent plane
        # to the sphere at that point, and find the line where it
        # intersects the uppermost plane Z=1.
        edges = []
        for j, x, y, z in rpoints:
            # The equation of the plane is xX + yY + zZ = 1.
            # Combining this with the equation Z=1 is trivial, and
            # yields the linear equation xX + yY = (1-z). Two
            # obvious points on this line are those with X=0 and
            # Y=0, which have coordinates (0,(1-z)/y) and
            # ((1-z)/x,0).
            if x == 0 or y == 0:
                continue  # this point must be diametrically opposite us
            x1, y1 = 0, (1 - z) / y
            x2, y2 = (1 - z) / x, 0

            # Find the point of closest approach between this line
            # and the origin. This is most easily done by returning
            # to the original equation xX+yY=(1-z); this clearly
            # shows the line to be perpendicular to the vector
            # (x,y), and so the closest-approach point is where X
            # and Y are in that ratio, i.e. X=kx and Y=ky. Thus
            # kx^2+ky^2=(1-z), whence k = (1-z)/(x^2+y^2).
            k = (1 - z) / (x * x + y * y)
            xx = k * x
            yy = k * y

            # Store details of this line.
            edges.append((x1, y1, x2, y2, xx, yy, i, j))

            # Find the intersection points of this line with the
            # edges of the square [-2,2] x [-2,2].
            xyl = crosspoint(x1, y1, x2, y2, -2, -2, -2, +2)
            xyr = crosspoint(x1, y1, x2, y2, +2, -2, +2, +2)
            xyu = crosspoint(x1, y1, x2, y2, -2, +2, +2, +2)
            xyd = crosspoint(x1, y1, x2, y2, -2, -2, +2, -2)
            # Throw out any which don't exist, or which are beyond
            # the limits.
            xys = []
            for xy in [xyl, xyr, xyu, xyd]:
                if xy == None: continue
                if xy[0] < -2 or xy[0] > 2: continue
                if xy[1] < -2 or xy[1] > 2: continue
                xys.append(xy)
            if len(xys) != 2:
                psprint("% unable to draw", "%d-%d" % (i + 1, j + 1), "edge")
            else:
                psprint(
                    xys[0][0],
                    xys[0][1],
                    "moveto",
                )
                psprint(xys[1][0], xys[1][1], "lineto stroke")
                # Move 0.1 beyond the point of closest approach and
                # print the number of the side.
                d = sqrt(xx * xx + yy * yy)
                xx = xx + (0.1 * xx / d)
                yy = yy + (0.1 * yy / d)
                psprint(xx, yy, "(%d)" % (j + 1), "cshow")

        psprint("0 0", "(%d)" % (i + 1), "cshow")

        # The diagram we have just drawn is going to be a complex
        # stellated thing, with many intersection lines shown that
        # aren't part of the actual face of the polyhedron because
        # they are beyond its edges. Now we narrow our focus to
        # find the actual edges of the polygon.

        # We begin by notionally growing a circle out from the
        # centre point until it touches one of the lines. This line
        # will be an edge of the polygon, and furthermore the point
        # of contact will be _on_ the edge of the polygon. In other
        # words, we pick the edge whose closest-approach point is
        # the shortest distance from the origin.
        best = None
        n = None
        for j in range(len(edges)):
            xx, yy = edges[j][4:6]
            d2 = xx * xx + yy * yy
            if best == None or d2 < best:
                best = d2
                n = j

        assert n != None
        e = edges[n]
        startn = n
        # We choose to look anticlockwise along the edge. This
        # means mapping the vector (xx,yy) into (-yy,xx).
        v = (-e[5], e[4])
        p = (e[4], e[5])
        omit = -1  # to begin with we omit the intersection with no other edge
        poly = []
        while 1:
            # Now we have an edge e, a point p on the edge, and a
            # direction v in which to look along the edge. Examine
            # this edge's intersection points with all other edges,
            # and pick the one which is closest to p in the
            # direction of v (discarding any which are _behind_ p).
            xa1, ya1, xa2, ya2 = e[0:4]
            best = None
            n2 = None
            xp = yp = None
            for j in range(len(edges)):
                if j == omit or j == n:
                    continue  # ignore this one
                xb1, yb1, xb2, yb2 = edges[j][0:4]
                xcyc = crosspoint(xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2)
                if xcyc == None:
                    continue  # this edge is parallel to e
                xc, yc = xcyc
                dotprod = (xc - p[0]) * v[0] + (yc - p[1]) * v[1]
                if dotprod < 0:
                    continue
                if best == None or dotprod < best:
                    best = dotprod
                    n2 = j
                    xp, yp = xc, yc
            assert n2 != None
            # Found a definite corner of the polygon. Save its
            # coordinates, and also save the numbers of the three
            # planes at whose intersection the point lies.
            poly.append((xp, yp, e[6], e[7], edges[n2][7]))
            # Now move on. We must now look along the new edge.
            e = edges[n2]
            p = xp, yp  # start looking from the corner we've found
            omit = n  # next time, ignore the corner we've just hit!
            n = n2
            # v is slightly tricky. We are moving anticlockwise
            # around the polygon; so we first rotate the previous v
            # 90 degrees left, and then we choose whichever
            # direction along the new edge has a positive dot
            # product with this vector.
            vtmp = (-v[1], v[0])
            v = (-e[5], e[4])
            if v[0] * vtmp[0] + v[1] * vtmp[1] < 0:
                v = (e[5], -e[4])
            # Terminate the loop if we have returned to our
            # starting edge.
            if n == startn:
                break

        # Draw round the polygon in thicker pen.
        #psprint("0.01 setlinewidth")
        #psprint("newpath")
        #cmd = "moveto"
        #for p in poly:
        #    psprint("   ", p[0], p[1], cmd)
        #    cmd = "lineto"
        #psprint("closepath stroke")
        psprint("showpage grestore")

        # Save everything we need to write out a 3D model later on.
        # In particular this involves keeping the coordinates of
        # the points, for which we will need to find the inverse of
        # the rotation matrix so as to put the points back where
        # they started.
        #
        # The inverse rotation matrix is
        #
        #  (  cos(-theta) sin(-theta) 0 ) (  cos(pi/2-phi)  0 sin(pi/2-phi) )
        #  ( -sin(-theta) cos(-theta) 0 ) (       0        1        0       )
        #  (      0            0      1 ) ( -sin(pi/2-phi)  0 cos(pi/2-phi) )
        #
        # which comes to
        #
        #  ( cos(theta)*sin(phi)  -sin(theta)  cos(theta)*cos(phi) )
        #  ( sin(theta)*sin(phi)   cos(theta)  sin(theta)*cos(phi) )
        #  (      -cos(phi)            0             sin(phi)      )

        imatrix = [[cos(theta) * sin(phi), -sin(theta),
                    cos(theta) * cos(phi)],
                   [sin(theta) * sin(phi),
                    cos(theta),
                    sin(theta) * cos(phi)], [-cos(phi), 0,
                                             sin(phi)]]

        facelist = []
        for p in poly:
            xa, ya = p[0:2]
            za = 1
            xb = imatrix[0][0] * xa + imatrix[0][1] * ya + imatrix[0][2] * za
            yb = imatrix[1][0] * xa + imatrix[1][1] * ya + imatrix[1][2] * za
            zb = imatrix[2][0] * xa + imatrix[2][1] * ya + imatrix[2][2] * za
            planes = list(p[2:5])
            planes.sort()
            planes = tuple(planes)
            if not vertices.has_key(planes):
                vertices[planes] = []
            vertices[planes].append((xb, yb, zb))
            facelist.append(planes)

        faces.append((i, facelist))

    psprint("%%EOF")

    # Now output the polygon description.
    #
    # Each polygon has been prepared in its own frame of reference,
    # so the absolute coordinates of the vertices will vary
    # depending on which polygon they were prepared in. For this
    # reason I have kept _every_ version of the coordinates of each
    # vertex, so we can now average them into a single canonical value.
    for key, value in vertices.items():
        xt = yt = zt = n = 0
        xxt = yyt = zzt = 0
        vlabel = pointlabel(key)
        for x, y, z in value:
            xt = xt + x
            yt = yt + y
            zt = zt + z
            xxt = xxt + x * x
            yyt = yyt + y * y
            zzt = zzt + z * z
            n = n + 1
        polyprint("point", vlabel, xt / n, yt / n, zt / n)

    for i, vlist in faces:
        flabel = "face_" + str(i)
        for key in vlist:
            vlabel = pointlabel(key)
            polyprint("face", flabel, vlabel)
        # And the surface normal (pointing outwards), which is
        # simply the position vector of the original point i.
        polyprint("normal", flabel, points[i][0], points[i][1], points[i][2])
Beispiel #6
0
    def set_params(self):
        x1, y1, dx1, dy1, x2, y2, dx2, dy2, mx = self.inparams
        try:
            # Normalise the direction vectors.
            dlen1 = sqrt(dx1**2 + dy1**2)
            dx1, dy1 = dx1 / dlen1, dy1 / dlen1
            dlen2 = sqrt(dx2**2 + dy2**2)
            dx2, dy2 = dx2 / dlen2, dy2 / dlen2

            self.inparams = (x1, y1, dx1, dy1, x2, y2, dx2, dy2, mx)

            # Transform into the squashed coordinate system.
            if mx != None:
                x1, y1 = squash(x1, y1, mx)
                dx1, dy1 = squash(dx1, dy1, mx)
                x2, y2 = squash(x2, y2, mx)
                dx2, dy2 = squash(dx2, dy2, mx)
                # And renormalise.
                dlen1 = sqrt(dx1**2 + dy1**2)
                dx1, dy1 = dx1 / dlen1, dy1 / dlen1
                dlen2 = sqrt(dx2**2 + dy2**2)
                dx2, dy2 = dx2 / dlen2, dy2 / dlen2

            # Find the normal vectors at each end by rotating the
            # direction vectors.
            nx1, ny1 = dy1, -dx1
            nx2, ny2 = dy2, -dx2

            # Find the crossing point of the normals.
            cx, cy = crosspoint(x1, y1, x1 + nx1, y1 + ny1, x2, y2, x2 + nx2,
                                y2 + ny2)

            # Measure the distance from that crossing point to each
            # endpoint, and find the difference.
            #
            # The distance is obtained by taking the dot product with
            # the line's defining vector, so that it's signed.
            d1 = (cx - x1) * nx1 + (cy - y1) * ny1
            d2 = (cx - x2) * nx2 + (cy - y2) * ny2

            dd = d2 - d1

            # Find the angle between the two direction vectors. Since
            # they're already normalised to unit length, the magnitude
            # of this is just the inverse cosine of their dot product.
            # The sign must be chosen to reflect which way round they
            # are.
            dp = dx1 * dx2 + dy1 * dy2
            if abs(dp) > 1: dp /= abs(dp)  # avoid EDOM from rounding error
            theta = -acos(dp)
            if dx1 * dy2 - dx2 * dy1 > 0:
                theta = -theta

            # So we need a circular arc rotating through angle theta,
            # such that taking the involute of that arc with the right
            # length does the right thing.
            #
            # Suppose the circle has radius r. Then, when the circle
            # touches the line going to point 1, we need our string to
            # have length equal to d1 - r tan(theta/2). When it touches
            # the line going to point 2, the string length needs to be
            # d2 + r tan(theta/2). The difference between these numbers
            # must be equal to the arc length of the portion of the
            # circle in between, which is r*theta. Setting these equal
            # gives d2-d1 = r theta - 2 r tan(theta/2), which we solve
            # to get r = (d2-d1) / (theta - 2 tan (theta/2)).
            #
            # (In fact, we then flip the sign to take account of the way
            # we subsequently use r.)
            r = dd / (-theta + 2 * tan(theta / 2))

            # So how do we find the centre of a circle of radius r
            # tangent to both those lines? We shift the start point of
            # each line by r in the appropriate direction, and find
            # their crossing point again.
            cx2, cy2 = crosspoint(x1-r*dx1, y1-r*dy1, x1-r*dx1+nx1, y1-r*dy1+ny1, \
            x2-r*dx2, y2-r*dy2, x2-r*dx2+nx2, y2-r*dy2+ny2)

            # Now find the distance along each line to the centre of the
            # circle, which will be the string lengths at the endpoints.
            s1 = (cx2 - x1) * nx1 + (cy2 - y1) * ny1
            s2 = (cx2 - x2) * nx2 + (cy2 - y2) * ny2

            # Determine the starting angle.
            phi = atan2(dy1, dx1)

            # And that's it. We're involving a circle of radius r
            # centred at cx2,cy2; the centre of curvature proceeds from
            # angle phi to phi+theta, and the actual point on the curve
            # is displaced from that centre by an amount which changes
            # linearly with angle from s1 to s2. Store all that.
            self.params = (r, cx2, cy2, phi, theta, s1, s2 - s1, mx)
        except ZeroDivisionError, e:
            self.params = None  # it went pear-shaped
Beispiel #7
0
     break
 if vl1.face != placement.face and \
 placement.vpos.has_key(e1[1]):
     break
 for e2 in faceedges[vl2.face]:
     if vl2.face != placement.face and \
     placement.vpos.has_key(e2[0]):
         break
     if vl2.face != placement.face and \
     placement.vpos.has_key(e2[1]):
         break
     xa1, ya1 = vl1.vpos[e1[0]]
     xa2, ya2 = vl1.vpos[e1[1]]
     xb1, yb1 = vl2.vpos[e2[0]]
     xb2, yb2 = vl2.vpos[e2[1]]
     ret = crosspoint(xa1, ya1, xa2, ya2, xb1, yb1, xb2,
                      yb2)
     if ret == None: continue
     x, y = ret
     dxa, dya = xa2 - xa1, ya2 - ya1
     dxb, dyb = xb2 - xb1, yb2 - yb1
     # See if the crossing point is between the
     # ends of each line. This will be true if
     # the dot product (x-xa1,y-ya1).(dxa,dya)
     # divided by the squared length
     # (dxa,dya).(dxa,dya) is strictly between 0
     # and 1. Likewise for b1/b2.
     dp = ((x - xa1) * dxa +
           (y - ya1) * dya) / (dxa**2 + dya**2)
     if dp < 0 or dp > 1: continue
     dp = ((x - xb1) * dxb +
           (y - yb1) * dyb) / (dxb**2 + dyb**2)