[Addon] Edge Roundifier - mesh modeling tool for building arcs on selected edges

here the version of today (23-9-14)
Install as usual
Add e.g. a Plane, ONE edge should be selected at least of any MESH-object, the active MESH is used …
Look in the tools for the TAB with PKHG ( :wink: ) and activate
Play with the parameters …(Operators part)
The view3d will become OBJECT mode change to EDIT mode and remove your start MESH e.g. to see the created bows alone
Each bow will be a single part, if using 180 degrees or 360 degrees you may remove double vertices to connect the bows to the
starting mesh.
Pay attention: if you use “nr of steps” == 1 , then you may not see anything, because it will probably by just an edge above its created edge (the other two parameters standard 0 and 180) check! You wil get 4 not connected edges if you used a Plane, removing doubles the result is so to say the Plane where the face (only) is deleted …
So WITH nr of step == 1 , change elliptic factor and use e.g. angle = 90 to see what happens!

These two parameters may be negative … try to see the effect!

Use Pietrs examples to build some nice objects … :wink: (extrude and solidify e.g.) Have fun!


# ***** BEGIN GPL LICENSE BLOCK *****
#
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENCE BLOCK *****

#22-sep-2014 start change (after prelaminary reseach)<

bl_info = {
    "name": "PKHG Edge Roundifier",
    "category": "Mesh",
    'author': 'idea Piotr Komisarczyk other implementation PKHG',
    'version': (0, 0, 3),
    'blender': (2, 7, 1),
    'location': 'PKHG tab in tools menu or SPACE > type then 03R',
    'description': 'Mesh editing script allowing edge rounding',
    'wiki_url': '',
    'tracker_url': '',
    'category': 'Mesh'
}


#========= new =========
import bpy
import bmesh
from bpy.props import FloatProperty, IntProperty, StringProperty
from math import copysign, pi, radians, degrees, atan, acos, asin
from mathutils import Vector, Matrix

#PKHG>INFO add or change yourself ;-)
DBG_info ={"axis before":False,
           "axis after":False,
          "show_angle":False,
           "geom_last":False,
           "edges_used":False,
           "heights":False,
       }

 
