Skip to content

Mitered offsets with Hemesh #8

@op-rt

Description

@op-rt

Dear @wblut,

Is there a simple way to output the mitered offsets of a polygon with Hemesh ?

For the moment I'm using the following routine:

  • get the straight skeleton of the polygon
  • convert each polygonal edge to a ray
  • offset that parallel ray inward
  • look for intersections with the boundaries of the corresponding skeleton face

Exemple:

add_library('hemesh')

verts = [PVector(144, 315), PVector(125, 273), PVector(134, 262), PVector(140, 276), PVector(176, 281), #05
         PVector(193, 269), PVector(193, 195), PVector(203, 236), PVector(225, 236), PVector(254, 203), #10
         PVector(301, 212), PVector(377, 139), PVector(417, 144), PVector(429, 200), PVector(402, 237), #15
         PVector(416, 250), PVector(442, 241), PVector(434, 267), PVector(337, 368), PVector(186, 401), #20
         PVector(162, 387)]


def setup():
    size(600, 500)
    background('#FFFFFF')

    #### COMPUTE STRAIGHT SKELETON ####
        
    #Convert vertices to WB_Points (must be in clockwise order) 
    polyverts = [WB_Point(p.x, p.y) for p in verts]
    
    #Set a very large offset for each vertex (similar to a wavefront so far projected it will output the whole skeleton)
    offsets = [1000] * len(verts)
    
    #Some HE_Mesh magic trick
    pyr = WB_PyramidFactory().setPoints(polyverts)
    mesh = HE_Mesh(pyr.createOffset(0, offsets))
    
    #Store Skeleton edges (just for rendering)
    edges = set()
    for i1, i2 in mesh.getEdgesAsInt():
        v1 = mesh.getVertex(i1).getPosition()
        v2 = mesh.getVertex(i2).getPosition()
    
        #make sure to cull edges belonging to the polygonal shape (select skeleton only)
        if v1 in polyverts and v2 in polyverts:
            continue
        
        edges.add((v1, v2))
        
    
    #### COMPUTE MITERED OFFSETS ####
    
    contours = []
    
    #Retrieve inner faces of the skeleton
    for i, face_id in enumerate(mesh.getFacesAsInt()):
        start_index = None
        face_verts = []

        #Get each vertex of visited face & store them
        for idx, n in enumerate(face_id):
            v = mesh.getVertex(n).getPosition()
            v = PVector(v.x, v.y) # convert to PVector
            face_verts.append(v)

            if v == verts[i]:
                start_index = idx
                
        #Re-order vertices & edges so they all start from the original polygon edges
        face_verts = face_verts[start_index:] + face_verts[:start_index]
        face_edges = zip(face_verts, face_verts[1:] + face_verts[:1])
        
        #Start-point & end-point of the 1st edge
        spt, ept = face_edges[0]

        #Convert 1st edge to ray & offset multiple times
        for gap in xrange(0, 100, 5):
            
            dir = PVector.sub(ept, spt).normalize()
            theta = dir.heading() + HALF_PI #angle perpendicular to the edge

            #Offset ray parallely
            vec = PVector.fromAngle(theta).setMag(gap)
            p1 = spt.copy().add(dir.mult(10000.0)).add(vec) #end-pt of ray
            p2 = spt.copy().add(dir.mult(-1.0)).add(vec) #start-pt of ray
                                
            #Look for intersections with the boundaries of current "inner" face
            intersections = []
            for p3, p4 in face_edges[1:]:
                X = intersect(p1, p2, p3, p4)
                if X:
                    intersections.append(X)
            
            #If any, store them grouped by 2
            if intersections: 
                it = iter(intersections)
                contours.extend(zip(it, it))

        
    #### RENDER ####
        
    #Polygonal shape
    pushStyle()
    strokeWeight(3)
    for v1, v2 in zip(verts, verts[1:] + verts[0:]):
        line(v1.x, v1.y, v2.x, v2.y)
    popStyle()

    #Straight-Skeleton
    for v1, v2 in edges:
        line(v1.x, v1.y, v2.x, v2.y)
        
    #Offsets
    for v1, v2 in contours:
        line(v1.x, v1.y, v2.x, v2.y)
    

    noLoop()

        
def intersect(p1, p2, p3, p4):
        
    '''Returns a PVector.
       Checks if 2 lines are intersecting. If so, returns location of intersection point'''
    
    uA = ((p4.x-p3.x)*(p1.y-p3.y) - (p4.y-p3.y)*(p1.x-p3.x)) / (((p4.y-p3.y)*(p2.x-p1.x) - (p4.x-p3.x)*(p2.y-p1.y)) + .01)
    uB = ((p2.x-p1.x)*(p1.y-p3.y) - (p2.y-p1.y)*(p1.x-p3.x)) / (((p4.y-p3.y)*(p2.x-p1.x) - (p4.x-p3.x)*(p2.y-p1.y)) + .01)
    
    if uA >= 0 and uA <= 1 and uB >= 0 and uB <= 1:
                                        
        secX = p1.x + (uA * (p2.x-p1.x))
        secY = p1.y + (uA * (p2.y-p1.y))
        
        return PVector(secX, secY)

It works fine but I'm wondering if there's a more appropriate way (a "Hemesh" way) of finding these offsets. Is there a class or function dedicated to this task somewhere ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions