2

I am very new to Python and was surprised to find that this section of my code:

print len(allCommunities[5].boundary)
allCommunities[5].surface = triangularize(allCommunities[5].boundary)
print len(allCommunities[5].boundary)

Outputs this:

1310
2

Below is a function I wrote in Processing (a language like Java) and ported into Python. It does what it is supposed to (triangulate a polygon) but my intention was to pass inBoundary for the function to use but not remove elements from allCommunities[5].boundary.

How should I go about preventing allCommunities[5].boundary from being modified in the function? On a side note, I would appreciate pointers if I am doing something silly otherwise in the function, still getting used to Python.

def triangularize(inBoundary):
    outSurface = []

    index = 0;
    while len(inBoundary) > 2:
        pIndex = (index+len(inBoundary)-1)%len(inBoundary);
        nIndex = (index+1)%len(inBoundary);

        bp = inBoundary[pIndex]
        bi = inBoundary[index]
        bn = inBoundary[nIndex]

        # This assumes the polygon is in clockwise order
        theta = math.atan2(bi.y-bn.y, bi.x-bn.x)-math.atan2(bi.y-bp.y, bi.x-bp.x);
        if theta < 0.0: theta += math.pi*2.0;

        # If bp, bi, and bn describe an "ear" of the polygon
        if theta < math.pi:
            inside = False;

            # Make sure other vertices are not inside the "ear"
            for i in range(len(inBoundary)):
                if i == pIndex or i == index or i == nIndex: continue;

                # Black magic point in triangle expressions
                # http://answers.yahoo.com/question/index?qid=20111103091813AA1jksL
                pi = inBoundary[i]
                ep = (bi.x-bp.x)*(pi.y-bp.y)-(bi.y-bp.y)*(pi.x-bp.x)
                ei = (bn.x-bi.x)*(pi.y-bi.y)-(bn.y-bi.y)*(pi.x-bi.x)
                en = (bp.x-bn.x)*(pi.y-bn.y)-(bp.y-bn.y)*(pi.x-bn.x)

                # This only tests if the point is inside the triangle (no edge / vertex test)
                if (ep < 0 and ei < 0 and en < 0) or (ep > 0 and ei > 0 and en > 0):
                    inside = True;
                    break

            # No vertices in the "ear", add a triangle and remove bi
            if not inside:
                outSurface.append(Triangle(bp, bi, bn))
                inBoundary.pop(index)
        index = (index+1)%len(inBoundary)
    return outSurface

print len(allCommunities[5].boundary)
allCommunities[5].surface = triangularize(allCommunities[5].boundary)
print len(allCommunities[5].boundary)
2

2 Answers 2

4

Lists in Python are mutable, and operations such as

inBoundary.pop

modify them. The easy solution is to copy the list inside the function:

def triangularize(inBoundary):
    inBoundary = list(inBoundary)
    # proceed as before
Sign up to request clarification or add additional context in comments.

5 Comments

There is not a problem with them having the same name?
@asimes: Nope, that is not a problem.
@asimes: on the contrary, the name inBoundary will just get re-bound to the new value (the copy), and only inside the function.
Personally I find that confusing for usage even if having two of the same variable name works
@asimes: it can be very advantageous to reuse variable names, since you can't accidentally use the "wrong" value anymore (the unvalidated input). I use this idiom all the time in input validation code, and find that it's more robust than inventing a new name.
3

The easiest thing to do would be to make a copy of the argument coming in:

def triangularize(origBoundary):
    inBoundary = origBoundary[:]

Then the rest of your code can stay the same.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.