def extra_DBG_info(name = "value from DBG_info", info_text="default
", info_obj=None):
    global DBG_info
    DBG_keys = DBG_info.keys()
    if name in DBG_keys:
        if DBG_info[name]:
            print("
DBG(see dict.!) >>> " + info_text, info_obj)
#PKHG>INFO my way of debugging (helpers)



###################################################################################

class View3DPanel():
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_category = "PKHG"   #PKHG>INFO if removed, panel will be in all tools menus

    @classmethod
    def poll(cls, context):
        return (context.object is not None and context.object.type == "MESH")


class PanelOne(View3DPanel, bpy.types.Panel):
    bl_idname = "VIEW3D_PT_pkhgroundifier"
    bl_label = "Roundifier"

    def draw(self, context):
        self.layout.label("select (some)")
        self.layout.label("edges of a MESH")
        self.layout.operator(NewEdgeRoundifier.bl_idname,"roundify")
        self.layout.label("see op. parameters")
bpy.utils.register_class(PanelOne)

"""
class PanelTwo(View3DPanel, bpy.types.Panel):
    bl_idname = "VIEW3D_PT_test_2"
    bl_label = "Panel Two"

    def draw(self, context):
        self.layout.label("Also Small Class")
bpy.utils.register_class(PanelTwo)
"""


class NewEdgeRoundifier(bpy.types.Operator):
    """New Edge Roundifier"""
    bl_idname = "mesh.new_edge_roundifier"
    bl_label = "03Roundifier"
    bl_options =  {'REGISTER', 'UNDO', 'PRESET' }
    
    n = IntProperty(name = 'nr of steps', default = 4, min = 1, max = 100, step = 1,
                    description = "number of vertices of a bow , first vertex not counted")
    factor = FloatProperty(name = "elliptic factor", default = 0, min = -20, max = 20,
                           description = "to make elliptic bows")
    angle = FloatProperty(name = "angle", default = 180, min = -360, max = 360, step = 5,\
                          description = "angle beteween first and last vertex")

    def draw(self, context):
        layout = self.layout
        column = layout.column()
        column.label("TODO")
        column.prop(self, "n")
        column.prop(self, "factor")
        column.prop(self, "angle")
        

    def spin_one_edge(self, bm = None, edge_index = 0, steps = 4, dvec = (0,0,0),\
                      angle = 180, factor = 0, object = None):
        
        space = object.matrix_parent_inverse
        #space = object.matrix_world #PKHG>??? I think don't
        this_edge = bm.edges[edge_index]
        v0 = this_edge.verts[0]
        v0co = v0.co
        v1 = this_edge.verts[1]
        midpoint = (v0co + v1.co) * 0.5
        loops = this_edge.link_loops
        direction = None

        #PKHG>INFO if there is a loop, then the edge is part of a real face
        if len(loops) > 0:
            #faces_adjacent = []
            #face_normals = []
            #axis = Vector()
            #for loop in loops:
            loop = loops[0]
            face = loop.face
            #faces_adjacent.append(face)
            #face_normals.append(face.normal)
            #axis += face.normal
            axis = face.normal
        else:
            direction = v1.co - v0.co
            axis = direction.cross(v1.co)

            #PKHG>INFO enable or disable in the dictionary above!
            extra_DBG_info("axis before", "axis before", axis)            
            if axis == Vector((0,0,0)):
                eps = 0.0000000000000000000000000001 #PKHG>??? a parameter?
                not_zero = [el for el in  map(lambda el: 0 if abs(el) < eps  else 1, direction)]
                nr_nz = sum(not_zero)
                if nr_nz == 3:
                    axis = Vector((direction[1],-direction[0],0)) #PKHG>INfO surely orthogonal!
                else:
                    extra_DBG_info("axis after", "axis axis", axis)
        offset = 0

        #PKHG>INFO for symmetrize shorter bow ;-)
        if abs(angle) <= 180:
            offset = copysign(180 - abs(angle), angle) * 0.5 
        else:
            offset = - copysign(180 - abs(angle), angle) * 0.5 
  
        tmp = angle / steps 
        angle = offset
        res_list = []
               
        for tel in range(steps + 1):
            #PKHG>INFO rotate start vertex
            result= bmesh.ops.spin(bm, geom=[v0], cent = midpoint, axis = axis, dvec = dvec,\
                                   angle = radians(angle), space = space,\
                                   steps = 1, use_duplicate = True)

            extra_DBG_info("geom_last", "geom_last =", result['geom_last'][0].co)
            res_list.extend(result['geom_last'])            #PKHG>INFO list of BMVerts
            #this list to be 'adjusted'??!! 3D triangle v0, v1 , vert[mid] (verts>=3)
            extra_DBG_info("show_angle", "angle used", angle)
            angle  +=  tmp 
        v0co = v0.co
        v1co = v1.co

        #PKHG>INFO first vert is always a corner point
        for nr in range(len(res_list)):
            #PKHg>INFO compute the base on the edge  of the height-vector
            top = res_list[nr].co
            t = (v1co - v0co).dot(top - v0co)/(v1co - v0co).length ** 2
            h_bottom = v0co + t*(v1co-v0co)
            height = (h_bottom - top )
            extra_DBG_info("heights", "heigts etc.",("nr t", nr,  t, h_bottom, height.length))
            res_list[nr].co = top + factor * height
        
        #PKHG>INFO make now edges from the bmverts
        for nr in range(steps):
            bm.edges.new([res_list[nr],res_list[nr + 1]])
            res_list.append(space*midpoint)
        return res_list

    def execute(self, context):
        #PKHG>INFO selected edge(s) of a MESH
        cu = bpy.context.scene.objects.active
        bpy.ops.object.mode_set(mode = 'EDIT')
        space = cu.matrix_parent_inverse
        bm = bmesh.from_edit_mesh(cu.data)
        selected_edge_indices = [ele.index for ele in bm.edges if ele.select]
        extra_DBG_info("edges_used","edges used --> ", selected_edge_indices)

        list_verts = []
        aaa = None
        steps = self.n
        factor = self.factor
        angle = self.angle
            
        for ii in selected_edge_indices:
            aaa = self.spin_one_edge(bm = bm, edge_index = ii, angle = angle,\
                                     steps = steps, factor = factor, object = cu)
        bpy.ops.object.mode_set(mode = 'OBJECT')
        bm.free()   #PKHG>INFO to not become confused (error)
        return {'FINISHED'}

    @classmethod
    def poll(cls, context):
        return (context.scene.objects.active.type == 'MESH')

def draw_item(self, context):
    self.layout.operator_context = 'INVOKE_DEFAULT'
    self.layout.operator('mesh.new_edge_roundifier')

def register():
    bpy.utils.register_class(NewEdgeRoundifier)
    bpy.types.VIEW3D_MT_edit_mesh_edges.append(draw_item)

def unregister():
    bpy.utils.unregister_class(NewEdgeRoundifier)
    bpy.types.VIEW3D_MT_edit_mesh_edges.remove(draw_item)

if __name__ == "__main__":
    register()


@red2blue: You actually can change the speed of video playback on Youtube.
Try going to this site: http://www.youtube.com/html5 and check all checkboxes. Then navigate to a movie on youtube. Then in settings next to resolution you will also find speed. So if you want you can run it in 0.5 or 0.25 speed and mute the sound. That way you will be able to follow the steps easily.

@PKHG I guess you can publish your own version of the addon but maybe it would be better to join forces and make something together - I mean extend the Edge Roundifier further :slight_smile: As I said I will go back to working on it at the beginning of October. and as for your question about rotating the arcs:
I would try something like this: Find vector between the two vertices - and this is your axis. Then somehow :slight_smile: you need to tranform this vector into a rotation matrix.
One more thing that comes to my mind is that Blender has an option of creating new transform orientation. You can select the edge and hit CTRL-ALT-SPACE and name it. Maybe you could find a way to do it using python…?
I would also use the center between the two vertices as the rotation center (you can move the 3Dcursor there)

Check these links and hopefully they will help a bit:



Cheers.

OK no problem … let us work together in october :wink:
The rotation of bow’s is I think soon solved too … to some extent at least , one should state what and when rotate something
Rotation axis will be the line through the end-points of the bow … thats clear …

Let us discuss this in october further, please! As well of what you/we will accomplish …

nice links to study!

Hi,

didn’t thought about that :). Will give it a try. Thanks :wink:

Just spent about 30 minutes watching your video and practicing with the Edge Roundifier script. It’s one of those ‘How did I get along without it’ moments. One suggestion: could you have it add itself to the ‘Addons’ tab by default?

Oh, the rotation around an give axis will probably easy solvable using Quaternions (Euler?) …

Nearly, I am getting strange inbetween results, meaning I do not know the whole story:
Try this:


import bpy
import bmesh
from math import pi, radians, atan, degrees
from mathutils import Vector, Matrix, Quaternion, Euler 

#Make a standard Plane available!!!
plane = bpy.data.objects['Plane']
bpy.ops.object.mode_set(mode = 'EDIT')
bm = bmesh.from_edit_mesh(plane.data)
inds = [el.index for el in bm.verts]
print(inds)

verts = bm.verts[:]
#PKHF>INFO : tow adjacent verts as rotation axis!
v0 = bm.verts[0].co
v1 = bm.verts[1].co
axis = (v1-v0).normalized()
#print(v0,v1)

a_quat = Quaternion(axis, radians(60)).normalized();
print("the quaternion", a_quat)
a_mat = a_quat.to_matrix()
#print("%.2f, %.2f, %.2f" % tuple(degrees(a) for a in a_quat.to_euler()))
print("(%.2f, %.2f, %.2f), %.2f"    % (a_quat.axis[:] + (degrees(a_quat.angle),)))

#see what happens.... strange inbetween results?! Explanation?
for i in [2,3]: #range(len(bm.verts)):
    bm.verts[i].co = a_mat* bm.verts[i].co


Downlaod and/or have a file with an addon at some know place.
In the addon tab use use “Install from File” , activate the just installed addon and use “Save User Settings” …

Is this what you wanted?

I was just noticing the addon makes a new tab named PKHG. Maybe it could go into the existing ‘Addon’ tab. I know I can change that line in the py code myself. And thanks for the contribution to this great script.

as you may see, there will be a combined effort (october) for something (even) better addon … my coded are only to show what I am trying to understand and build …

I understand. I’m looking forward to your combined effort on this.

I think i finally understand what a quaternion does …
a_mat = Quaternion(axis, radians(45)).normalized().to_matrix()

The object should be moved to the origin , the axis goes through the origin too
so you have to move all vertices to be rotated (edit mode) such that one of the verts defining the axis is too in the origin, now apply the rotation

Who confirms this interpretation of rotate some thing around an edge?

EDIT:
No, somewhat more complicated ;-( … nearly …
Sometimes I get what I want, and somtimes the rotation behaves strange …

But when it works I can rotate the newly built edges (the rounded edge so to say) around its axis.
A Plane at the border divided differently, then on the four sides playing with the parameters … afterwards making faces :frowning: a filled bow)
and extruding gave the picture below :wink:

==> workd to do to understand fully.


Rotation problem solved
See http://pkhg.nl/COCwithWWW/uploads/Roundifier/roundifier_06.ogv

Done via shapekeys (from geodesic domes imported) … (not the rotation, the ‘film’ is meant :

Like so: First make the roundify (a bit squashed in this example) and make the same object but not with rotated roundify … THEN the shapekey addon combines the two objects so to say: one basis and one key
such that an animiation can be created by using the shapekey value and insert keyframe :wink:

Hi all
I am pleased to announce that I have finished Edge Roundifier v 0.0.2. It includes suggestions proposed by PKHG.
You can download it from here:

The main thing added is the support of 3 working planes. This allows to use the addon in FRONT, SIDE, or TOP view.
Note that all calculations are done in LOCAL coordinate system.
It also fixes the math domain error related to acos and cos that some of users reported.

If you find any issues/errors with the script let me know. I will continue working on this addon to include some more cool features. First I want to integrate PKHG’s script with this one.
So in the nearest future I want to add the ability to rotate arcs around edges and to create elliptic arcs.

Hope you like it
Cheers :slight_smile:

The yesterday-version of komi3d contain a lot of not needed print statements (occurring in the console). Needed for komi3D but not in production search the code for " print(" and replace by " #print(" :wink: (or even deleting …)

Sent to komi3d a message that using Vectors would be smarter, so I suppose that you should look here for an updated version …
komi3D is a much more busy man than PKHG (old man, retired :wink: )

By the way komi3D refers to something like this http://pkhg.nl/COCwithWWW//uploads/Roundifier/roundifier_2.gif

i need this to be in sverchok… omg. it is cool feature

“Adaptive edges” is almost fine. we just need a way to turn them in the right direction.


Here the link to my version: http://pkhg.nl/COCwithWWW/pmwiki.php/Roundifier/SourceVersion11
password for this page with (two) sources (take the first one!)
version11
at the end of the source there is a button to display and then get the raw version for copy paste!

Hi

I am really sorry guys for posting a version with prints in it… I had some issues with Github and it I had to revert to a version that did not have prints removed… I also noticed that because of that version 0.0.2 had some performance issues.

Anyway today I am happy to release a new version of Edge Roundifier 0.0.3. It has 2 small but extremely helpful features added. One is remove edges which allows removing the originally selected edges. The other is both sides. This basically calls edgerounidifier twice - for standard and inverted arc. Here is an example of what you can get with one call:
[ATTACH=CONFIG]339648[/ATTACH]

One more tip here. Remember to select all vertices after the script run and remove doubles manually. The bothSides function may add some more vertices. The remove doubles in the script sometimes does not remove all doubles.

Here is also a quick screenshot of what the working planes buttons do (XY, YZ, XZ) (this was included in ver 0.0.2):


You can download the script from here:

@nikitron: I am also interested in seeing this script in Sverchok :slight_smile: I don’t really know Sverchok yet but from what I saw it really is a powerful tool and I would like to learn it :slight_smile: As PKHG said I am a busy man… :slight_smile: Anyway I want to polish this script even more and add some more cool features to it. There are still many things that can be done better… If you can let me know how this script could be used inside Sverchok? Are there any guidelines??
I am going to the Blender Conference in a few days and I know there is gonna be a presentation on Sverchok that I really want to see :slight_smile:

Cheers
komi3D

Hi all

Lately Blender 2.73 arrived. And unfortunately Edge Roundifier got broken.
Here is a quick fix.

If you still find any issues please report it in this thread.

Cheers:)

Hopefully in the near future I can further develop this addon…

Thanks, very cool job!