nortikin / sverchok

Sverchok
http://nortikin.github.io/sverchok/
GNU General Public License v3.0
2.26k stars 234 forks source link

"Single stroke" WIP image processing. #509

Closed ghost closed 9 years ago

ghost commented 9 years ago

I am trying to replicate my Grasshopper algorithm on Sverchock https://www.behance.net/gallery/Single-Stroke-mimicry/14936449 And here what i managed to achieve yet: 1) I tried to use @zeffii 's SN code to create the spiral , but it i needed more control on the parameters so i manipulated a little and wrote this :

import math

def sv_main(radius=1,turns=1, smth=4):
    verts_out = []
    edges_out = []

    in_sockets = [
        ['s', 'radius', radius],
        ['s', 'turns', turns],
        ['s', 'smooth', smth]
    ]
    out_sockets = [
        ['v', 'Verts', verts_out]
    ]

    smth = abs(turns*smth*8) #least subdivision = 8
    Htheta = turns*360 #total angle
    av = verts_out.append

    for i in range(0,smth+1):
        theta = math.radians(i*Htheta/smth)
        av((math.cos(theta)*theta*radius/(turns*6.28), math.sin(theta)*theta*radius/(turns*6.28), 0.0))    

    return in_sockets, out_sockets

To create the spiral with controlling the exact Radius, No of Turns and the Smoothness :

screenshot from 2014-12-25 15 33 32

I have desperately tried to output the edges as well but never succeeded, so i surrendered and just used an external Line Node :\ .

2) My spiral SN node have one problem that the vertices are not evenly distributed on the curve so i needed to Rebuild it (as in rhino and grasshopper rebuild commands) :

screenshot from 2014-12-25 15 41 24

3) Now we have an evenly distributed vertices that need to be Offseted in both directions. This was too hard but here how i managed to do the first Offset.

screenshot from 2014-12-25 15 48 30

Basically what i needed to do is rotating every vertex 90 degrees around the previous vertex to create a vector perpendicular to the spiral at this point, to use it as moving vector. I found the Lists node too complicated but i managed to do it with trial and error.

Now i need to : A- control the offset distance for every vertex and B- manage to connect vertices with edges and C- Loft the two spirals to create the stroke width, may be with UVconnect node

But i think these will be too difficult after what i have experienced with lists and data nesting. i need to have some break and restudy the manual again to understand how this works ! Or may be i have to study Blender scripting to be more aware of what Sverchock does.

zeffii commented 9 years ago

I don't have much time today to respond to this, but will show how to do the edges, it is usually a missing [ ] around the list, to inform sverchok about the level of nestedness (nestedness is on the table for being solved.. we talk about it often)

import math

TWO_PI = 2*math.pi

def sv_main(radius=1, turns=1, smth=4):
    verts_out = []
    edges_out = []

    in_sockets = [
        ['s', 'radius', radius],
        ['s', 'turns', turns],
        ['s', 'smooth', smth]
    ]
    out_sockets = [
        ['v', 'Verts', [verts_out]],   # extra wrapping
        ['s', 'Edges', [edges_out]]
    ]

    smth = abs(turns*smth*8) #least subdivision = 8
    Htheta = turns*360 #total angle
    av = verts_out.append

    for i in range(0,smth+1):
        theta = math.radians(i*Htheta/smth)
        gamma = theta*radius/(turns*TWO_PI)
        av((math.cos(theta)*gamma, math.sin(theta)*gamma, 0.0))

    ae = edges_out.append
    num_verts = len(verts_out)
    for i in range(num_verts-1):
        ae([i,(i+1)])

    return in_sockets, out_sockets

My suggested previous script kept the distance between consecutive next verts approximately the same (Evenly distributed - depending on the slider settings). But anyway, this is how to do edges for a non closed entity

ghost commented 9 years ago

Great, thanks for your help, i'll try to proceed from here.

zeffii commented 9 years ago

image a slightly different effect using my spiral script and the image decomp node and a SN script that modifies the size of the dupliObject (icosphere) on the Particle system attached to the spiral., for every point on the spiral, it tracks the nearest pixel on the image, and gets the z distance

zeffii commented 9 years ago

I've experimented now and had difficulty generating a single polygon (to simulate the spiral paintbrush stroke so-to-say) of so many vertices.. Blender may even crash.

image

probably need to make sure that the edges of the polygon never cross over too ahwell, good fun anyway

ghost commented 9 years ago

Wow this is great, i wish i get familiar enough with Sverchock to achieve similar results, would you like to share the node tree screenshots?

zeffii commented 9 years ago

https://www.dropbox.com/s/b0ok3fobnya40nb/sprial_from_img_no_particles2.blend?dl=0

you may have to press 'load' on the image

zeffii commented 9 years ago

actually, here's a fixed version which does bake correctly. When baked you can hit F with the sv_(number) mesh in edit more and it will make the polygon :) https://www.dropbox.com/s/b0ok3fobnya40nb/sprial_from_img_no_particles2.blend?dl=0

