Skip to content
Snippets Groups Projects
EXM.py 6.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • '''
    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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    
    END GPL LICENCE BLOCK
    '''
    
    import math
    import itertools
    
    import bpy
    import bgl
    import mathutils
    import bmesh
    
    from bpy_extras.view3d_utils import location_3d_to_region_2d as loc3d2d
    
    from mesh_tinyCAD import cad_module as cm
    
    
    line_colors = {
        "prime": (0.2, 0.8, 0.9),
        "extend": (0.9, 0.8, 0.2),
        "projection": (0.9, 0.6, 0.5)
    }
    
    
    def restore_bgl_defaults():
        bgl.glLineWidth(1)
        bgl.glDisable(bgl.GL_BLEND)
        bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
    
    
    def get_projection_coords(self):
        list2d = [val for key, val in self.xvectors.items()]
        list2d = [[p, self.bm.verts[pidx].co] for (p, pidx) in list2d]
        return list(itertools.chain.from_iterable(list2d))
    
    
    def get_extender_coords(self):
        coords = []
        for idx in self.selected_edges:
            c = cm.coords_tuple_from_edge_idx(self.bm, idx)
            coords.extend(c)
        return coords
    
    
    def add_or_remove_new_edge(self, idx):
        '''
            - only add idx if not edge_prime
            - and not currently present in selected_edges
        '''
        p_idx = self.edge_prime_idx
    
        if idx == p_idx:
            print(idx, 'is edge prime, not adding')
            return
    
        if (idx in self.selected_edges):
            self.selected_edges.remove(idx)
            del self.xvectors[idx]
        else:
            edge_prime = cm.coords_tuple_from_edge_idx(self.bm, p_idx)
            edge2 = cm.coords_tuple_from_edge_idx(self.bm, idx)
            p = cm.get_intersection(edge_prime, edge2)
    
            if not cm.point_on_edge(p, edge_prime):
                return
    
            vert_idx_closest = cm.closest_idx(p, self.bm.edges[idx])
            self.xvectors[idx] = [p, vert_idx_closest]
            self.selected_edges.append(idx)
    
    
    def set_mesh_data(self):
        history = self.bm.select_history
        if not (len(history) > 0):
            return
    
        a = history[-1]
        if not isinstance(a, bmesh.types.BMEdge):
            return
    
        add_or_remove_new_edge(self, a.index)
        a.select = False
    
    
    def draw_callback_px(self, context, event):
    
        if context.mode != "EDIT_MESH":
            return
    
        # get screen information
        region = context.region
        rv3d = context.space_data.region_3d
        this_object = context.active_object
        matrix_world = this_object.matrix_world
        # scene = context.scene
    
        def draw_gl_strip(coords, line_thickness):
            bgl.glLineWidth(line_thickness)
            bgl.glBegin(bgl.GL_LINES)
            for coord in coords:
                vector3d = matrix_world * coord
                vector2d = loc3d2d(region, rv3d, vector3d)
                bgl.glVertex2f(*vector2d)
            bgl.glEnd()
    
        def draw_edge(coords, mode, lt):
            bgl.glColor3f(*line_colors[mode])
            draw_gl_strip(coords, lt)
    
        def do_single_draw_pass(self):
    
            # draw edge prime
            c = cm.coords_tuple_from_edge_idx(self.bm, self.edge_prime_idx)
            draw_edge(c, "prime", 3)
    
            # draw extender edges and projections.
            if len(self.selected_edges) > 0:
    
                # get and draw selected valid edges
                coords_ext = get_extender_coords(self)
                draw_edge(coords_ext, "extend", 3)
    
                # get and draw extenders only
                coords_proj = get_projection_coords(self)
                draw_edge(coords_proj, "projection", 3)
    
            restore_bgl_defaults()
    
        do_single_draw_pass(self)
    
    
    class ExtendEdgesMulti(bpy.types.Operator):
        bl_idname = "view3d.extend_eges"
        bl_label = "EXM extend multiple edges"
        bl_description = "extend edge towards"
    
        @classmethod
        def poll(cls, context):
            return context.mode == "EDIT_MESH"
    
        def modify_geometry(self, context, event_type):
            list2d = [val for key, val in self.xvectors.items()]
            vertex_count = len(self.bm.verts)
    
            # adds new geometry
            if event_type == 'PERIOD':
    
                for point, closest_idx in list2d:
                    self.bm.verts.new((point))
                    v1 = self.bm.verts[-1]
                    v2 = self.bm.verts[closest_idx]
                    self.bm.edges.new((v1, v2))
    
            # extend current egdes towards intersections
            elif event_type == 'COMMA':
    
                for point, closest_idx in list2d:
                    self.bm.verts[closest_idx].co = point
    
            bmesh.update_edit_mesh(self.me)
    
        def modal(self, context, event):
    
            if event.type in ('PERIOD', 'COMMA', 'ESC'):
                bpy.types.SpaceView3D.draw_handler_remove(self.handle, 'WINDOW')
                bpy.context.space_data.show_manipulator = True
    
                if not (event.type is 'ESC'):
                    self.modify_geometry(context, event.type)
    
                del self.selected_edges
                del self.xvectors
                return {'FINISHED'}
    
            if (event.type, event.value) == ("RIGHTMOUSE", "RELEASE"):
                set_mesh_data(self)
    
            if context.area:
                context.area.tag_redraw()
    
            return {'PASS_THROUGH'}
    
        def invoke(self, context, event):
            if context.area.type == "VIEW_3D":
    
                self.selected_edges = []
                self.xvectors = {}
                self.me = context.active_object.data
                self.bm = bmesh.from_edit_mesh(self.me)
                self.me.update()
    
                # enforce singular edge selection first then assign to edge_prime
                m = [e.index for e in self.bm.edges if e.select]
                if not len(m) is 1:
                    self.report({"WARNING"}, "Please select 1 edge only")
                    return {'CANCELLED'}
    
                # switch off axial manipulator, set important variables.
                self.edge_prime_idx = m[0]
                bpy.context.space_data.show_manipulator = False
    
                # configure draw handler
                fparams = self, context, event
                handler_config = draw_callback_px, fparams, 'WINDOW', 'POST_PIXEL'
                draw_handler_add = bpy.types.SpaceView3D.draw_handler_add
                self.handle = draw_handler_add(*handler_config)
    
                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}
            else:
                self.report({"WARNING"}, "Please run operator from within 3d view")
                return {'CANCELLED'}