nortikin / sverchok

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

Triangulate invents faces. #595

Closed snovvfall closed 8 years ago

snovvfall commented 9 years ago

Ok, this problem makes me crazy. Spent three days trying to solve it with some sort of a workaround, but succeeded only by chance with some meshes. Problem is I cannot import normal mesh with or without triangulate modifier from Blender to Sverchok. In my case I need to import closed ngon consisting of only edges and make it a face right inside Sverchok. But "Fill Holes" returns incorrect result. On the picture below the figure should be with only vertical and horizontal lines, "L"-shaped. sverchok fill holes

snovvfall commented 9 years ago

Example file: https://drive.google.com/file/d/0B3S8JZs3SIP4MmdPM3dUVWhBM3M/view?usp=sharing

snovvfall commented 9 years ago

I made more or less stable and predictable solution. sverchok fill holes solution But this is terrible. And it only works for meshes made of vertices with Z coordinate 0. The Script node removes faces which have vertices with Z = 0. But it should be fine for the project I need all this madness for. See the magic: https://drive.google.com/file/d/0B3S8JZs3SIP4c1MxdHd1WTVjUGM/view?usp=sharing Blender itself should have good algorithm for dealing with this kind of situations, but I need faces to be created in Sverchok.

nortikin commented 9 years ago

so, it was in plans - to create node to make mesh, but in blender there is no such command, you cannot make polygons in complex meshes. task of meshing point cloud this is: http://download.autodesk.com/global/docs/softimage2013/en_us/userguide/index.html?url=files/poly_topomod_MeshingPointCloudsandOtherThings.htm,topicNumber=d30e123376

nortikin commented 9 years ago

what did i found is http://blenderartists.org/forum/showthread.php?241950-A-Script-to-Skin-a-Point-Cloud-%28for-Blender-2-6x-or-Later%29

nortikin commented 9 years ago

but it takes ugly result because of ignoring edges, so current approach has to be improved or to find something better.

ly29 commented 9 years ago

Fill Hole works, the bug is actually in triangulate. That the the output from fill hole shows incorrectly is due to that the option ngon tesselate has to be set to show concave n-gons correctly. This perhaps should be on by default @zeffii can fill explain that I guess. http://nikitron.cc.ua/sverch/html/nodes/viz/viewer_draw.html skarmavbild 2015-03-20 kl 10 30 02

ly29 commented 9 years ago

However I can't replicate it using the console so it hard to make a proper bug report since it doesn't seem like a bmesh bug The below works like it should. Which is what the above nodes should do (with a lot of extra work.) With Plane in edit mode

bm=bmesh.from_edit_mesh(D.meshes['Plane'])
bmesh.ops.holes_fill(bm, edges=bm.edges[:])
bmesh.ops.triangulate(bm, faces=bm.faces[:])
bmesh.update_edit_mesh(D.meshes['Plane'])
zeffii commented 9 years ago

I had time to look at it, and this scenario is exactly why ngon tesselation feature was added to the side panel options (draw_buttons_ext) of the ViewerDrawMK2 node. NGon tesselation was a way to get around the issue of drawing complex irregular polygons. Something we don't need very often, but annoying when we couldn't draw them correctly.

image

image

If my memory serves me correctly, the reason ngon-tesselation is not on by default is because it's extra computation that only needs to happen in special cases. The fact that I (or most of the team) almost never need to use it, is a good indication that this is a sane default.

ly29 commented 9 years ago

The error that appears is after the triangulate node and is a very strange error.

The error was misdiagnosed as being caused by Fill Holes due to the rendering issue With concave. The real error in this cases is the triangulate node that does something very strange that does not seem to be happen when using the bmesh module directly in the console for the same geometry.

zeffii commented 9 years ago

Suggesting a difference between the bmesh.ops.triangulate (on a collection of polygons) and mathutils.geometry.tessellate_polygon (on per polygon base, as this is used by ViewerDrawMK2)

