def _get_crosses(self, p0, p1): Point_to_Point2D = self.rfcontext.Point_to_Point2D dist = self.rfcontext.drawing.scale(options['knife snap dist']) crosses = set() touched = set() #p0 = Point_to_Point2D(p0) #p1 = Point_to_Point2D(p1) v01 = Vec2D(p1 - p0) lv01 = max(v01.length, 0.00001) d01 = v01 / lv01 def add(p, e): if e in touched: return p.freeze() touched.add(e) crosses.add((p, e, d01.dot(p - p0) / lv01)) p0v = self.rfcontext.accel_nearest2D_vert(point=p0, max_dist=options['knife snap dist'])[0] if p0v and not p0v.link_edges: add(p0, p0v) for e in self.vis_edges: v0, v1 = e.verts c0, c1 = Point_to_Point2D(v0.co), Point_to_Point2D(v1.co) i = intersect2d_segment_segment(p0, p1, c0, c1) clc0 = closest2d_point_segment(c0, p0, p1) clc1 = closest2d_point_segment(c1, p0, p1) clp0 = closest2d_point_segment(p0, c0, c1) clp1 = closest2d_point_segment(p1, c0, c1) if (clc0 - c0).length <= dist: add(c0, v0) elif (clc1 - c1).length <= dist: add(c1, v1) elif (clp0 - p0).length <= dist: add(clp0, e) elif (clp1 - p1).length <= dist: add(clp1, e) elif i: add(Point2D(i), e) crosses = sorted(crosses, key=lambda cross: cross[2]) return crosses
def draw_postpixel(self): # TODO: put all logic into set_next_state(), such as vertex snapping, edge splitting, etc. #if self.rfcontext.nav or self.mode != 'main': return if not self.actions.using_onlymods({'insert', 'insert alt1'}): return hit_pos = self.actions.hit_pos if not hit_pos: return self.set_next_state() bgl.glEnable(bgl.GL_BLEND) CC_DRAW.stipple(pattern=[4, 4]) CC_DRAW.point_size(8) CC_DRAW.line_width(2) if self.next_state == 'knife selected edge': bmv1, bmv2 = self.nearest_edge.verts faces = self.nearest_edge.link_faces if faces: for f in faces: lco = [] for v0, v1 in iter_pairs(f.verts, True): lco.append(v0.co) if (v0 == bmv1 and v1 == bmv2) or (v0 == bmv2 and v1 == bmv1): lco.append(hit_pos) self.draw_lines(lco) else: self.draw_lines([bmv1.co, hit_pos]) self.draw_lines([bmv2.co, hit_pos]) elif self.next_state == 'new vertex': p0 = hit_pos e1, d = self.rfcontext.nearest2D_edge(edges=self.vis_edges) if e1: bmv1, bmv2 = e1.verts if d is not None and d < self.rfcontext.drawing.scale(15): f = next(iter(e1.link_faces), None) if f: lco = [] for v0, v1 in iter_pairs(f.verts, True): lco.append(v0.co) if (v0 == bmv1 and v1 == bmv2) or (v0 == bmv2 and v1 == bmv1): lco.append(p0) self.draw_lines(lco) else: self.draw_lines([bmv1.co, hit_pos]) self.draw_lines([bmv2.co, hit_pos]) else: self.draw_lines([hit_pos]) else: self.draw_lines([hit_pos]) elif self.next_state in {'vert-edge', 'vert-edge-vert'}: sel_verts = self.sel_verts bmv0 = next(iter(sel_verts)) if self.nearest_vert: p0 = self.nearest_vert.co elif self.next_state == 'vert-edge': p0 = hit_pos e1, d = self.rfcontext.nearest2D_edge(edges=self.vis_edges) if e1: bmv1, bmv2 = e1.verts if d is not None and d < self.rfcontext.drawing.scale(15): f = next(iter(e1.link_faces), None) if f: lco = [] for v0, v1 in iter_pairs(f.verts, True): lco.append(v0.co) if (v0 == bmv1 and v1 == bmv2) or (v0 == bmv2 and v1 == bmv1): lco.append(p0) self.draw_lines(lco) else: self.draw_lines([bmv1.co, p0]) self.draw_lines([bmv2.co, p0]) elif self.next_state == 'vert-edge-vert': p0 = hit_pos else: return self.draw_lines([bmv0.co, p0]) elif self.actions.shift and not self.actions.ctrl: if self.next_state in [ 'edge-face', 'edge-quad', 'edge-quad-snap', 'tri-quad' ]: nearest_vert, _ = self.rfcontext.nearest2D_vert( verts=self.sel_verts, max_dist=options['polypen merge dist']) if nearest_vert: self.draw_lines([nearest_vert.co, hit_pos]) elif not self.actions.shift and self.actions.ctrl: if self.next_state == 'edge-face': e0, _ = self.rfcontext.nearest2D_edge( edges=self.sel_edges) #next(iter(self.sel_edges)) if not e0: return e1, d = self.rfcontext.nearest2D_edge(edges=self.vis_edges) if e1 and d < self.rfcontext.drawing.scale(15) and e0 == e1: bmv1, bmv2 = e1.verts p0 = hit_pos f = next(iter(e1.link_faces), None) if f: lco = [] for v0, v1 in iter_pairs(f.verts, True): lco.append(v0.co) if (v0 == bmv1 and v1 == bmv2) or (v0 == bmv2 and v1 == bmv1): lco.append(p0) self.draw_lines(lco) else: self.draw_lines([bmv1.co, hit_pos]) self.draw_lines([bmv2.co, hit_pos]) else: # self.draw_lines([hit_pos]) bmv1, bmv2 = e0.verts if self.nearest_vert and not self.nearest_vert.select: p0 = self.nearest_vert.co else: p0 = hit_pos self.draw_lines([p0, bmv1.co, bmv2.co]) elif self.next_state == 'edge-quad': # a Desmos construction of how this works: https://www.desmos.com/geometry/bmmx206thi xy0, xy1, xy2, xy3 = self._get_edge_quad_verts() if xy0 is None: return co0 = self.rfcontext.raycast_sources_Point2D(xy0)[0] co1 = self.rfcontext.raycast_sources_Point2D(xy1)[0] co2 = self.rfcontext.raycast_sources_Point2D(xy2)[0] co3 = self.rfcontext.raycast_sources_Point2D(xy3)[0] self.draw_lines([co1, co2, co3, co0]) elif self.next_state == 'edge-quad-snap': e0, _ = self.rfcontext.nearest2D_edge(edges=self.sel_edges) e1 = self.nearest_edge if not e0 or not e1: return bmv0, bmv1 = e0.verts bmv2, bmv3 = e1.verts p0, p1 = self.rfcontext.Point_to_Point2D( bmv0.co), self.rfcontext.Point_to_Point2D(bmv1.co) p2, p3 = self.rfcontext.Point_to_Point2D( bmv2.co), self.rfcontext.Point_to_Point2D(bmv3.co) if intersect2d_segment_segment(p1, p2, p3, p0): bmv2, bmv3 = bmv3, bmv2 # if e0.vector2D(self.rfcontext.Point_to_Point2D).dot(e1.vector2D(self.rfcontext.Point_to_Point2D)) > 0: # bmv2,bmv3 = bmv3,bmv2 self.draw_lines([bmv0.co, bmv1.co, bmv2.co, bmv3.co]) elif self.next_state == 'tri-quad': if self.nearest_vert and not self.nearest_vert.select: p0 = self.nearest_vert.co else: p0 = hit_pos e1, _ = self.rfcontext.nearest2D_edge(edges=self.sel_edges) if not e1: return bmv1, bmv2 = e1.verts f = next(iter(e1.link_faces), None) if not f: return lco = [] for v0, v1 in iter_pairs(f.verts, True): lco.append(v0.co) if (v0 == bmv1 and v1 == bmv2) or (v0 == bmv2 and v1 == bmv1): lco.append(p0) self.draw_lines(lco)
def _insert(self): self.last_delta = None self.move_done_pressed = None self.move_done_released = 'insert' self.move_cancelled = 'cancel' if self.actions.shift and not self.actions.ctrl and not self.next_state in [ 'new vertex', 'vert-edge' ]: self.next_state = 'vert-edge' nearest_vert, _ = self.rfcontext.nearest2D_vert( verts=self.sel_verts, max_dist=options['polypen merge dist']) self.rfcontext.select(nearest_vert) sel_verts = self.sel_verts sel_edges = self.sel_edges sel_faces = self.sel_faces if self.next_state == 'knife selected edge': # overriding: if hovering over a selected edge, knife it! # self.nearest_edge and self.nearest_edge.select: #print('knifing selected, hovered edge') bmv = self.rfcontext.new2D_vert_mouse() if not bmv: self.rfcontext.undo_cancel() return 'main' bme0, bmv2 = self.nearest_edge.split() bmv.merge(bmv2) self.rfcontext.select(bmv) self.mousedown = self.actions.mousedown xy = self.rfcontext.Point_to_Point2D(bmv.co) if not xy: #print('Could not insert: ' + str(bmv.co)) self.rfcontext.undo_cancel() return 'main' self.bmverts = [(bmv, xy)] if bmv else [] self.set_vis_bmverts() return 'move' if self.next_state in {'vert-edge', 'vert-edge-vert'}: bmv0 = next(iter(sel_verts)) if self.next_state == 'vert-edge': nearest_vert, dist = self.rfcontext.nearest2D_vert( verts=self.vis_verts, max_dist=options['polypen merge dist']) if nearest_vert: bmv1 = nearest_vert lbmf = bmv0.shared_faces(bmv1) if len(lbmf) == 1 and not bmv0.share_edge(bmv1): # split face bmf = lbmf[0] bmf.split(bmv0, bmv1) self.rfcontext.select(bmv1) return 'main' nearest_edge, dist = self.rfcontext.nearest2D_edge( edges=self.vis_edges) bmv1 = self.rfcontext.new2D_vert_mouse() if not bmv1: self.rfcontext.undo_cancel() return 'main' if dist is not None and dist < self.rfcontext.drawing.scale( 15): if bmv0 in nearest_edge.verts: # selected vert already part of edge; split bme0, bmv2 = nearest_edge.split() bmv1.merge(bmv2) self.rfcontext.select(bmv1) else: bme0, bmv2 = nearest_edge.split() bmv1.merge(bmv2) bmf = next(iter(bmv0.shared_faces(bmv1)), None) if bmf: if not bmv0.share_edge(bmv1): bmf.split(bmv0, bmv1) if not bmv0.share_face(bmv1): bme = self.rfcontext.new_edge((bmv0, bmv1)) self.rfcontext.select(bme) self.rfcontext.select(bmv1) else: bme = self.rfcontext.new_edge((bmv0, bmv1)) self.rfcontext.select(bme) elif self.next_state == 'vert-edge-vert': if self.nearest_vert: bmv1 = self.nearest_vert else: bmv1 = self.rfcontext.new2D_vert_mouse() if not bmv1: self.rfcontext.undo_cancel() return 'main' bme = bmv0.shared_edge(bmv1) or self.rfcontext.new_edge( (bmv0, bmv1)) self.rfcontext.select(bmv1) else: return 'main' self.mousedown = self.actions.mousedown xy = self.rfcontext.Point_to_Point2D(bmv1.co) if not xy: dprint('Could not insert: ' + str(bmv1.co)) self.rfcontext.undo_cancel() return 'main' self.bmverts = [(bmv1, xy)] if bmv1 else [] self.set_vis_bmverts() return 'move' if self.next_state == 'edge-face': bme, _ = self.rfcontext.nearest2D_edge(edges=self.sel_edges) if not bme: return bmv0, bmv1 = bme.verts if self.nearest_vert and not self.nearest_vert.select: bmv2 = self.nearest_vert bmf = self.rfcontext.new_face([bmv0, bmv1, bmv2]) self.rfcontext.clean_duplicate_bmedges(bmv2) else: bmv2 = self.rfcontext.new2D_vert_mouse() if not bmv2: self.rfcontext.undo_cancel() return 'main' bmf = self.rfcontext.new_face([bmv0, bmv1, bmv2]) self.rfcontext.select(bmf) self.mousedown = self.actions.mousedown xy = self.rfcontext.Point_to_Point2D(bmv2.co) if not xy: dprint('Could not insert: ' + str(bmv2.co)) self.rfcontext.undo_cancel() return 'main' self.bmverts = [(bmv2, xy)] if bmv2 else [] self.set_vis_bmverts() return 'move' if self.next_state == 'edge-quad': xy0, xy1, xy2, xy3 = self._get_edge_quad_verts() if xy0 is None or xy1 is None or xy2 is None or xy3 is None: return # a Desmos construction of how this works: https://www.desmos.com/geometry/bmmx206thi e0, _ = self.rfcontext.nearest2D_edge(edges=self.sel_edges) if not e0: return bmv0, bmv1 = e0.verts bmv2, _ = self.rfcontext.nearest2D_vert( point=xy2, verts=self.vis_verts, max_dist=options['polypen merge dist']) if not bmv2: bmv2 = self.rfcontext.new2D_vert_point(xy2) bmv3, _ = self.rfcontext.nearest2D_vert( point=xy3, verts=self.vis_verts, max_dist=options['polypen merge dist']) if not bmv3: bmv3 = self.rfcontext.new2D_vert_point(xy3) if not bmv2 or not bmv3: self.rfcontext.undo_cancel() return 'main' e1 = bmv2.shared_edge(bmv3) if not e1: e1 = self.rfcontext.new_edge([bmv2, bmv3]) bmf = self.rfcontext.new_face([bmv0, bmv1, bmv2, bmv3]) bmes = [ bmv1.shared_edge(bmv2), bmv0.shared_edge(bmv3), bmv2.shared_edge(bmv3) ] self.rfcontext.select(bmes, subparts=False) self.mousedown = self.actions.mousedown self.bmverts = [] if bmv2: self.bmverts.append( (bmv2, self.rfcontext.Point_to_Point2D(bmv2.co))) if bmv3: self.bmverts.append( (bmv3, self.rfcontext.Point_to_Point2D(bmv3.co))) self.set_vis_bmverts() return 'move' if self.next_state == 'edge-quad-snap': e0, _ = self.rfcontext.nearest2D_edge(edges=self.sel_edges) e1 = self.nearest_edge if not e0 or not e1: return bmv0, bmv1 = e0.verts bmv2, bmv3 = e1.verts p0, p1 = self.rfcontext.Point_to_Point2D( bmv0.co), self.rfcontext.Point_to_Point2D(bmv1.co) p2, p3 = self.rfcontext.Point_to_Point2D( bmv2.co), self.rfcontext.Point_to_Point2D(bmv3.co) if intersect2d_segment_segment(p1, p2, p3, p0): bmv2, bmv3 = bmv3, bmv2 # if e0.vector2D(self.rfcontext.Point_to_Point2D).dot(e1.vector2D(self.rfcontext.Point_to_Point2D)) > 0: # bmv2,bmv3 = bmv3,bmv2 bmf = self.rfcontext.new_face([bmv0, bmv1, bmv2, bmv3]) # select all non-manifold edges that share vertex with e1 bmes = [ e for e in bmv2.link_edges + bmv3.link_edges if not e.is_manifold and not e.share_face(e1) ] if not bmes: bmes = [bmv1.shared_edge(bmv2), bmv0.shared_edge(bmv3)] self.rfcontext.select(bmes, subparts=False) return 'main' if self.next_state == 'tri-quad': hit_pos = self.actions.hit_pos if not hit_pos: self.rfcontext.undo_cancel() return 'main' if not self.sel_edges: return 'main' bme0, _ = self.rfcontext.nearest2D_edge(edges=self.sel_edges) if not bme0: return bmv0, bmv2 = bme0.verts bme1, bmv1 = bme0.split() bme0.select = True bme1.select = True self.rfcontext.select(bmv1.link_edges) if self.nearest_vert and not self.nearest_vert.select: self.nearest_vert.merge(bmv1) bmv1 = self.nearest_vert self.rfcontext.clean_duplicate_bmedges(bmv1) for bme in bmv1.link_edges: bme.select &= len(bme.link_faces) == 1 bme01, bme12 = bmv0.shared_edge(bmv1), bmv1.shared_edge(bmv2) if len(bme01.link_faces) == 1: bme01.select = True if len(bme12.link_faces) == 1: bme12.select = True else: bmv1.co = hit_pos self.mousedown = self.actions.mousedown self.rfcontext.select(bmv1, only=False) xy = self.rfcontext.Point_to_Point2D(bmv1.co) if not xy: dprint('Could not insert: ' + str(bmv3.co)) self.rfcontext.undo_cancel() return 'main' self.bmverts = [(bmv1, xy)] if bmv1 else [] self.set_vis_bmverts() return 'move' nearest_edge, d = self.rfcontext.nearest2D_edge(edges=self.vis_edges) bmv = self.rfcontext.new2D_vert_mouse() if not bmv: self.rfcontext.undo_cancel() return 'main' if d is not None and d < self.rfcontext.drawing.scale(15): bme0, bmv2 = nearest_edge.split() bmv.merge(bmv2) self.rfcontext.select(bmv) self.mousedown = self.actions.mousedown xy = self.rfcontext.Point_to_Point2D(bmv.co) if not xy: dprint('Could not insert: ' + str(bmv.co)) self.rfcontext.undo_cancel() return 'main' self.bmverts = [(bmv, xy)] if bmv else [] self.set_vis_bmverts() return 'move'