image

import mathutils
from mathutils import Vector, kdtree

def sv_main(image_verts=[[]], spiral_verts=[[]], reamp=-0.3, maxdist=0.7, rescale=1.0):

    gloc = []
    edges_out = []

    in_sockets = [
        ['v', 'image_verts', image_verts],
        ['v', 'spiral_verts', spiral_verts],
        ['s', 'reamp', reamp],
        ['s', 'maxdist', maxdist],
        ['s', 'rescale', rescale]
    ]

    out_sockets = [
        ['v', 'locations', [gloc]],
        ['s', 'edges', [edges_out]]
    ]

    print('point 1')

    # A little boilerplate is needed 
    if (not image_verts) or (not image_verts[0]):
        return in_sockets, out_sockets
    else:
        v = image_verts[0]

    if (not spiral_verts) or (not spiral_verts[0]):
        return in_sockets, out_sockets
    else:
        sv = spiral_verts[0]

    print('point 4')

    # KD BUILD
    size = len(v)
    kd = kdtree.KDTree(size)

    for i, vtx in enumerate(v):
        kd.insert(Vector(vtx), i)
    kd.balance()

    side_left = []
    side_right = []
    sidel_add = side_left.append
    sider_add = side_right.append

    # PARTICLE LOOP
    for i in range(len(sv)):
        pt = Vector(sv[i])
        for (co, index, dist) in kd.find_range(pt, maxdist):
            z1 = pt.z
            z2 = co.z
            size = ((z1 - z2) * reamp) / 2
            dtc = Vector(pt).length   # distance to center
            if dtc == 0:
                dtc += 0.01
            size1 = dtc - size
            size2 = dtc + size
            amp1 = (size1 / dtc) * rescale
            amp2 = (size2 / dtc) * rescale

            v1 = Vector((0,0,0)).lerp(pt, amp1)
            v2 = Vector((0,0,0)).lerp(pt, amp2)
            sidel_add(v1[:])
            sider_add(v2[:])

            # stop at first find.
            break

    gloc.extend(side_left)
    gloc.extend(side_right[::-1])

    ae = edges_out.append
    num_verts = len(gloc)
    for i in range(num_verts-1):
        ae([i,(i+1)])
    ae([num_verts-1,0])

    print(num_verts)
    print(edges_out[-1])

    return in_sockets, out_sockets
nortikin commented 9 years ago

wow. uvconnect node can do polygons anyway, but needed two equal count of vertices, so list of inner and list of outer vertices chain. also we can remake pipe tube node to handle various thickness of pipe. such way we can manipulate it on layout IMHO

zeffii commented 9 years ago

I explored a bit further since those posts, but it's busy around here at home :).

this outputs a polygon (replaces and reuses the edges list, because lazy)

    gloc.extend(side_left)
    gloc.extend(side_right[::-1])

    ae = edges_out.extend
    num_verts = len(gloc)
    for i in range(num_verts-1):
        ae([i])

i'd need to write a more stable spiral / coil routine to get better output, with fine control over distance between points and delta radius for consecutive arms of the spiral. But am not so interested anymore

zeffii commented 9 years ago
import math

def spiral_points(scale, arc=1, separation=1):
    """
    lifted directly from: 
    http://stackoverflow.com/a/27528612/1243487 
    by user: liborm

    generate points on an Archimedes' spiral
    with `arc` giving the length of arc between two points
    and `separation` giving the distance between consecutive 
    turnings
    - approximate arc length with circle arc at given distance
    - use a spiral equation r = b * phi
    """
    def p2c(r, phi):
        """polar to cartesian
        """
        return (scale * r * math.cos(phi), scale * r * math.sin(phi), 0)

    # yield a point at origin
    # not using the origin may give better results
    yield (0, 0, 0) 

    # initialize the next point in the required distance
    r = arc
    b = separation / (2 * math.pi)
    # find the first phi to satisfy distance of `arc` to the second point
    phi = float(r) / b
    while True:
        yield p2c(r, phi)
        # advance the variables
        # calculate phi that will give desired arc length at current radius
        # (approximating with circle)
        phi += float(arc) / r
        r = b * phi

def sv_main(arc=1, separation=3, num_points=5000, scale=0.13):
    points = []

    in_sockets = [
        ['s', 'arc', arc],
        ['s', 'separation', separation],
        ['s', 'num_points', num_points],
        ['s', 'scale', scale]
    ]

    out_sockets = [
        ['v', 'verts', [points]]
    ]

    point_gen = spiral_points(scale, arc, separation)

    for i in range(num_points):
        points.append(next(point_gen))

    return in_sockets, out_sockets

image

zeffii commented 9 years ago

https://www.dropbox.com/s/op096voyamg2vrc/sprial_from_img_no_particles5.blend?dl=0

enzyme69 commented 7 years ago

Aah... I can simplify this simply by using Zeffi Spiral and Blender Displace Modifier 💃