i don't know :)

ly29 commented 9 years ago

skarmavbild 2015-03-22 kl 19 54 37 Original, from fill hole, and from triangulate.

Something goes wrong in triangulate. Don't know what though.

ly29 commented 9 years ago

skarmavbild 2015-03-23 kl 10 16 09 This is the input geometry.

BMesh example, everything works correctly.

import bmesh
bm = bmesh.new()
bm.from_mesh(bpy.data.meshes["Plane"])
bmesh.ops.holes_fill(bm, edges=bm.edges)
bmesh.ops.triangulate(bm, faces=bm.faces)
print([tuple(v.index for v in face.verts) for face in bm.faces])

--> [(4, 2, 3), (4, 0, 1), (4, 3, 5), (5, 0, 4)]

Which is the correct result.

However Triangulate node produces this however.

[(1, 2, 5), (1, 4, 2), (2, 3, 5), (5, 0, 1)]

skarmavbild 2015-03-23 kl 10 22 48 Looking at the code I don't get what produces this result.

portnov commented 9 years ago

@ly29 , could you try bmesh.ops.triangulate(bm, verts=bm.verts, edges=bm.edges faces=bm.faces) (as it is written in triangulate.py) will it work correct?

ly29 commented 9 years ago

I tried some different things with triangulate.py, but it looks like this, no?

            res = bmesh.ops.triangulate(bm, faces=b_faces,
                            quad_method=int(self.quad_mode),
                            ngon_method=int(self.ngon_mode))

https://github.com/nortikin/sverchok/blob/master/nodes/modifier_change/triangulate.py#L102-L105

zeffii commented 9 years ago

this is quite the bug @snovvfall , nice catch.

snovvfall commented 9 years ago

I confirm that this is Triangulate node which causes this issue. Viewer node ngons tesselation option returns the same result as Bmesh Viewer node, so it seems Bmesh Viewer node has tesselation always turned on or something like that. But I need to use Triangulate node sometimes to help me solve problems with CSG Boolean node. The headache got real when I was working on a project to calculate the area of ceiling inside rooms. Here is how monster tree looks now: sverchok fill holes calculation 1) I create columns, walls, beams (hidden on the image), doors and windows. In each room I draw 2 lines on the floor level: one along monolith walls and columns and one along other walls (separators, thin, non-bearing). 2) Then I import these two objects, beams and objects responsible for doors and windows boolean. 3) Extrude both of them up, solidify just a tiny bit (0.02 mm) to make boolean work correctly, cut windows, doors and beams from them with boolean, calculate the area, divide the result by 2 to negate solidify influence and output the result as purple and red numbers on the image below. 4) Join these two objects into one, remove doubles (is there a single node to do all this by the way?). Remove all vertices except those on floor level Z = 0, remove doubles 1 cm, calculate edge length, output as green number. 5) Join two objects from the very beginning. Do the weird trick with fill holes I described in my previous comment in this discussion to prevent incorrect triangles, calculate the area, output as blue number. Also, IMPORTANT, the "RECALC NORMALS" node saves the day and greatly helps to prevent incorrect triangles in the future, see my previous comment. 6) Extrude up, cut beams (the most glitchy part here), remove vertices whose Z = 0, calculate the area and output as yellow number. sverchok fill holes calculation 2

zeffii commented 9 years ago

I would like to revisit this node, and perhaps include a safety-force option.

zeffii commented 8 years ago

was this ever resolved or what?

ly29 commented 8 years ago

Not as far as I know

zeffii commented 8 years ago

Interestingly, and i'm not sure what this means:

image

vs

image

zeffii commented 8 years ago

we can offer a slightly heavier homegrown triangluate much like what viewerDraw does, and call it HeavyTriangulate.

zeffii commented 8 years ago

image

ly29 commented 8 years ago

Hmmm, now I can't get to misbehave.

