Kukan Kogei -空間工芸-

Notes about my 3d printing artcrafts

Modeling Kagome weave pattern with sverchok

f:id:asahidari:20190818004219p:plain

The following article below describes the details of how to apply kagome weave pattern for 3d models. 

(PDF) Beyond the basket case: A principled approach to the modelling ...https://www.researchgate.net/.../328043391_Beyond_the_basket_case_A_pri...

In this post, following the paper, I try to make a simple kagome-weave-pattern model with Blender and its node editor, sverchok.

 

(This may currently work well only for the closed and symmetrical surfaces.)

 

At first, It needs to modify the original mesh and prepare it for applying the main algorithm. Connecting the center points of the mesh triangles to make dual meshes and truncating the vertices of the hexagons to make a mesh before the next procedure.

f:id:asahidari:20190818004928p:plain

Then, the next step is to divide the edges into linear elements for weaving, but I could not find the way to make edge loops easily with the sverchok nodes. So I wrote a small script to 'walk' the vertices, find the connected edges to the each points, and connecting two of them which has similar angles around the vertices (the script is at the bottom of this post). Before applying this scripted node, I made expanded and shrinked meshes to get the vertices of the 'upper' and 'lower' point lists.

f:id:asahidari:20190818011416p:plain

After that, you have to choose the vertices from the two lists interchangeably to make crossing points to model interlacing. You have already two lists of the vertices('upper' or 'lower') in the node editor, so I chose the vertices one after another from the two lists and integrated selected vertices into one list.

f:id:asahidari:20190818012213p:plain

 After identifing the weave-lines, these are converted to material strip which has some width. In this process, it seems difficult to generate the strip in the right way (the original article described) with Blender. Instead, I use some different ways to get the strip by using Blender's modifiers, though the strip may not accurately follow the original surface . For example, It seems easy to get the one line with 'Extdude-edge' modifier (and 'UV Connection' modifier will also worked fine).

f:id:asahidari:20190818095050p:plain

But when I tried to iterate the strip-generation with applying the 'range' node for eace lines, it did not work correctly, especially at the matrix node(, the matrix can not be applied individually, and currently I do not understand why this does not work properly). I could not get the right direction of the extrusion for each lines with this way. 

f:id:asahidari:20190818100427p:plain

Instead, I used 'Solidify' modifier node to get the strip width, and applied 'Fill holes' node to materialize the strip for the edges of the strip. It works correctly and the result seems to be fine. And though I do not try yet, it may be better if you use 'obj raycast' node to move verts by their normals onto original mesh. 

f:id:asahidari:20190818101610p:plain

Finally, I applied 'Subdivide' node with smoothing to get the final mesh.

f:id:asahidari:20190818101903p:plain

 In this modeling process, I probably do not use the best way to generate kagome pattern. Because it may not work correctly when using 'open' surface or different pattern meshes, and there may be more better and effective way. But this noding method can be the first step to model the kagome weave with Blender's sverchok, and I hope this will help someone who will try to make more complex kagome weave models.

 

 The script below is used for the 'Scripted' node in this noding process.

(Sorry for the ugly code...)

+linear_edges.py+

import numpy as np

import copy

from numpy import linalg as LA

 

# calc angles between two edges

def calcVectorAngle(verts=, e1=, e2=):

    angle = -1

    if len(verts) > 0 and len(e1) > 1 and len(e2) > 1:

        if len(verts) > max([e1[0], e1[1], e2[0], e2[1]]):

            v1 = np.array(verts[e1[1]]) - np.array(verts[e1[0]])

            v2 = np.array(verts[e2[1]] - np.array(verts[e2[0]]))

            i = np.inner(v1, v2)

            n = LA.norm(v1) * LA.norm(v2)

            c = i / n

            angle = np.rad2deg(np.arccos(np.clip(c, -1.0, 1.0)))     

 

    return angle 

 

# find an edge which angle is similar to the source

def findNextEdge(verts=, linked_edges=, vertex_index=0, source_edge=, angle_range=0.0):

 

        if (len(verts) == 0) or (len(linked_edges) < vertex_index) or (len(source_edge) != 2):

            return

        

        linked = linked_edges[vertex_index]

        index = linked.index(source_edge)

        

        targets =

        diffs =

        

        # find next edge

        for i, elem in enumerate(linked):

            if i != index:

                angle = calcVectorAngle(verts, source_edge, elem)

                diff = -1

                if angle != -1:

                    if abs(angle) < angle_range:

                        diff = abs(angle)

                    elif abs(angle - 180) < angle_range:

                        diff = abs(angle - 180)

                    elif abs(angle - 360) < angle_range:

                        diff = abs(angle - 360)

                if diff >= 0:

                    targets.append(elem)

                    diffs.append(diff)

                    

        if (len(diffs) == 1) and (len(targets) == 1):

            return targets[0]

        elif len(diffs) > 1:

            min_index = diffs.index(min(diffs))

            return targets[min_index]

        

        return

 

def sv_main(verts_in=, edges_in=, angle_range_in=0.0):

 

    in_sockets = [

        ['v', 'verts', verts_in],

        ['s', 'edges', edges_in],

        ['s', 'angle_range', angle_range_in]]

    # press Ctrl+I, look in console

 

    verts =

    edges =

    angle_range = 0.0

    out_verts =

    out_edges =

    

    if verts_in:

        verts = verts_in[0]

        

    if edges_in:

        edges = edges_in[0]

 

    linked_edges = [ for i in range(len(verts))]

        

    if angle_range_in:

        angle_range = angle_range_in

    

    # prepare linked_edges per verts

    for i in range(len(verts)):

        for e in edges:

            if e.count(i) > 0:

                linked_edges[i].append(e)

 

    # walk through edges

    walked = [False]*len(edges)

    loop_verts =

    i = 0

    prev_vertex = -1

        

    while True:

        

        if len(edges) == 0:

            break

        e = edges[i]

        if len(e) < 2:

            break;

 

        # check duplication        

        if walked[i]:

            out_verts.append(copy.deepcopy(loop_verts))

            loop_verts.clear()

            

            # prepare next loop

            if walked.count(False) == 0:

                break

            i = walked.index(False)

            e = edges[i]

            prev_vertex = -1

 

        # determine next direction        

        v = e[1] if (prev_vertex == -1 or prev_vertex == e[0]) else e[0]

        prev_vertex = v

        

        walked[i] = True

        loop_verts.append(verts[v])

        

        next_edge = findNextEdge(verts, linked_edges, v, e, angle_range)

        if len(next_edge) > 0:

            # continue to walk current loop

            i = edges.index(next_edge)

        elif walked.count(False) > 0:

            # begin to walk another loop

            i = walked.index(False)

        else:

            # walked all verts

            continue

 

    # generate new loop edges

    for i, vi in enumerate(out_verts):

        es = []

        for j in range(len(vi)):

            e = [j, (j + 1) if (j < len(vi) - 1) else 0]

            es.append(e)

        out_edges.append(es)

 

    out_sockets = [['s', 'verts', [out_verts]],

                ['s', 'edges', [out_edges]]]

 

    return in_sockets, out_sockets

 

References:

 

Environment: