Contour

UNDER DEVELOPMENT

Script which takes a mesh object which contains closed loops and generates a ‘nice’ mesh from them.

I’ve got development snapshot here, it’s in a very rough state still, but the result’s are not quite what I expected.

There are a few tests I am going to run, but wondered if the reasoning jumps out to you?

contour.blend

Basically make sure the mesh is selected and then “Alt + P” in the script window.

The area of interest should stem from “do_version_02()”.

What the issue is, the script takes a point from a grid, checks it against regions and then should find the highest one. But for some reason, some of the points get set as default (0.0). There can be issues with the point in poly function when the point is too close, but i’d expect that the output would be the next region down, not the default…

Any ideas?

Cheers!

I’ve tried various things but still can’t see where the issue is stemming from. I’m not even sure if the same happens in both versions and at both high and low resolutions…

contour.blend

you might want to specify for which build the script works on i have tried on 2.56 and 2.57 no luck script failed .

Developed with 2.57.

How do you mean “no luck script failed”?

Was there no output to the terminal?

it just generates a flat mesh …just a plane …is it because you need python 3.2> to utilize your script?

3.2 is what’s bundled with my version of Blender 2.57, why are you using something else?

But looking through I can’t see anything fancy or new.

Personally i’m having no issues with running the script (nor are others), but just with the results!

cheers man appreciate your work … but for know i,m screwed guess will have to live without this script … keep up the good work.

Hi,

I forgot about this script because I couldn’t fathom why it behaves oddly in places.

There are some new right angled examples which work as expected, however the angled loops still default to the baseline in places…

Any ideas, i’ve run out!

Cheers

contour.blend

cool little script, but you need to write more robust functions like:

  • ‘create_list_of_polygons_on_plane(z)’
    ::: accepts as arguments a z height,
    ::: returns list of (Verts, Edges) tuples

  • ‘check_if_polygon_is_convex_or_concave(Verts, Edges)’
    ::: accepts two lists (Verts, Edges)
    ::: returns “Concave” or “Convex” depending

  • ‘is_point_inside_polygon(point, list_of_polygons)’
    ::: accepts (x,y) and a list of polygons
    ::: returns True or False depending if the point is inside any polygon at that height.

http://alienryderflex.com/polygon/

Do I conditionally need convex partitioning?

If so, any good examples please?

actually, it is not so relevant to the eventual code if the polygon is convex or concave, but i spent a couple of days working on similar code last week and can perhaps share some thoughts on the matter. The wikipedia article regarding ‘point in polygon’ described a solution that works for most cases except when vertices of the polygon happen to share a component-coordinate with the point you are checking against. http://en.wikipedia.org/wiki/Point_in_polygon (ray casting algorithm)

I think the most natural solution is to do a >1 point multisample, with small differences in the x/y axis if the plane is z. So if one or more of the verts share a coordinate you can ask instead, does it share component-coordinates if I check several points close to this point. More sample points near the coordinate you want to check, will yield a more reliable answer.

The code isn’t pretty because it contains a bit of additional debug code… if you have a polygon in edit mode, place the 3d cursor either inside or outside the polygon, the console will then print inside or outside. (but doesn’t here deal with the special case of sharing coordinate components)



import bpy
from mathutils import Vector, geometry

# defaults :
precision = 1.0e-6
intersection_list = []

# returns rounded str version of the vector for quick debug.
def rounded_vector(vec):
    str_rounded_vector = ""
    
    vec_list = vec.x, vec.y, vec.z
    axis = "  x ", "  y ", "  z "
    iter = 0
    for i in vec_list:
        str_rounded_vector += axis[iter]
        str_rounded_vector += str(round(i, 5))
        iter += 1
    return str_rounded_vector[2:]


# a polygon is a minimum of 3 verts, 3 edges.
def make_edges_from_polygon(polygon):
    Edges = []
    polylen = len(polygon)
    for e in range(polylen):
        if e == polylen-1:
            Edges.append([e, 0])
            break
        Edges.append([e, e+1])
    return Edges


# could even make Edges internally
def make_polygon(Verts, Edges, object_name):
    object_mesh = object_name + "_mesh"
    
    mesh = bpy.data.meshes.new(object_mesh)
    mesh.from_pydata(Verts, Edges, [])
    mesh.update()
    new_object = bpy.data.objects.new(object_name, mesh)
    new_object.data = mesh
    scene = bpy.context.scene
    scene.objects.link(new_object)
    return


def make_single_edge(vert1, vert2, edge_name):
    # initialize some stuff
    object_mesh = edge_name + "_mesh"
    Verts = vert1, vert2 
    Edges = [[0,1]]
    
    # work with this data
    mesh = bpy.data.meshes.new(object_mesh)
    mesh.from_pydata(Verts, Edges, [])
    mesh.update()
    new_object = bpy.data.objects.new(edge_name, mesh)
    new_object.data = mesh
    scene = bpy.context.scene
    scene.objects.link(new_object)
    return    


def make_handle(h_size, vertex):
    vertex = vertex.co

    top_left = Vector((vertex.x-h_size, vertex.y+h_size, 0.0))
    top_right = Vector((vertex.x+h_size, vertex.y+h_size, 0.0))
    bottom_right = Vector((vertex.x+h_size, vertex.y-h_size, 0.0))
    bottom_left = Vector((vertex.x-h_size, vertex.y-h_size, 0.0))
    
    Verts = top_left, top_right, bottom_right, bottom_left    
    Edges = make_edges_from_polygon(Verts)
    make_polygon(Verts, Edges, "handle")
    pass