ly29 commented 8 years ago

Okay now I get it (and remember), n-gons misbehave. But only via the node, not when using the bmesh interface itself.

ly29 commented 8 years ago

it is almost like it ignores the faces for some reason and just triangulates the vertices....

zeffii commented 8 years ago

image

zeffii commented 8 years ago
# ##### 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 LICENSE BLOCK #####

import bpy
from bpy.props import IntProperty, EnumProperty, BoolProperty, FloatProperty
import mathutils
from mathutils.geometry import tessellate_polygon as tessellate

from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, match_long_repeat, fullList
from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata, pydata_from_bmesh

class SvHeavyTriangulateNode(bpy.types.Node, SverchCustomTreeNode):
    ''' Triangulate mesh (Heavy)'''
    bl_idname = 'SvHeavyTriangulateNode'
    bl_label = 'Heavy Triangulate'
    bl_icon = 'OUTLINER_OB_EMPTY'

    def sv_init(self, context):
        self.inputs.new('VerticesSocket', "Vertices", "Vertices")
        self.inputs.new('StringsSocket', 'Polygons', 'Polygons')

        self.outputs.new('VerticesSocket', 'Vertices')
        self.outputs.new('StringsSocket', 'Edges')
        self.outputs.new('StringsSocket', 'Polygons')

    def process(self):

        inputs = self.inputs
        outputs = self.outputs

        if not (inputs['Vertices'].is_linked and inputs['Polygons'].is_linked):
            return

        named = ['Vertices', 'Edges', 'Polygons']
        if not (any(outputs[name].is_linked for name in named)):
            return

        vertices_s = inputs['Vertices'].sv_get(default=[[]])
        faces_s = inputs['Polygons'].sv_get(default=[[]])

        result_vertices = []
        result_edges = []
        result_faces = []

        meshes = match_long_repeat([vertices_s, faces_s])

        for vertices, faces in zip(*meshes):

            bm = bmesh_from_pydata(vertices, [], faces)

            new_edges = []
            new_faces = []

            for f in bm.faces:
                coords = [v.co for v in f.verts]
                indices = [v.index for v in f.verts]

                if len(coords) > 3:
                    for pol in tessellate([coords]):
                        new_faces.append([indices[i] for i in pol])
                else:
                    new_faces.append([v.index for v in f])

            result_vertices.append([v.co[:] for v in bm.verts])
            result_edges.append(new_edges)
            result_faces.append(new_faces)

        output_list = [
            ['Vertices', result_vertices],
            ['Edges', result_edges],
            ['Polygons', result_faces]
        ]

        for output_name, output_data in output_list:
            if outputs[output_name].is_linked:
                outputs[output_name].sv_set(output_data)

def register():
    bpy.utils.register_class(SvHeavyTriangulateNode)

def unregister():
    bpy.utils.unregister_class(SvHeavyTriangulateNode)
zeffii commented 8 years ago

This might be a bug in bmesh.ops.triangulate? none of the return geom seems sane.

ly29 commented 8 years ago

Something goes wrong but if you call the op directly it seems to work as expected, if called from the python console.

ly29 commented 8 years ago

If you can replicate it outside of node code there it is a bug with bmesh.ops.triangulate, otherwise there is something we are missing. I think it is the latter.

zeffii commented 8 years ago

the difference between running from console and in a node is the console kind of requires there to be a Mesh structure, and that's the one thing we bypass by using bmesh_from_pydata. But why triangulate ops should behave differently is weird.

zeffii commented 8 years ago

how about committing the slower version as SvTriangulateHeavy (we know it's potentially slow, but it works) ? or was this solved?

nortikin commented 8 years ago

@zeffii it was not solved as i know. Please, commit

zeffii commented 8 years ago

OK :)

zeffii commented 8 years ago

github commit automatically closes an issue when you write 'fixes #somenumber`. Can leave closed for now until someone finds time to investigate