From 320b8efc4b3c7860a114bb9b1fd8a8c5f9c4ca34 Mon Sep 17 00:00:00 2001 From: Chayan Deb Date: Mon, 20 Jan 2025 23:20:53 +0530 Subject: [PATCH] [Snap Cursor (WIP)]: Experimental feature 'snap_cursor' is now available to be selected from the Options menu (disabled by default). This is a work in progress and needs a lot more polishing to be properly usable. Currently only supports snapping to nearest component-endpoint. --- src/callback.c | 51 ++++++++++++++++++++++++++ src/draw.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/xschem.tcl | 7 ++-- 3 files changed, 154 insertions(+), 2 deletions(-) diff --git a/src/callback.c b/src/callback.c index 37888f88..3276ba95 100644 --- a/src/callback.c +++ b/src/callback.c @@ -1393,6 +1393,51 @@ void draw_crosshair(int what) xctx->draw_pixmap = sdp; } +/* cursor_type == 0 : only erase drawn cursor + * cursor_type == 1 : erase and draw a normal grid-snapping cursor + * cursor_type == 2 : erase and draw a diamond-shaped cursor that snaps to a component endpoint */ +void draw_snap_cursor(int cursor_type) +{ + int sdw, sdp; + dbg(1, "draw_snap_cursor(): cursor_type=%d\n", cursor_type); + sdw = xctx->draw_window; + sdp = xctx->draw_pixmap; + + if(!xctx->mouse_inside) return; + xctx->draw_pixmap = 0; + xctx->draw_window = 1; + if(fix_broken_tiled_fill || !_unix) { + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + 0, (int)Y_TO_SCREEN(xctx->prev_crossy) - 2 * INT_WIDTH(xctx->lw), + xctx->xrect[0].width, 4 * INT_WIDTH(xctx->lw), + 0, (int)Y_TO_SCREEN(xctx->prev_crossy) - 2 * INT_WIDTH(xctx->lw)); + + MyXCopyArea(display, xctx->save_pixmap, xctx->window, xctx->gc[0], + (int)X_TO_SCREEN(xctx->prev_crossx) - 2 * INT_WIDTH(xctx->lw), 0, + 4 * INT_WIDTH(xctx->lw), xctx->xrect[0].height, + (int)X_TO_SCREEN(xctx->prev_crossx) - 2 * INT_WIDTH(xctx->lw), 0); + } else { + drawtemparc(xctx->gctiled, NOW, xctx->prev_crossx, xctx->prev_crossy, 1, 0, 360); + } + if(cursor_type != 1) { + /*drawline(xctx->crosshair_layer, NOW, xctx->mousex_snap, xctx->mousey_snap, xctx->mousex_snap, xctx->mousey_snap, 0, NULL);*/ + double x, y; + find_closest_net_or_symbol_pin(xctx->mousex, xctx->mousey, &x, &y); + drawarc(xctx->crosshair_layer, NOW, x, y, 1, 1, 360, 1, 0); + draw_selection(xctx->gc[SELLAYER], 0); + xctx->prev_crossx = x; + xctx->prev_crossy = y; + } else { + drawarc(xctx->crosshair_layer, NOW, xctx->mousex_snap, xctx->mousey_snap, 1, 1, 360, 1, 0); + draw_selection(xctx->gc[SELLAYER], 0); + xctx->prev_crossx = xctx->mousex_snap; + xctx->prev_crossy = xctx->mousey_snap; + } + + xctx->draw_window = sdw; + xctx->draw_pixmap = sdp; +} + /* complete the STARTWIRE, STARTRECT, STARTZOOM, STARTCOPY ... operations */ static int end_place_move_copy_zoom() { @@ -2218,6 +2263,7 @@ int callback(const char *winpath, int event, int mx, int my, KeySym key, #endif int draw_xhair = tclgetboolvar("draw_crosshair"); int infix_interface = tclgetboolvar("infix_interface"); +int snap_cursor = tclgetboolvar("snap_cursor"); int rstate; /* (reduced state, without ShiftMask) */ /* this fix uses an alternative method for getting mouse coordinates on KeyPress/KeyRelease @@ -2335,6 +2381,7 @@ int rstate; /* (reduced state, without ShiftMask) */ case LeaveNotify: if(draw_xhair) draw_crosshair(1); + if(snap_cursor) draw_snap_cursor(0); tclvareval(xctx->top_path, ".drw configure -cursor {}" , NULL); xctx->mouse_inside = 0; break; @@ -2403,11 +2450,13 @@ int rstate; /* (reduced state, without ShiftMask) */ if(draw_xhair) { draw_crosshair(1); } + if(snap_cursor) draw_snap_cursor(0); if(xctx->ui_state & STARTPAN) pan(RUBBER, mx, my); if(xctx->semaphore >= 2) { if(draw_xhair) { draw_crosshair(2); } + if(snap_cursor) draw_snap_cursor(2); break; } dbg(1, "ui_state=%d deltax=%g\n", xctx->ui_state, xctx->deltax); @@ -2502,6 +2551,7 @@ int rstate; /* (reduced state, without ShiftMask) */ if(draw_xhair) { draw_crosshair(2); } + if(snap_cursor) draw_snap_cursor(2); break; case KeyRelease: @@ -4259,6 +4309,7 @@ int rstate; /* (reduced state, without ShiftMask) */ statusmsg(str,1); } if(draw_xhair) draw_crosshair(0); + if(snap_cursor) draw_snap_cursor(2); break; case -3: /* double click : edit prop */ if( waves_selected(event, key, state, button)) { diff --git a/src/draw.c b/src/draw.c index 6c7bf8b7..dcf409ac 100644 --- a/src/draw.c +++ b/src/draw.c @@ -1601,6 +1601,104 @@ void drawarc(int c, int what, double x, double y, double r, double a, double b, } } +void draw_snap_point(int c, int what, double x, double y, double r, double a, double b, int arc_fill, int dash) +{ + static int i=0; + static XArc xarc[CADDRAWBUFFERSIZE]; + double x1, y1, x2, y2; /* arc bbox */ + double xx1, yy1, xx2, yy2; /* complete circle bbox in screen coords */ + GC gc; + + if(arc_fill || dash) what = NOW; + + if(!has_x) return; + if(what & ADD) + { + if(i>=CADDRAWBUFFERSIZE) + { + if(xctx->draw_window) XDrawArcs(display, xctx->window, xctx->gc[c], xarc,i); + if(xctx->draw_pixmap) XDrawArcs(display, xctx->save_pixmap, xctx->gc[c], xarc,i); + i=0; + } + xx1=X_TO_SCREEN(x-r); + yy1=Y_TO_SCREEN(y-r); + xx2=X_TO_SCREEN(x+r); + yy2=Y_TO_SCREEN(y+r); + arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2); + x1=X_TO_SCREEN(x1); + y1=Y_TO_SCREEN(y1); + x2=X_TO_SCREEN(x2); + y2=Y_TO_SCREEN(y2); + if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) ) + { + xarc[i].x=(short)xx1; + xarc[i].y=(short)yy1; + xarc[i].width =(unsigned short)(xx2 - xx1); + xarc[i].height=(unsigned short)(yy2 - yy1); + xarc[i].angle1 = (short)(a*64); + xarc[i].angle2 = (short)(b*64); + ++i; + } + } + else if(what & NOW) + { + xx1=X_TO_SCREEN(x-r); + yy1=Y_TO_SCREEN(y-r); + xx2=X_TO_SCREEN(x+r); + yy2=Y_TO_SCREEN(y+r); + if(arc_fill) + arc_bbox(x, y, r, 0, 360, &x1,&y1,&x2,&y2); + else + arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2); + x1=X_TO_SCREEN(x1); + y1=Y_TO_SCREEN(y1); + x2=X_TO_SCREEN(x2); + y2=Y_TO_SCREEN(y2); + if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) ) + { + if(dash) { + char dash_arr[2]; + dash_arr[0] = dash_arr[1] = (char)dash; + XSetDashes(display, xctx->gc[c], 0, dash_arr, 1); + XSetLineAttributes (display, xctx->gc[c], XLINEWIDTH(xctx->lw), xDashType, xCap, xJoin); + } + + if(xctx->draw_window) { + XDrawArc(display, xctx->window, xctx->gc[c], (int)xx1, (int)yy1, + (int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64)); + } + if(xctx->draw_pixmap) { + XDrawArc(display, xctx->save_pixmap, xctx->gc[c], (int)xx1, (int)yy1, + (int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64)); + } + + if(xctx->fill_pattern && (xctx->fill_type[c] || arc_fill == 3) ){ + + if(arc_fill & 2) gc = xctx->gc[c]; + else gc = xctx->gcstipple[c]; + if(arc_fill) { + if(xctx->draw_window) + XFillArc(display, xctx->window, gc, (int)xx1, (int)yy1, + (int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64)); + if(xctx->draw_pixmap) + XFillArc(display, xctx->save_pixmap, gc, (int)xx1, (int)yy1, + (int)(xx2-xx1), (int)(yy2-yy1), (int)(a*64), (int)(b*64)); + } + } + if(dash) { + XSetLineAttributes (display, xctx->gc[c], XLINEWIDTH(xctx->lw) ,LineSolid, LINECAP , LINEJOIN); + } + } + } + else if((what & END) && i) + { + if(xctx->draw_window) XDrawArcs(display, xctx->window, xctx->gc[c], xarc,i); + if(xctx->draw_pixmap) XDrawArcs(display, xctx->save_pixmap, xctx->gc[c], xarc,i); + i=0; + } +} + + void filledrect(int c, int what, double rectx1,double recty1,double rectx2,double recty2, int fill, int e_a, int e_b) { diff --git a/src/xschem.tcl b/src/xschem.tcl index 99f2c4fc..b02bb3ef 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -7622,7 +7622,7 @@ set tctx::global_list { PDK_ROOT PDK SKYWATER_MODELS SKYWATER_STDCELLS INITIALINSTDIR INITIALLOADDIR INITIALPROPDIR INITIALTEXTDIR XSCHEM_LIBRARY_PATH add_all_windows_drives auto_hilight auto_hilight_graph_nodes autofocus_mainwindow - autotrim_wires infix_interface orthogonal_wiring bespice_listen_port big_grid_points bus_replacement_char cadgrid cadlayers + autotrim_wires orthogonal_wiring snap_cursor bespice_listen_port big_grid_points bus_replacement_char cadgrid cadlayers cadsnap cairo_font_name cairo_font_scale change_lw color_ps tctx::colors compare_sch constr_mv copy_cell crosshair_layer custom_label_prefix custom_token dark_colors dark_colorscheme dark_gui_colorscheme delay_flag dim_bg dim_value disable_unique_names @@ -8042,7 +8042,7 @@ proc build_widgets { {topwin {} } } { global recentfile color_ps transparent_svg menu_debug_var enable_stretch global netlist_show flat_netlist split_files compare_sch intuitive_interface global draw_grid big_grid_points sym_txt change_lw incr_hilight symbol_width - global cadsnap cadgrid draw_window toolbar_visible hide_symbols undo_type + global cadsnap cadgrid draw_window toolbar_visible hide_symbols undo_type snap_cursor global disable_unique_names persistent_command autotrim_wires infix_interface orthogonal_wiring en_hilight_conn_inst global local_netlist_dir editor netlist_type netlist_dir spiceprefix initial_geometry if { $dark_gui_colorscheme} { @@ -8168,6 +8168,8 @@ proc build_widgets { {topwin {} } } { -selectcolor $selectcolor -accelerator Y $topwin.menubar.option add checkbutton -label "Enable infix-interface" -variable infix_interface \ -selectcolor $selectcolor + $topwin.menubar.option add checkbutton -label "Enable snap cursor" -variable snap_cursor \ + -selectcolor $selectcolor $topwin.menubar.option add checkbutton -label "Enable orthogonal wiring" -variable orthogonal_wiring \ -selectcolor $selectcolor -accelerator Shift+L $topwin.menubar.option add checkbutton -label "Unsel. partial sel. wires after stretch move" \ @@ -9101,6 +9103,7 @@ set_ne persistent_command 0 set_ne intuitive_interface 1 set_ne autotrim_wires 0 set_ne infix_interface 0 +set_ne snap_cursor 0 set_ne orthogonal_wiring 1 set_ne compare_sch 0 set_ne disable_unique_names 0