def get_handle_and_boundary(polygon):
    
    # subroutine for sorting
    def fn1(edge):
        return edge[1]
    
    xy_list = []
    for vertex in polygon:
        xy_list.append([vertex.co.x, vertex.co.y])        

    topx = sorted(xy_list, reverse=True)[0][0]
    lowx = sorted(xy_list)[0][0]
    topy = sorted(xy_list, key=fn1, reverse=True)[0][1]
    lowy = sorted(xy_list, key=fn1)[0][1]
    
    vecA = Vector((topx, 1.0, 0.0))
    vecB = Vector((lowx, 1.0, 0.0))   
    h_size = ( (vecA-vecB).length ) / 30    
    return h_size, (lowx, topx, lowy, topy)


def generate_verts_from_boundary_values(boundary):
    arb_z = 0.0
    Verts = []
    lowx, topx, lowy, topy = boundary
    Verts.append((lowx, topy, arb_z))
    Verts.append((topx, topy, arb_z))
    Verts.append((topx, lowy, arb_z))
    Verts.append((lowx, lowy, arb_z))
    return Verts


def make_intersection_line(boundary, test_coord):
    lowx = boundary[0]
    vert1 = (lowx, test_coord.y, 0.0)
    # make_single_edge(vert1, test_coord, "incident_line")
    return vert1, test_coord

    
def make_boundary(boundary):
    BB_Verts = generate_verts_from_boundary_values(boundary)
    BB_Edges = make_edges_from_polygon(BB_Verts)
    make_polygon(BB_Verts, BB_Edges, "Boundary")   


def make_handles(polygon):
    for vertex in polygon:
        make_handle(h_size, vertex)
    

def make_tag_on_intersection(test_coord, polygon):
    '''
    Edges = make_edges_from_polygon(polygon)
    for edgekey in Edges:
        print(edgekey)
        vert1 = polygon[edgekey[0]].co
        vert2 = polygon[edgekey[1]].co
        vert_list = vert1, vert2
        for i in vert_list:
            print(rounded_vector(i))
        print(" ")
    '''
    pass



def make_tag_at_point(point, topy):
    vert2 = Vector((point.x, topy, 0.0))
    intersection_list.append(vert2)
    # make_single_edge(point, vert2, "edge_tick")    


def is_intersection_on_both_edges(v1, v2, v3, v4):
    
    def check_verts_for_similarity(v3, v4):
        # if 1 (or more) of these verts shares the y component of the
        # coordinate being checked
        num_sims = 0
        for coord in (v3, v4):
            if coord.y == test_coord.y:
                num_sims += 1
        # take test_coord appart.
        print(num_sims, "verts checked share a y coordinate") 
        return
    
    def is_intersection_on_edge(v1, v2, point):
        edge_len = (v1-v2).length
        portion1 = (v1-point).length
        portion2 = (v2-point).length
        variance = edge_len - (portion1 + portion2)
        if (variance < precision) and (variance > 0.0-precision):
            # print("variance: ", variance)
            return True
        else: 
            return False
        
    point = geometry.intersect_line_line(v1, v2, v3, v4)[0]
    if is_intersection_on_edge(v1, v2, point):
        if is_intersection_on_edge(v3, v4, point):
            make_tag_at_point(point, boundary[3])
            
            # having reached this point we know that the intersection is
            # occurring on both edges , we also know that we aren't counting
            # edges that are not of interest
            check_verts_for_similarity(v3, v4)
            return True
    else:
        return False


def check_polygon_against_intersections(intersection_line, polygon, edge_keys):
    v1, v2 = intersection_line
    v1 = Vector(v1) # doesn't receive Vector version, no problem!

    num_intersections = 0
    for edgekey in edge_keys:
        v3 = polygon[edgekey[0]].co
        v4 = polygon[edgekey[1]].co
        if is_intersection_on_both_edges(v1, v2, v3, v4): 
            # print("yes, point lies on both edges")
            num_intersections += 1
    
    # print(num_intersections)
    
    
# if bpy.context.active_object.data.edge_keys returns an empty list.
def get_edgekeys_from_active_object(active_pgon):
    edge_keys = []
    for i in active_pgon.edges:
        edge_keys.append([i.vertices[0],i.vertices[1]])
    return edge_keys


print("="*30)

active_pgon = bpy.context.active_object.data
polygon = active_pgon.vertices

p_edge_keys = get_edgekeys_from_active_object(active_pgon)
h_size, boundary = get_handle_and_boundary(polygon)
    
test_coord = bpy.context.scene.cursor_location

# make_handles(polygon)
# make_boundary(boundary)

intersection_line = make_intersection_line(boundary, test_coord)
make_tag_on_intersection(test_coord, polygon)
check_polygon_against_intersections(intersection_line, polygon, p_edge_keys)

# maybe one should multisample from reasonably spaced test_coordinates to see
# if the result from this process is accurately reflecting reality.    
def return_rounded_unique_list(intersection_list):
    temp_list = []
    for i in intersection_list:
        print(i)
        temp_list.append(round(i.x, 6))
    
    for i in temp_list:
        print(i, i.__class__)
        
    lm = set(temp_list)
    lm = list(lm)
    return len(lm)

real_num = return_rounded_unique_list(intersection_list)
print(real_num)    
if real_num % 2 == 0:
    print(real_num, "is even, the test_coordinate lies outside the polygon")
else:
    print(real_num, "is uneven so the test coordinate lies inside the polygon")



to make things a lot easier, faster, i’d suggest attempting to make a solid surface from the polygon outline (in which case concave or convex does make a difference), then checking each face for intersection with the point you are checking against.

Oooh, my heads done too much today to be fathoming that right now… Thankyou, give me a few days to reply…

P.S. I don’t get instant (can be two or three days, or never!) email notifications from BA for quite a while now…