* Bugs #7777 & #12303 fixed - interactive move of any object in figure 26/18826/18
Samuel GOUGEON [Sat, 31 Dec 2016 19:40:08 +0000 (20:40 +0100)]
 * http://bugzilla.scilab.org/7777  : move mode turned off after each object
 * http://bugzilla.scilab.org/12302 : enable moving blocks of legends
 * ADDED in this commit:
    - "Move objects" icon added in the graphics toolbar
    - "Move objects" item added in the graphics Edit menu
    - A true "Move mode" is now available: It is no longer necessary to
      re-run ged(6) after each (successfull or unsuccessfull) selecting
      click.
    - error for Segs fixed.
    - Labels: Moving with reversed axis added.
    - Moving in log or/and reversed axis added for the following objects:
       - Text
       - Segments and arrows
       - Polyline
       - Rectangle
       - Arc
    - Moving added in all axis modes (normal, reversed, log) for the
      following types of objects:
       - Axes in their whole: handle = its frame box
       - Legend: Both types of legends blocks, as generated with legend()
         or with legends(): handle = its frame box
       - Datatip detached: handle = datatip area
       - uicontrols
    - Unit test added for ged(6,)
    - ged_loop, ged_move_entity: header added (split from ged.sci)
    - dist2Ellipse() used nowhere in Scilab => removed

Change-Id: I8787fd46fc2a54f477ad2ca1e43554fa22683304

12 files changed:
scilab/CHANGES.md
scilab/modules/graphics/macros/legends.sci
scilab/modules/graphics/tests/unit_tests/ged_6_move_objects.tst [new file with mode: 0644]
scilab/modules/gui/etc/graphics_menubar.xml
scilab/modules/gui/etc/graphics_toolbar.xml
scilab/modules/gui/images/icons/16x16/actions/ged_move.png [new file with mode: 0644]
scilab/modules/tclsci/macros/dist2Arc.sci [deleted file]
scilab/modules/tclsci/macros/dist2Ellipse.sci [deleted file]
scilab/modules/tclsci/macros/ged_getobject.sci
scilab/modules/tclsci/macros/ged_loop.sci
scilab/modules/tclsci/macros/ged_move_entity.sci
scilab/modules/tclsci/macros/pixDist2Arc.sci [deleted file]

index 1ac9863..4828be8 100644 (file)
@@ -186,6 +186,7 @@ Feature changes and additions
 * `nchoosek` is introduced, to compute the binomial coefficients.
 * The left .\. and right ./. Kronecker divisions are now implemented, for arrays of decimal or complex numbers.
 * `perms` can now build and return only unique permutations, without duplicates.
+* Most of graphic objects can be moved interactively in opened figures.
 
 
 Help pages:
@@ -270,6 +271,7 @@ Bug Fixes
 * [#7724](https://bugzilla.scilab.org/7724): When a figure is created in .auto_resize="on" mode, its .axes_size sets its .figure_size accordingly, not the reverse. But this was not documented.
 * [#7732](https://bugzilla.scilab.org/7732): The `datafit` help page needed to be fixed and overhauled.
 * [#7765](https://bugzilla.scilab.org/7765): `champ1` is useless. `champ().colored` is available for a long time.
+* [#7777](https://bugzilla.scilab.org/7777): `ged` did not allow moving several objects. 
 * [#7948](https://bugzilla.scilab.org/7948): `gsort` could not perform multilevel sorting, and could not sort complex numbers completely.
 * [#7967](https://bugzilla.scilab.org/7967): The tricky size `[ny,nx]` of `meshgrid(x,y)` results and usages with graphics was not enough documented.
 * [#8301](https://bugzilla.scilab.org/8301): `definedfields` wrongly considered as defined void elements in lists, tlists and mlists.
@@ -289,6 +291,7 @@ Bug Fixes
 * [#11852](https://bugzilla.scilab.org/11852): File browser didn't update after file creation or removal.
 * [#11363](https://bugzilla.scilab.org/11363): `show_window()` did not raise the current graphics window.
 * [#12013](https://bugzilla.scilab.org/12013): `bitset` did not work for numbers greater than 2^32-1.
+* [#12302](https://bugzilla.scilab.org/12302): Blocks of legends could not be moved interactively. 
 * [#12520](https://bugzilla.scilab.org/12520): Variable browser did not display the size of the variables.
 * [#12534](https://bugzilla.scilab.org/12534): Variable browser did not display the size of the variables.
 * [#12837](https://bugzilla.scilab.org/12837): `strcmpi` was an obsolete duplicate of `strcmp(,'i')`. It is removed.
index 5c5a25e..09fea6b 100644 (file)
@@ -19,30 +19,34 @@ function legends(leg, style, opt, with_box, font_size )
     //    F. Delebecque + slight modif from B. Pincon
     //  modified again by Eric Dubois and Jean-Baptiste Silvy 18/01/07
 
-    rhs=argn(2)
+    rhs = argn(2)
 
     if rhs < 2 then
-        error(msprintf(gettext("%s: Wrong number of input arguments: At least %d expected.\n"), "legends", 2));
+        msg = _("%s: Wrong number of input arguments: At least %d expected.\n")
+        error(msprintf(msg, "legends", 2));
     end
     if type(leg) ~= 10 then,
-        error(msprintf(gettext("%s: Wrong type for input argument #%d: String array expected.\n"), "legends", 1));
+        msg = _("%s: Wrong type for input argument #%d: String array expected.\n")
+        error(msprintf(msg, "legends", 1));
     end
-    nleg=size(leg,"*")
+    nleg = size(leg,"*")
     if type(style) ~= 1 & type(style) ~= 9 then
-        error(msprintf(gettext("%s: Wrong type for input argument #%d: Real array or graphic handle array expected.\n"), "legends", 2));
+        msg = _("%s: Wrong type for input argument #%d: Real array or graphic handle array expected.\n")
+        error(msprintf(msg, "legends", 2));
     end
     if ~exists("opt","local") then
-        opt=5
+        opt = 5
     elseif typeof(opt)=="string" then
         select opt
-        case "ur" then, opt=1,
-        case "ul" then, opt=2,
-        case "ll" then, opt=3,
-        case "lr" then, opt=4,
-        case "?"  then, opt=5,
-        case "below" then, opt=6,
+        case "ur" then, opt = 1,
+        case "ul" then, opt = 2,
+        case "ll" then, opt = 3,
+        case "lr" then, opt = 4,
+        case "?"  then, opt = 5,
+        case "below" then, opt = 6,
         else
-            error(msprintf(gettext("%s: Wrong value for input argument #%d: ''%s'', ''%s'', ''%s'', ''%s'', ''%s'' or ''%s'' expected.\n"),"legends", 3, "ur", "ul", "ll", "lr", "?", "below"));
+            msg = _("%s: Wrong value for input argument #%d: ''%s'', ''%s'', ''%s'', ''%s'', ''%s'' or ''%s'' expected.\n")
+            error(msprintf(msg,"legends", 3, "ur", "ul", "ll", "lr", "?", "below"));
         end
     end
     if ~exists("with_box","local") then, with_box=%t, end
@@ -51,34 +55,37 @@ function legends(leg, style, opt, with_box, font_size )
     end
 
     ns=size(style)
-    if ns ~= [2 1] & or(ns==1) then, style=matrix(style,1,-1),end
-    ns=size(style,2)
+    if ns ~= [2 1] & or(ns==1) then
+        style = matrix(style,1,-1)
+    end
+    ns = size(style,2)
 
     //preserve current graphic context
-    f=gcf()
-    vis=f.immediate_drawing;
-    old_ax=gca(),
-    arect=old_ax.margins;
-    r1=old_ax.axes_bounds;
+    f = gcf()
+    vis = f.immediate_drawing;
+    old_ax = gca(),
+    arect = old_ax.margins;
+    r1 = old_ax.axes_bounds;
 
     //create small axes on the top left corner (the axes is chosen very
     //small to avoid it can be selected for rotation in new graphic mode
     //case (a little tricky)
     xsetech(wrect=[r1(1),r1(2),r1(3)/1000,r1(4)/1000],frect=[0 0 1,1]/1000,arect=[0,0,0,0])
-    xmin=arect(1);xmax=1-arect(2);ymin=-1+arect(4);ymax=-arect(3);
-    cur_ax=gca(),
-    cur_ax.clip_state="off";
+    xmin = arect(1);xmax=1-arect(2);ymin=-1+arect(4);ymax=-arect(3);
+    cur_ax = gca(),
+    cur_ax.clip_state = "off";
+    cur_ax.tag = "legends"     // for ged(6,#)
 
     dy = ymax-ymin ;
     yOffset = dy / 60 ;
 
-    drx=(xmax-xmin)/20 //length of the line
+    drx = (xmax-xmin)/20 //length of the line
     xOffset = drx/5 ;
 
-    bbx=[]
-    for k=1:nleg
+    bbx = []
+    for k = 1:nleg
         r = stringbox( leg(k), 0, 0, 0, old_ax.font_style, font_size ) ;
-        bbx=[bbx;r(1,3) - r(1,1) , r(2,3) - r(2,1)]; //[width height]
+        bbx = [bbx;r(1,3) - r(1,1) , r(2,3) - r(2,1)]; //[width height]
     end
 
     height = sum(bbx(:,2))
@@ -96,107 +103,115 @@ function legends(leg, style, opt, with_box, font_size )
         pos(1) = xmin + ((opt(1)-r2(1))/(r2(3)-r2(1)))*(1-arect(1)-arect(2))
         pos(2) = ymin + ((opt(2)-r2(2))/(r2(4)-r2(2)))*(1-arect(3)-arect(4))
         // end bugfix
-        opt=0 ;
+        opt = 0 ;
     elseif opt<1 | opt>6 then
-        error(msprintf(gettext("%s: Wrong value for input argument %s: Must be in the interval [%d, %d]."),"legends", "opt", 1, 6)); // if opt is entered by a string by the user it is in the range
+        msg = gettext("%s: Wrong value for input argument %s: Must be in the interval [%d, %d].")
+        error(msprintf(msg,"legends", "opt", 1, 6)); // if opt is entered by a string by the user it is in the range
     end
     select opt
     case 1 then
-        pos=[xmax-width-xOffset,ymax-yOffset]
+        pos = [xmax-width-xOffset, ymax-yOffset]
     case 2 then
-        pos=[xmin+xOffset,ymax-yOffset]
+        pos = [xmin+xOffset, ymax-yOffset]
     case 3 then
-        pos=[xmin+xOffset,ymin+height+yOffset]
+        pos = [xmin+xOffset, ymin+height+yOffset]
     case 4 then
-        pos=[xmax-width-xOffset,ymin+height+yOffset]
+        pos = [xmax-width-xOffset, ymin+height+yOffset]
     case 5 then
-        rect=dragrect([xmax-width-xOffset,ymax-yOffset,width,height])
-        pos=rect(1:2)
+        rect = dragrect([xmax-width-xOffset, ymax-yOffset,width,height])
+        pos = rect(1:2)
     case 6 then
         // decrease the size of the graph to leave place for the legend
-        old_ax.margins=[xmin;arect(2);arect(4);arect(3)+height + 2 * yOffset ]
-        pos=[(xmin+xmax)/2-width/2, ymin + height - 2 * yOffset ]
+        old_ax.margins = [xmin; arect(2); arect(4); arect(3)+height + 2 * yOffset ]
+        pos = [(xmin+xmax)/2-width/2, ymin + height - 2 * yOffset ]
     end
 
-    x=pos(1)+xOffset
-    y=pos(2)
+    x = pos(1) + xOffset
+    y = pos(2)
 
     f.immediate_drawing = "off",
-    a=gca() ;
-    a.foreground=old_ax.foreground ;
-    a.background=old_ax.background ;
-    a.font_color=old_ax.font_color ;
-    a.font_size =old_ax.font_size  ;
-    a.font_style=old_ax.font_style ;
-
-    R=[]
+    a = gca() ;
+    a.foreground = old_ax.foreground ;
+    a.background = old_ax.background ;
+    a.font_color = old_ax.font_color ;
+    a.font_size  = old_ax.font_size  ;
+    a.font_style = old_ax.font_style ;
+
+    R = []
     if with_box then
         xpol = [pos(1), pos(1)+width, pos(1)+width, pos(1)];
         ypol = [pos(2), pos(2), pos(2)-height, pos(2)-height];
         xfpoly(xpol, ypol,1)
         R = gce();
-        R.foreground=a.foreground;
-        R.background=a.background;
+        R.foreground = a.foreground;
+        R.background = a.background;
     end
-    for k=1:nleg
-        if k<=size(style,2) then
+    for k = 1:nleg
+        if k <= size(style,2) then
             if type(style)==9 then
-                h=style(k)
+                h = style(k)
                 select h.type
                 case "Polyline"
                     if h.polyline_style==5 then //patch
                         xfpoly([x;x+drx;x+drx;x;x],[y-bbx(k,2);y-bbx(k,2);y;y;y-bbx(k,2)]);
-                        r=gce();
+                        r = gce();
                         r = unglue(r); // one xfpoly returns 2 polylines -> tmp bug to fix later F.Leray
-                        r.foreground=h.foreground;
-                        r.thickness=h.thickness;
-                        r.polyline_style=h.polyline_style;
-                        r.line_style=h.line_style;
+                        r.foreground = h.foreground;
+                        r.thickness = h.thickness;
+                        r.polyline_style = h.polyline_style;
+                        r.line_style = h.line_style;
                     else
                         if stripblanks(h.mark_mode)=="off"
-                            xpoly([x;x+drx],[y;y]-bbx(k,2)/2,"lines");r=gce();
-                            r.foreground=h.foreground;
-                            r.thickness=h.thickness;
-                            r.polyline_style=h.polyline_style;
-                            r.line_style=h.line_style;
+                            xpoly([x;x+drx], [y;y]-bbx(k,2)/2, "lines");
+                            r = gce();
+                            r.foreground = h.foreground;
+                            r.thickness = h.thickness;
+                            r.polyline_style = h.polyline_style;
+                            r.line_style = h.line_style;
                         else
-                            xpoly(x+drx/2,y-bbx(k,2)/2);r=gce();
-                            r.foreground=h.foreground;
-                            r.thickness=h.thickness;
-                            r.mark_style=h.mark_style;
-                            r.mark_size=h.mark_size;
+                            xpoly(x+drx/2, y-bbx(k,2)/2);
+                            r = gce();
+                            r.foreground = h.foreground;
+                            r.thickness  = h.thickness;
+                            r.mark_style = h.mark_style;
+                            r.mark_size  = h.mark_size;
                         end
                     end
                 else
-                    error(msprintf(gettext("%s: Wrong type for input argument #%d: Polyline handles expected.\n"),"legends",2));
+                    msg = _("%s: Wrong type for input argument #%d: Polyline handles expected.\n")
+                    error(msprintf(msg,"legends",2));
                 end
             else
                 if style(1,k)<= 0 then
-                    xpoly(x+drx/2,y-bbx(k,2)/2)
-                    r=gce(),
-                    r.mark_mode="on"
-                    r.mark_style=-style(1,k)
-                    if size(style,1)==2 then r.mark_foreground=style(2,k);end
+                    xpoly(x+drx/2, y-bbx(k,2)/2)
+                    r = gce(),
+                    r.mark_mode  = "on"
+                    r.mark_style = -style(1,k)
+                    if size(style,1)==2 then
+                        r.mark_foreground = style(2,k);
+                    end
                 else
-                    xpoly([x;x+drx],[y;y]-bbx(k,2)/2,"lines")
-                    r=gce(),
-                    r.foreground=style(1,k)
-                    if size(style,1)==2 then r.line_style=style(2,k);end
+                    xpoly([x;x+drx], [y;y]-bbx(k,2)/2, "lines")
+                    r = gce()
+                    r.foreground = style(1,k)
+                    if size(style,1)==2 then
+                        r.line_style = style(2,k);
+                    end
                 end
             end
         end
-        R=[R,r']
-        xstring(x + drx*1.2 + bbx(k,1)/2,y-bbx(k,2)/2,leg(k))
+        R = [R, r']
+        xstring(x + drx*1.2 + bbx(k,1)/2, y-bbx(k,2)/2, leg(k))
 
-        r=gce()
+        r = gce()
         r.font_size = font_size ;
         r.alignment = "center" ;
         r.text_box_mode = "centered" ; // the string is centered on (x,y)
-        R=[R,r]
-        y=y-bbx(k,2) ;
+        R = [R, r]
+        y = y - bbx(k,2) ;
     end
     glue(R)
-    a=gca();
+    a = gca();
     a.data_bounds = [0,0;0.001,0.001];
 
     set("current_axes",old_ax),
diff --git a/scilab/modules/graphics/tests/unit_tests/ged_6_move_objects.tst b/scilab/modules/graphics/tests/unit_tests/ged_6_move_objects.tst
new file mode 100644 (file)
index 0000000..c511dc5
--- /dev/null
@@ -0,0 +1,142 @@
+// =============================================================================
+// Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
+// Copyright (C) 2017 - Samuel GOUGEON
+//
+//  This file is distributed under the same license as the Scilab package.
+// =============================================================================
+// <-- TEST WITH GRAPHIC -->
+// <-- INTERACTIVE TEST -->
+//
+// Unitary tests for ged(6,figure)
+//  = interactively selects and moves objects in a figure
+
+// 1) Executes the following block of instructions to generate the test figure:
+// ==================================
+function move_test(ptitle)
+    x = linspace(-2,2,150);
+    y = exp(2*x).*(1.05+sin(5*x));
+    y2 = exp(2*x).*(1.05+cos(5*x));
+    y3 = exp(x).*(1.05+cos(10*x));
+    plot2d(x,[y', y2' y3'])
+    ax = gca();
+    ax.log_flags = "nln";
+    ax.axes_reverse(1) = "on";
+    ax.grid_style = [8 8];
+    ax.sub_ticks = [1 8];
+    xgrid(color("grey80"))
+    // Labels
+    xtitle([ptitle; "row #2"],"Abscissae (inverted)", "Ordinates (log)")
+    // Text
+    xstring(0.8, 0.02, "Message");
+    e = gce();
+    e.font_size = 3;
+    e.box = "on";
+    xstring(0.5, 0.005, "Titled text");
+    e = gce();
+    e.font_size = 2;
+    e.font_angle = -30;
+    //xstring(-0.5, 3, "Centered");  // bugged
+    //e = gce();
+    //e.font_size = 2;
+    //e.text_box_mode = "centered";
+
+    // Segs - arrows
+    x = linspace(0,2*%pi,20);
+    xv = [sin(x); 9*sin(x)]/20 + 0.4;
+    yv = [cos(x); 9*cos(x)]*2 + 30;
+    xsegs(xv,yv,color("cyan"))
+    e = gce();
+    e.arrow_size = 1;
+    // Rectangle
+    xrect(-0.6, 20, 0.3, 14)
+    e = gce();
+    e.thickness = 2;
+    e.foreground = color("red");
+    // Datatip
+    d = datatipCreate(ax.children($).children(1),[-1.14 0.6]);
+    d.detached_position = [-1.2 1 0];
+    // Arc
+    xarc(0.9, 3.181, 0.6, 3.18, 120*64, 250*64)
+    e = gce();
+    e.foreground = color("orange");
+    e.thickness = 2;
+    // Legends from legends() or legend()
+    legend(['cos(t)';'cos(2*t)';'cos(3*t)']); // "Legend" type
+    legends(['sin(t)';'sin(2*t)';'sin(3*t)'],[-1,2 3],opt="ll") // "Axes" type
+    replot tight
+    // colorbar
+    c = jetcolormap(50);
+    gcf().color_map = [gcf().color_map ; c];
+    colorbar(-5,10,[35 85]);
+    //gce().parent.axes_bounds(3) = 0.06;
+endfunction
+clf reset
+subplot(1,2,1)
+move_test("Subplot")
+subplot(2,2,2)
+plot2d()
+xtitle("plot2d() example","Abscissae X","Ordinates Y")
+
+// Uicontrols
+// ----------
+f = gcf();
+    // listbox
+h = uicontrol(f,'style','listbox','units','normalized','position', [0.55 0.1 0.1 0.15]);
+h.string = "item 1|item 2|item3";   // fill the list
+h.value = 2;
+    // slider
+h = uicontrol(f,'style','slider', 'string', "Slider", ..
+    'units','normalized','position', [0.68 0.1 0.03 0.15], ..
+    'min', -3, 'max', 10, 'value', 1, 'sliderstep', [0.1 1]);
+h = uicontrol(f,'style','slider', ..
+    'units','normalized','position', [0.55 0.05 0.17 0.030], ..
+    'min', -3, 'max', 10, 'value', 1, 'sliderstep', [0.1 1]);
+    // popup menu
+h = uicontrol(f,'style','popupmenu', 'position', [550 175 100 20], ..
+    'string', ["Option 1" "Option 2" "$x^{-3}$"], 'value', 2);
+    // Checkbox
+h = uicontrol(f,'style','checkbox', ..
+    'position', [400 175 130 20], ..
+    'string', "Absolute position", 'value', 1);
+    // Radio buttons
+h = uicontrol(f,'style','radiobutton', 'position', [400 150 130 20], ..
+    "min", 0, "max", 3, "value", 3, ..
+    'string', "Resize window & see", "groupname", "test");
+h = uicontrol(f,'style','radiobutton', 'position', [400 125 130 20], ..
+    'string', "Alternative", "groupname", "test");
+
+// Second separated figure
+// -----------------------
+scf();
+subplot(2,1,2)
+plot2d()
+xtitle("plot2d() example","Abscissae X","Ordinates Y")
+// ==================================
+
+// 2) Click on the "Move objects" icon in the graphic toolbar
+//   or in the graphic "Edit => Move objects" menu.
+//
+// 3) Check that the proper message is displayed in the console:
+//    "WARNING : Mouse moving picker ON => Console LOCKED
+//    On a figure: Click left to get and set. Click middle to QUIT..."
+//
+// 4) Test moving each type of object:
+//    - the whole axes: (handle = its box)
+//    - both blocks of legends (generated with legend() or with legends())
+//    - the boxed "Message" plotted with xstring() (inside the box)
+//    - the "Tilted text" plotted with xstring() (inside the box)
+//    - one of the curves, displayed with plot2d()
+//    - the detached datatip linked to the green curve (inside the box)
+//    - the set of cyan segments with arrows (one of its segments)
+//    - the red rectangle (border)
+//    - the orange arc (arc)
+//    - the colorbar (small border)
+//    - each uicontrol (border): listbox, popupmenu, sliders.
+//      Choose a border point to drag the control ; avoid to push it.
+//    - Titles of the plot2d() example in linear scaled
+//
+// 5) Zoom in the semi-log axes, and make the same tests as here-above
+//
+// 6) Click the middle button, and check that the proper message is displayed
+//    in the console: "// Moving picker OFF => Back to the console."
+
index 3f64626..8a2b17b 100644 (file)
         <submenu label="&amp;Axes properties">
             <callback instruction='ged(9,[SCILAB_FIGURE_ID]);' type="0"/>
         </submenu>
+        <submenu label="&amp;Move objects">
+            <callback instruction='ged(6,[SCILAB_FIGURE_ID]);' type="0"/>
+        </submenu>
         <separator/>
-        <submenu label="&amp;Start entity picker">
+        <submenu label="Start entity &amp;picker">
             <callback instruction='ged(10,[SCILAB_FIGURE_ID]);' type="0"/>
         </submenu>
         <submenu label="S&amp;top entity picker">
             <callback instruction='ged(11,[SCILAB_FIGURE_ID]);' type="0"/>
         </submenu>
         <separator/>
-        <submenu label="&amp;Start datatip manager" icon="datatips">
+        <submenu label="Start &amp;datatip manager" icon="datatips">
             <callback instruction='org.scilab.modules.gui.datatip.DatatipManager.start([SCILAB_FIGURE_ID])' type="3"/>
         </submenu>
         <submenu label="Stop datatip manager">
index ed40841..d5c41f5 100644 (file)
@@ -32,6 +32,9 @@
     <button icon="data_modify" tooltiptext="Toggle curve data modification" toggle="true">
         <callback instruction="useeditor([SCILAB_FIGURE_ID])" type="-2"/>
     </button>
+    <button icon="ged_move" tooltiptext="Move objects">
+        <callback instruction="ged(6,[SCILAB_FIGURE_ID])" type="-2"/>
+    </button>
     <separator/>
     <!-- Help -->
     <button icon="help-browser" tooltiptext="Help Browser">
diff --git a/scilab/modules/gui/images/icons/16x16/actions/ged_move.png b/scilab/modules/gui/images/icons/16x16/actions/ged_move.png
new file mode 100644 (file)
index 0000000..d398895
Binary files /dev/null and b/scilab/modules/gui/images/icons/16x16/actions/ged_move.png differ
diff --git a/scilab/modules/tclsci/macros/dist2Arc.sci b/scilab/modules/tclsci/macros/dist2Arc.sci
deleted file mode 100644 (file)
index aba4acb..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-// Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
-// Copyright (C) INRIA
-// Copyright (C) 2012 - 2016 - Scilab Enterprises
-//
-// This file is hereby licensed under the terms of the GNU GPL v2.0,
-// pursuant to article 5.3.4 of the CeCILL v.2.1.
-// This file was originally licensed under the terms of the CeCILL v2.1,
-// and continues to be available under such terms.
-// For more information, see the COPYING file which you should have received
-// along with this program.
-// compute the distance between a point and the arc
-// in 2D included in an axis aligned rectangle whose upper left
-// corner is upperLeft and its wifth and heigth is defined.
-function [dist,diffClose] = dist2Arc( point, upperLeft, width, heigth, sector1, sector2 )
-
-
-    if ( width == 0 | heigth == 0 ) then
-        dist = %inf ;
-        diffClose = [%inf,%inf];
-        return ;
-    end
-
-    // convert the sector into radiant angle
-    angle1 =  sector1            * %pi / 180. ;
-    angle2 = (sector1 + sector2) * %pi / 180. ;
-
-    width2  = width  / 2. ;
-    heigth2 = heigth / 2. ;
-    centerC = [ upperLeft(1) + width2, upperLeft(2) - heigth2 ] ; // center of the ellipse
-
-    // clicked point in the circle frame
-    pointC  = [ (point(1) - centerC(1)) / width2, (point(2) - centerC(2)) / heigth2 ] ;
-
-    // get the projection of the clicked point on the circle
-    closest = pointC / norm( pointC ) ;
-
-    // now a quite tricky part. The aim is to find
-    // if the closest point is in the drawing sector
-    // ie if it is between bound1 and bound2 on the circle
-    // maybe a eayer solution exists.
-
-    // get the boundaries of the displayed angle
-    // the closest point is not on the arc it is one of the two
-    // boundaries
-    bound1 = [cos(angle1),sin(angle1)] ;
-    bound2 = [cos(angle2),sin(angle2)] ;
-
-    // now get the vector of bissecting line between the two bounds
-    // with the orientation toward the arc
-    b2b1       = bound1 -  bound2 ;
-    bissect(1) = -b2b1(2)         ;
-    bissect(2) =  b2b1(1)         ;
-
-    // get the position of the point along this axis
-    side = closest(1) * bissect(1) + closest(2) * bissect(2) ;
-
-    // get the position of one of the bound (same value for both)
-    boundPos = bound1(1) * bissect(1) + bound1(2) * bissect(2) ;
-
-    if side > boundPos  then
-        // the closest point is on the arc
-        diffClose = ( pointC - closest ) .* [width2,heigth2] ;
-        // bring it back to the current frame value
-        //diffclose = diffclose .* [width2,heigth2] ;
-
-        // get the distance with the closest point
-        dist = norm( diffClose ) ;
-
-    else
-        // the closest point is one of the bounds
-        // return back to the real coordinates
-        bound1 = centerC + bound1 .* [width2,heigth2];
-        bound2 = centerC + bound2 .* [width2,heigth2];
-
-        // get the minimum distance
-        dist  = norm( bound1 - point ) ;
-        dist2 = norm( bound2 - point ) ;
-        if dist > dist2 then
-            diffClose = bound1 - point ;
-        else
-            dist = dist2 ;
-            diffClose = bound2 - point ;
-        end
-        //dist = min( norm( bound1 - point ), norm( bound2 - point ) ) ;
-    end
-
-endfunction
diff --git a/scilab/modules/tclsci/macros/dist2Ellipse.sci b/scilab/modules/tclsci/macros/dist2Ellipse.sci
deleted file mode 100644 (file)
index 0163015..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// compute the square of distance between a point and the ellipse
-// in 2D included in an axis aligned rectangle whose upper left
-// corner is upperLeft and its wifth and heigth is defined.
-function [dist] = dist2Ellipse( point, upperLeft, width, heigth )
-    width2  = width  / 2. ;
-    heigth2 = heigth / 2. ;
-    centerC = [ upperLeft(1) + width2, upperLeft(2) - heigth2 ] ; // center of the ellipse
-
-    // clicked point in the circle frame
-    pointC  = [ (point(1) - centerC(1)) / width2, (point(2) - centerC(2)) / heigth2 ] ;
-
-    // get the vector between the point and the closest on the circle
-    diffclose = ( 1 - 1 / norm( pointC ) ) * pointC ;
-    //closest = pointC / sqrt( pointC(1) * pointC(1) + pointC(2) * pointC(2) ) ;
-
-    // get the difference between the two
-    //ffclose = pointC - closest ;
-
-    // bring it back to the current frame value
-    diffclose(1) = diffclose(1) * width2  ;
-    diffclose(2) = diffclose(2) * heigth2 ;
-
-    // get the distance with the closest point
-    dist = norm( diffclose ) ;
-
-endfunction
index 7f8e4e9..6bbba43 100644 (file)
@@ -1,6 +1,7 @@
 // Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
 // Copyright (C) INRIA
 // Copyright (C) 2012 - 2016 - Scilab Enterprises
+// Copyright (C) 2017 - Samuel GOUGEON
 //
 // This file is hereby licensed under the terms of the GNU GPL v2.0,
 // pursuant to article 5.3.4 of the CeCILL v.2.1.
 // For more information, see the COPYING file which you should have received
 // along with this program.
 
-function [h,Axes]=ged_getobject(pt)
-    h=[];Axes=[];
-    f=get("current_figure");
-    aold=get("current_axes")
-    axes_array=f.children
-    //retains only the entities of type Axes (remove uimenus)
-    axes_array(axes_array.type<>"Axes")=[];
-    // assume that the lastly created objects
-    // are at the beginning of the arrays of children
-    // We can then select the last object
-    // first in the loop.
-    for k=1:size(axes_array,"*")
-        Axes=axes_array(k)
-        set("current_axes",Axes)
-        h=ged_loop(Axes,pt)
+function [h, Axes] = ged_getobject(pt)
+    // Internal private function called by ged_move_entity()
+    // pt: [x,y] coordinates of the last left mouse click, in pixels from top-left
+    // h: Handle of the graphic object
+    h = [];
+    Axes = [];
+    f = gcf();
+    aold = gca();
+    Children = f.children
+    //retains only the entities of type Axes or uicontrol (remove uimenus)
+    axes_array = Children(Children.type=="Axes");
+    uicontrol_array = Children(Children.type=="uicontrol");
+    // Axes
+    for k = 1:size(axes_array,"*")
+        Axes = axes_array(k)
+        set("current_axes", Axes)
+        h = ged_loop(Axes, pt)
         if h<>[] then break,end
     end
-    set("current_axes",aold)
+    if h~=[] then
+        set("current_axes",aold)
+        return
+    end
+    // uicontrols
+    Axes = f;
+    h = ged_loop(uicontrol_array, pt)
 endfunction
index ed6f91f..558b3fd 100644 (file)
@@ -1,6 +1,7 @@
 // Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
 // Copyright (C) INRIA
 // Copyright (C) 2012 - 2016 - Scilab Enterprises
+// Copyright (C) 2017 - Samuel GOUGEON
 //
 // This file is hereby licensed under the terms of the GNU GPL v2.0,
 // pursuant to article 5.3.4 of the CeCILL v.2.1.
 // For more information, see the COPYING file which you should have received
 // along with this program.
 
-function h=ged_loop(a,pt)
+function h = ged_loop(a,pt)
+    // Internal private function called by ged_getobject()
+    // a: handle of the object to scan.
+    // pt: position of the mouse pointer, in pixels from the top-left
+    //
+    // ged_loop() scans all existing graphic objects in the given Axes/object,
+    //  and try to find one of them with its handle in the pointer vicinity.
+    //  If an object is found, its handle h is returned.
 
     h=[]
     minDist    = 0.01 ;
     minPixDist = 3    ;
 
-    for ka=1:size(a,"*")
+    for ka = 1:size(a,"*")    // size>1 for compounds
         ck=a(ka) ;
         select ck.type
 
         case "Polyline"
-            xy=ck.data;
-            d=Dist2polyline((xy(:,1)-Xmin)/Dx,(xy(:,2)-Ymin)/Dy,pts)
-            if d < minDist then h=ck,return,end
+            x = ck.data(:,1)
+            y = ck.data(:,2)
+            [xn, yn] = ged_get_normalized_axes_xy(x,y)
+            d = Dist2polyline(xn, yn, pts)
+            if d < minDist then
+                h = ck
+                return
+            end
+            h = ged_loop(ck.datatips,pt)
+            if h<>[] then
+                return
+            end
 
         case "Rectangle"
-            xy=ck.data;
-            x0=xy(1);y0=xy(2);W=xy(3);H=xy(4);
-            d=Dist2polyline((x0+[0,W,W,0]-Xmin)/Dx,(y0+[0,0,-H,-H]-Ymin)/Dy,pts)
-            if d < minDist then h=ck,return,end
-
-        case "Arc"
-            xy=ck.data;
-            [xp,yp]=xchange(pt(1),pt(2),"i2f")
-            //[dist, toto] = dist2Arc( [xp,yp] ./ [Dx,Dy], xy(1:2)./[Dx,Dy], xy(3)/Dx, xy(4)/Dy, xy(5) / 64., xy(6) / 64. ) ;
-            dist = pixDist2Arc( [xp,yp], xy(1:2), xy(3), xy(4), xy(5) / 64., xy(6) / 64. ) ;
-            if dist <= minPixDist then
-                h=ck;
-                return;
+            xy = ck.data;
+            [x0, y0, w, h] = (xy(1), xy(2), xy(3), xy(4))
+            x = [x0  x0+w  x0+w  x0  x0]
+            y = [y0   y0   y0-h y0-h y0]
+            [xn, yn] = ged_get_normalized_axes_xy(x,y)
+            d = Dist2polyline(xn, yn, pts)
+            if d < minDist then
+                h = ck
+                return
+            end
+
+         case "Arc"
+            // The area of detection is not the whole enclosing rectangle, but
+            // only the given arc.
+            xy = ck.data;
+            [x, y, w, h, a0, da] = (xy(1), xy(2), xy(3), xy(4), xy(5)/64, xy(6)/64)
+            pax = ck.parent
+            xc = x + w/2
+            yc = y - h/2
+            A = linspace(a0, a0+da, max(ceil(da),3))
+            x = xc + w/2*cos(A)
+            y = yc + h/2*sin(A)
+            [xn, yn] = ged_get_normalized_axes_xy(x,y)
+            d = Dist2polyline(xn, yn, pts)
+            if d < 2*minDist then
+                h = ck
+                return
             end
 
         case "Segs"
-            xy=ck.data;
-            xv=(matrix(xy(:,1),2,-1)-Xmin)/Dx
-            yv=(matrix(xy(:,2),2,-1)-Ymin)/Dy
-            for ks=1:size(xv,2)
-                d=Dist2polyline(xv(:,ks),yv(:,ks),pts)
-                if d < minDist then h=ck,return,end
+            xy = ck.data;
+            xv = matrix(xy(:,1), 2, -1).'
+            yv = matrix(xy(:,2), 2, -1).'
+            [xn, yn] = ged_get_normalized_axes_xy(xv, yv)
+            for ks = 1:size(xn,1)
+                d = Dist2polyline(xn(ks,:).', yn(ks,:).', pts)
+                if d < minDist then h = ck, return, end
             end
-        case "Compound"
-            h=ged_loop(ck.children,pt)
-            if h<>[] then return,end
 
-        case "Axes"
-            xy=ck.data_bounds;
-            [xp,yp]=xchange(pt(1),pt(2),"i2f")
-            Xmin=xy(1,1);Ymin=xy(1,2),Dx=xy(2,1)-xy(1,1);Dy=xy(2,2)-xy(1,2);
-            pts=[(xp-Xmin)/Dx (yp-Ymin)/Dy]
-            d=Dist2polyline([0,1,1,0],[0,0,1,1],pts)
-            if d < minDist then h=ck,return,end
-            h=ged_loop([a.children(:);ck.x_label;ck.y_label;ck.z_label;ck.title],pt)
+        case "Compound"
+            h = ged_loop(ck.children,pt)
             if h<>[] then return,end
 
         case "Text"
-            if is_in_text(ck,[xp;yp]) then
-                h=ck,
-                return,
+            b = mystringbox(ck)
+            if xp0>=b(1) & xp0<=b(2) & yp0>=b(3) & yp0<=b(4)
+                h = ck
+                return
             end
 
         case "Label"
-            if is_in_text(ck,[xp;yp]) then
-                h=ck
-                return,
+            b = mystringbox(ck)
+            if xp0>=b(1) & xp0<=b(2) & yp0>=b(3) & yp0<=b(4)
+                h = ck
+                return
+            end
+
+        case "Legend"
+            // Is the click for the legend box?
+            // Whatever is the filling mode of the block, we consider that no
+            //  external element on the legend background can be moved.
+            // BTW, the legend content of the box is frozen and can't either
+            // be moved inside it. So, the click is for the legend block as a
+            // whole if it is within the legend frame:
+            b = ged_legend_box_in_data_units(ck)
+            if (xp0>=b(1) & xp0<=b(2)) & (yp0>=b(3) & yp0<=b(4))
+                h = ck
+                return
             end
 
+        case "Datatip"
+            // Only boxes of detached datatips can be moved
+            // Reversed and log axes supported
+            p = ck.detached_position
+            if p~=[]
+                b = mystringbox(ck)
+                if (xp0>=b(1) & xp0<=b(2)) & (yp0>=b(3) & yp0<=b(4))
+                    h = ck
+                    return
+                end
+            end
+
+        case "Axes"
+            //sca(ck);
+            [xp0, yp0] = xchange(pt(1),pt(2),"i2f")
+            xp = xp0
+            yp = yp0
+            if ck.zoom_box==[]
+                db = ck.data_bounds;
+            else
+                db = ck.zoom_box([1 3 2 4])
+            end
+            if ck.tag=="legends"
+                // micro-axes generated by legends()
+                // The frame is this polygon:
+                p = ck.children.children($).data
+                xmin = p(1,1), xmax = p(2,1)
+                ymin = p(4,2), ymax = p(1,2)
+                if xp>=xmin & xp<=xmax & yp>=ymin & yp<=ymax
+                    h = ck  // We return the whole axes to move it as a whole
+                    return
+                end
+            else
+                Xlog = part(ck.log_flags,1)=="l"
+                if Xlog
+                    xp = log10(xp)
+                    db(1:2) = log10(db(1:2))
+                end
+                Ylog = part(ck.log_flags,2)=="l"
+                if Ylog
+                    yp = log10(yp)
+                    db(3:4) = log10(db(3:4))
+                end
+                [Xmin, Xmax, Ymin, Ymax] = (db(1), db(2), db(3), db(4))
+                Dx = Xmax - Xmin
+                Dy = Ymax - Ymin
+                Xreversed = ck.axes_reverse(1)=="on"
+                if ~Xreversed
+                    dx = xp - Xmin
+                else
+                    dx = Xmax - xp
+                end
+                Yreversed = ck.axes_reverse(2)=="on"
+                if ~Yreversed
+                    dy = yp - Ymin
+                else
+                    dy = Ymax - yp
+                end
+                pts = [dx/Dx dy/Dy]
+                d = Dist2polyline([0,1,1,0,0],[0,0,1,1,0],pts)
+                if d < minDist then
+                    h = ck
+                    return
+                end
+                // Restoring linear xp,yp (for recursive calls)
+                xp = xp0, yp = yp0
+                if length(a.children)>0
+                    h = ged_loop([ck.children(:);ck.x_label;ck.y_label;ck.z_label;ck.title],pt)
+                    if h<>[] then
+                        return
+                    end
+                end
+            end
+
+        case "uicontrol"
+            tmp = gcf();
+            wh = tmp.axes_size;   // width, height
+            xy = ck.position
+            // ck.units == {pixels} | points | normalized
+            if ck.units=="normalized"   // => converts into pixels:
+                xy([1 3]) = xy([1 3])*wh(1);
+                xy([2 4]) = xy([2 4])*wh(2);
+            else
+                // "points" case: to be implemented
+            end
+            [x0, y0, w, h] = (xy(1), wh(2)-xy(2)-xy(4), xy(3), xy(4))
+            // y0: position of the top-left corner of the uicontrol, wrt to the
+            //     top-left corner of the figure
+            // [xn, yn] = ged_get_normalized_axes_xy(x,y)
+            dd = minDist * sqrt(sum(wh.^2));
+            if pt(1)>x0-dd & pt(1)<(x0+w+dd) & pt(2)>y0-dd & pt(2)<y0+h+dd
+                h = ck
+                return
+            end
+            // RECURSIVE CALLS TO BE IMPLEMENTED (into tabs and frames)
+            // The origin is on the top left corner no longer of the figure
+            // but of the parent frame, as well for xchange()
+            // => code to be overhauled
+            // (As well, axes in frames are not zoomable not pannable)
+            //if ck.style=="frame"
+            //    // It may have axes:
+            //    h = ged_loop(ck.children(:), pt)
+            //    if h<>[] then
+            //        return
+            //    end
+            //end
         end
     end
 endfunction
+function b = mystringbox(e)
+    // Internal private function for ged_loop()
+    // Works in lin & log scales, but only for angle = 0
+    // e: handle of type "Text" or "Label" or "Datatip"
+    a = gca();
+    db = a.data_bounds;
+    ang = 0;
+    if or(e.type==["Text" "Label"]) then
+        ang = e.font_angle;
+    end
+    ud = e.user_data
+    if typeof(ud)~="st" | ~isfield(ud,"pixbox") | ..
+        ud.text ~= e.text | or(ud.params ~= [ang e.font_style e.font_size]) then
+        drawlater   // to avoid flashing
+        old_flags = a.log_flags;
+        a.log_flags = "nnn";
+        b = stringbox(e.text, db(1), db(3), ang, e.font_style, e.font_size);
+        [xpix, ypix] = xchange(b(1,:), b(2,:), "f2i");
+        a.log_flags = old_flags;
+        drawnow
+        xpix = xpix - xpix(1);
+        ypix = ypix - ypix(1);
+        e.userdata.pixbox = [xpix ; ypix];
+        e.userdata.params = [ang e.font_style e.font_size];  // hash
+        e.userdata.text = e.text;                            // hash
+    else
+        tmp =  e.userdata.pixbox;
+        xpix = tmp(1,:);
+        ypix = tmp(2,:);
+    end
+    // Position of the pivot (default = bottom left)
+    if e.type=="Text"
+        pos = e.data;
+    elseif e.type=="Label"
+        pos = e.position
+    else    // Detached datatip
+        pos = e.detached_position
+    end
+    [xp0, yp0] = xchange(pos(1), pos(2), "f2i");
+    if ~Xlog & ~Ylog & ..           // centered xstring() bugged in log modes
+       e.type=="Text" & e.text_box_mode=="centered" & and(e.text_box==[0 0])
+        xpix = xpix - strange(xpix)/2
+        ypix = ypix - strange(ypix)/2
+    end
+    [xbox, ybox] = xchange(xp0 + xpix, yp0 + ypix, "i2f");
+    b = [ min(xbox) max(xbox) min(ybox) max(ybox) ];
+endfunction
+// function r=is_in_text(h,xy)
+//    if h.Type == "Text" & h.text_box_mode=="filled" then
+//        r=(xy(1)>h.data(1)&xy(1)<h.data(1)+h.text_box(1))&(xy(2)>h.data(2)&xy(2)<h.data(2)+h.text_box(2))
+//    else
+//        r = stringbox(h);
+//        r=[r r(:,1)];
+//        r=and([xy(2) -xy(1)]*diff(r,1,2)+(r(1,1:$-1).*r(2,2:$)-r(1,2:$).*r(2,1:$-1))<0)
+//    end
+//endfunction
+
+function [xn,yn] = ged_get_normalized_axes_xy(x,y)
+    // Internal private function for ged_loop()
+    if Xlog
+        k = find(x<=0)
+        x(k) = 1e-308
+        x = log10(x)
+    end
+    if Ylog
+        k = find(y<=0)
+        y(k) = 1e-308
+        y = log10(y)
+    end
+    if ~Xreversed
+        xn = (x - Xmin)/Dx
+    else
+        xn = (Xmax - x)/Dx
+    end
+    if ~Yreversed
+        yn = (y - Ymin)/Dy
+    else
+        yn = (Ymax - y)/Dy
+    end
+endfunction
+
+function rect = ged_legend_box_in_data_units(idLeg)
+    // Internal private function for ged_loop()
+    // Returns rect = [xmin xmax ymin ymax] of the legend box.
+    // * reversed and log axes are implemented
+    // * axes.tight_limits="off" may slightly disturb results (no workaround)
+    // * The box size is set only according to the text box of legends.
+    // legend.position are the coordinates of the top-left corner of the
+    //  legend box, in gca().axes_bounds normalized units
+    ax = idLeg.parent
+    db = ax.data_bounds
+
+    // Getting the relative width and height of the block of text in linear scales
+    if typeof(idLeg.userdata)~="st" | ~isfield(idLeg.userdata,"relwidth") | ..
+        idLeg.userdata.text ~= idLeg.text then
+        drawlater
+        old_flags = ax.log_flags;
+        ax.log_flags = "nnn";
+        b = xstringl(db(1), db(3), idLeg.text, idLeg.font_style, idLeg.font_size);
+        ax.log_flags = old_flags
+        drawnow
+        relwidth  = b(3)/(db(2)-db(1))
+        relheight = b(4)/(db(4)-db(3))
+        idLeg.userdata.relwidth = relwidth
+        idLeg.userdata.relheight = relheight
+        idLeg.userdata.text = idLeg.text
+    else
+        relwidth = idLeg.userdata.relwidth
+        relheight = idLeg.userdata.relheight
+    end
+    // x position:
+    xlog = part(ax.log_flags,1)=="l"
+    if xlog
+        db(1:2) = log10(db(1:2))
+    end
+    tmp = (idLeg.position(1) - ax.margins(1)) / (1-sum(ax.margins(1:2)))* ..
+            (db(2)-db(1))
+    if ax.axes_reverse(1)=="off"
+        x0 = db(1) + tmp
+    else
+        x0 = db(2) - tmp
+    end
+
+        // y position:
+    ylog = part(ax.log_flags,2)=="l"
+    if ylog
+        db(3:4) = log10(db(3:4))
+    end
+    tmp = (idLeg.position(2) - ax.margins(3)) / (1-sum(ax.margins(3:4)))* ..
+            (db(4)-db(3))
+    if ax.axes_reverse(2)=="off"
+        y0 = db(4) - tmp
+    else
+        y0 = db(3) + tmp
+    end
+
+    // For the time being, the legend block is considered to be
+    // as tall and as wide as the text block only
+    x1 = x0 + (1 - 2*(ax.axes_reverse(1)=="on")) * relwidth * (db(2)-db(1));
+    y1 = y0 - (1 - 2*(ax.axes_reverse(2)=="on")) * relheight * (db(4)-db(3));
+    x = [x0 x1];
+    y = [y0 y1];
+    if xlog, x = 10.^x; end
+    if ylog, y = 10.^y; end
+    rect = [min(x) max(x) min(y) max(y)];
+endfunction
index dc67c94..3f654cd 100644 (file)
+// Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
+// Copyright (C) INRIA
+// Copyright (C) 2012 - 2016 - Scilab Enterprises
+// Copyright (C) 2017 - Samuel GOUGEON
+//
+// This file is hereby licensed under the terms of the GNU GPL v2.0,
+// pursuant to article 5.3.4 of the CeCILL v.2.1.
+// This file was originally licensed under the terms of the CeCILL v2.1,
+// and continues to be available under such terms.
+// For more information, see the COPYING file which you should have received
+// along with this program.
+
 function ged_move_entity()
-    [btn,xc,yc]=xclick()
-    pos=[xc,yc]
-    [xc,yc]=xchange(xc,yc,"f2i")
-    [r,ax]=ged_getobject([xc,yc])
-    cur_ax=gca(),sca(ax)
-    [xc,yc]=xchange(xc,yc,"i2f");pos=[xc,yc]
-    if r==[] return,end
-    drawlater();
-    rep(3)=-1
-    select r.type
-    case "Rectangle" then
-        while rep(3)==-1 do
-            rep=xgetmouse([%t %t])
-            r.data(1:2)= r.data(1:2)+(rep(1:2)-pos)
-            pos=rep(1:2)
-            drawnow();
-        end
-    case "Segs" then //Segment
-        while rep(3)==-1 do
-            rep=xgetmouse([%t %t])
-            r.data=r.data+ones(2,1)*(rep(1:2)-pos)
-            pos=rep(1:2)
-            drawnow();
-        end
-    case "Polyline" then //Polyline
-        while rep(3)==-1 do
-            rep=xgetmouse([%t %t])
-            r.data(:,1:2)=r.data(:,1:2)+ones(r.data(:,1))*(rep(1:2)-pos)
-            pos=rep(1:2)
-            drawnow();
+    // Internal private function called by ged(6,fig)
+    //  - Follows the mouse
+    //  - When it is left or right clicked, tries to detect a graphical component
+    //    in its vicinity.
+    //  - If a component is detected,
+    //    - then moves it following the mouse movements
+    //    - sets its new position when the mouse is left or right clicked
+    //  -... until the center button is clicked or 15 s are elapsed without
+    //    moving the mouse on the figure.
+    //
+    //  BUG: When the mouse goes out of the figure where the object has been
+    //       picked, on another figure, the object may reappear on the current
+    //       figure, far from where the mouse stands.
+    //       See http://bugzilla.scilab.org/13913
+
+    f0 = gcf();
+    f = f0;
+    if f.default_axes=="off" then
+        // xclick() and xgetmouse() are disabled in such figures
+        return
+    end
+    ax0 = gca();                // Save it to restore it before leaving
+    btn = 0;
+    maxRestTime = 15; // s
+    warning(_("Mouse moving picker ON => Console LOCKED"))
+    mprintf(_("On a figure: Click left to get and set. Click middle to QUIT...\n"))
+    while ~or(btn==[1 4]);  // click or press middle button => Quit
+        tic()
+        [btn, xc, yc, nFig, cb] = xclick(); // [xc, yc] are in data scales
+        if nFig ~= f.figure_id
+            f = scf(nFig);
         end
-    case "Arc" then //Circle
-        while rep(3)==-1 do
-            rep=xgetmouse([%t %t])
-            r.data(1:2)= r.data(1:2)+(rep(1:2)-pos)
-            pos=rep(1:2)
-            drawnow();
+        if toc()>maxRestTime
+            break
         end
-    case "Text" then
-        while rep(3)==-1 do
-            rep=xgetmouse([%t %t])
-            r.data(1:2)= r.data(1:2)+(rep(1:2)-pos)
-            pos=rep(1:2)
-            drawnow();
+        pos = [xc,yc];
+        [xc,yc] = xchange(xc,yc,"f2i");  // [xc, yc] => in pixels. Origin on the top-left
+        [r,ax] = ged_getobject([xc,yc]); // <-- Detects the clicked object
+        cur_ax = gca()
+        if ax.type=="Axes"
+            sca(ax);
+            Xlog = part(ax.log_flags,1)=="l";
+            Ylog = part(ax.log_flags,2)=="l";
+            [xc,yc] = xchange(xc,yc,"i2f");
+        else    // uicontrols: coordinates in pixels
+            Xlog = %f;
+            Ylog = %f;
+            // [xc, yc] remain in pixels coordinates
         end
-    case "Label" then
-        while rep(3)==-1 do
-            rep=xgetmouse([%t %t])
-            r.position= r.position+(rep(1:2)-pos)
-            r.auto_position = "off"
-            pos=rep(1:2)
-            drawnow();
+        pos = [xc,yc]
+        if r~=[] & type(r)==9
+            rep(3) = -1;
+            //disp(r.type+" clicked")
+            select r.type
+            case "Rectangle" then
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    [nx, ny] = ged_newObjectPosition(r.data, rep, pos);
+                    // The sizes of the rectangle are not changed.
+                    // So in log scale(s) they are rendered in an elastic way.
+                    r.data(1:2) = [nx ny];
+                    pos = rep(1:2);
+                end
+            case "Segs" then //Segment
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    r.data = r.data + ones(r.data(:,1))*(rep(1:2)-pos);
+                    pos = rep(1:2);
+                end
+            case "Polyline" then //Polyline
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    r.data(:,1:2) = r.data(:,1:2) + ones(r.data(:,1))*(rep(1:2)-pos);
+                    pos = rep(1:2);
+                end
+            case "Arc" then //Circle
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    [nx, ny] = ged_newObjectPosition(r.data, rep, pos);
+                    // The sizes of the enclosing rectangle of the arc are not changed.
+                    // So in log scale(s) they are rendered in an elastic way.
+                    r.data(1:2) = [nx ny];
+                    pos = rep(1:2);
+                end
+
+            case "Text" then
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    [nx, ny] = ged_newObjectPosition(r.data, rep, pos);
+                    r.data = [nx ny];
+                    pos = rep(1:2);
+                end
+            case "Label" then
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    [nx, ny] = ged_newObjectPosition(r.position, rep, pos);
+                    r.auto_position = "off";
+                    r.position = [nx ny];
+                    pos = rep(1:2);
+                end
+            case "Axes" then
+                // The axes coordinates are in normalized units in the figure
+                cf = ged_coordinates_in_figure(ax, xc, yc);
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    ncf =  ged_coordinates_in_figure(ax, rep(1), rep(2));
+                    ax.axes_bounds(1:2) = ax.axes_bounds(1:2) + (ncf - cf);
+                    cf = ncf;
+                end
+            case "Datatip" then
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t])
+                    p = r.detached_position
+                    [nx, ny] = ged_newObjectPosition(p, rep, pos)
+                    r.detached_position = [nx ny p(3)]
+                    pos = rep(1:2)
+                end
+            case "Legend" then
+                // Legend block generated with legend()
+                // (legends() (with "s") sets an axes instead: move axes
+                //  not yet supported)
+                //
+                // * reversed and log axes supported
+                // * prone to slight error if axes.tight_limits=="off"
+                //   (no way to take it into account)
+                r.legend_location = "by_coordinates";
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    nx = rep(1); // New x position in data unit
+                    ny = rep(2); // New y position in data unit
+                    // Conversion in normalized axes units:
+                    db = ax.data_bounds;
+                    xlog = part(ax.log_flags,1)=="l";
+                    if xlog
+                        db(1:2) = log10(db(1:2));
+                        nx = log10(nx);
+                    end
+                    if ax.axes_reverse(1)=="off"
+                        nxa = (nx - db(1)) / (db(2)-db(1)) * ..
+                              (1-sum(ax.margins(1:2))) + ax.margins(1);
+                    else
+                        nxa = (db(2) - nx) / (db(2)-db(1)) * ..
+                              (1-sum(ax.margins(1:2))) + ax.margins(1);
+                    end
+                    ylog = part(ax.log_flags,2)=="l";
+                    if ylog
+                        db(3:4) = log10(db(3:4));
+                        ny = log10(ny);
+                    end
+                    if ax.axes_reverse(2)=="off"
+                        nya = (db(4) - ny) / (db(4)-db(3))  * ..
+                              (1-sum(ax.margins(3:4))) + ax.margins(3);
+                    else
+                        nya = (ny - db(3)) / (db(4)-db(3))  * ..
+                              (1-sum(ax.margins(3:4))) + ax.margins(3);
+                    end
+                    r.position = [nxa nya];
+                    pos = rep(1:2);
+                end
+
+            case "uicontrol"
+                Fx = 1; Fy = 1;
+                if r.units=="normalized"
+                    Fx = f.axes_size(1);
+                    Fy = f.axes_size(2);
+                end
+                while rep(3)==-1 do
+                    rep = xgetmouse([%t %t]);
+                    [x, y] = xchange(rep(1),rep(2), "f2i");
+                    r.position(1:2) = r.position(1:2) + [(x-pos(1))/Fx, (pos(2)-y)/Fy];
+                    pos = [x y];
+                end
+            end
+            sca(cur_ax);
         end
+    end
+    // Restoring initial current figure and axes:
+    scf(f0);
+    mprintf(_("\n--> // Moving picker OFF => Back to the console.\n"))
+endfunction
+
+function [nx, ny] = ged_newObjectPosition(objPos, pt, prevpt)
+    if ~Xlog
+        nx = objPos(1) + (pt(1)-prevpt(1))
+    else
+        nx = objPos(1)*(pt(1)/prevpt(1))
+    end
+    if ~Ylog
+        ny = objPos(2) + (pt(2)-prevpt(2))
+    else
+        ny = objPos(2)*(pt(2)/prevpt(2))
+    end
+endfunction
 
+function xy_in_figure = ged_coordinates_in_figure(idA, xdata,ydata)
+    // reversed and log axes supported
+    ab = idA.axes_bounds
+    db = idA.data_bounds
+    if part(idA.log_flags,1)=="l" then
+        db(1:2) = log10(db(1:2))
+        xdata = log10(xdata)
+    end
+    dx = xdata - db(1)
+    if idA.axes_reverse(1)=="on" then
+        dx = db(2) - xdata
+    end
+    if part(idA.log_flags,2)=="l" then
+        db(3:4) = log10(db(3:4))
+        ydata = log10(ydata)
+    end
+    dy = db(4) - ydata
+    if idA.axes_reverse(2)=="on" then
+        dy = ydata - db(3)
     end
-    sca(cur_ax)
+    xf = (dx/(db(2)-db(1))*(1-sum(idA.margins(1:2))) + idA.margins(1))*ab(3) + ab(1)
+    yf = (dy/(db(4)-db(3))*(1-sum(idA.margins(3:4))) + idA.margins(3))*ab(4) + ab(2)
+    xy_in_figure = [xf yf]
 endfunction
diff --git a/scilab/modules/tclsci/macros/pixDist2Arc.sci b/scilab/modules/tclsci/macros/pixDist2Arc.sci
deleted file mode 100644 (file)
index 8fdae0e..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-// Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
-// Copyright (C) INRIA
-// Copyright (C) 2012 - 2016 - Scilab Enterprises
-//
-// This file is hereby licensed under the terms of the GNU GPL v2.0,
-// pursuant to article 5.3.4 of the CeCILL v.2.1.
-// This file was originally licensed under the terms of the CeCILL v2.1,
-// and continues to be available under such terms.
-// For more information, see the COPYING file which you should have received
-// along with this program.
-
-// same as before but return the value in pixels
-function dist = pixDist2Arc( point, upperLeft, width, heigth, sector1, sector2 )
-
-    [dist, difference] = dist2Arc( point, upperLeft, width, heigth, sector1, sector2 ) ;
-    // convert to pixels
-    // get the length of the difference vector
-    // we construct it by getting two points
-    [origin(1),origin(2)] = xchange(0,0,"f2i");
-    [extremity(1),extremity(2)] = xchange(difference(1),difference(2),"f2i");
-    dist = norm( extremity - origin ) ;
-
-endfunction