ImageEn for Delphi and C++ Builder ImageEn for Delphi and C++ Builder

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
Forum membership is Free!  Click Join to sign-up
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 miSelect not working
 New Topic  Reply to Topic
Author Previous Topic Topic Next Topic  

timfa

USA
8 Posts

Posted - Oct 04 2024 :  13:04:03  Show Profile  Reply
Hello,

I'm trying to display a selection rectangle in an image using "TImageEnView.MouseInteractGeneral := [miSelect];" to setup the correct mode. I am using RAD Studio/Delphi 12.1 Patch 1 with ImageEn 13.5.0. As long as there are no other layer objects added to the background image the selection rectangle displays and works fine. However, as soon as you add a layer object such as a TIEPolylineLayer object then the selection rectangle won't display again (when you left click in the image). This isn't quite correct because you might be able to get the selection rectangle to display if you left click on a layer near the top left of the image. I'm not fully sure where you have to click the second time to make the selection rectangle appear as it doesn't seem to always work.

To replicate this issue, using the Demos\Other\MouseInteract\MouseInteract.dproj project, perform these steps:
- Open an image file
- Click miSelect in the upper left list.
- Click the left mouse button and drag creating a selection rectangle anywhere in the image.
- You can repeat creating selection rectangle repeatedly anywhere with no issue.
- Click mlCreateShapeLayers in the left bottom list (msSelect is auto unchecked).
- Left click and drag in the image to create a circular shape.
- Click miSelect in the upper left list.
- Click the left mouse button and drag. (Just to be safe, perform this click left and above the shape that was created.) A selection rectangle should be created, but it isn't on my system.
- Interestingly, left click in the shape object that was created earlier and drag to the right. This works.
- The only way I can get a selection rectangle to show now is to start or end the drag operation such that the created rectangle touches the shape somewhere.

I don't think this behavior is the expected way this miSelect functionality is meant to work. What do I have to do to be able to define selection rectangles anywhere in the image regardless of it touching an existing layer object?

Thank you.

Tim


Tim F

xequte

38586 Posts

Posted - Oct 05 2024 :  00:15:19  Show Profile  Reply
Hi Tim

miSelect creates a selection in the current layer. When you use mlCreateShapeLayers to create a layer, the new layer becomes the "current" layer and is now the only layer accepting selection.

In order to be able to highlight a layer (make it the current one) when you click it, and then perform selection, you need to include one of the layer editing interactions, such as mlMoveLayers or mlResizeLayers (or add your own code in the mouse down event to set ImageEnView1.LayersCurrent).

https://www.imageen.com/help/TImageEnView.LayersCurrent.html

One other matter to remember is that, by default, the background layer (Layer 0) is not selectable, so you would need to enable ImageEView1.Layers[0].Selectable to be able to click and select it:

http://www.imageen.com/help/TIELayer.Selectable.html

Nigel
Xequte Software
www.imageen.com
Go to Top of Page

timfa

USA
8 Posts

Posted - Oct 08 2024 :  19:30:56  Show Profile  Reply
Hello again Nigel,
I spent a little time playing around with your suggestions and finally gave up on using your layers and setMouseInteractGeneral := []. Now, I capture the starting mouse location in the mousedown event (when in the appropriate mode) and then in the mousemove event I save the move point. Then, I invalidate the ImageEnView object so the TImageEnView.OnDrawCanvas event fires and in that routine I draw the rectangle. This works fine. The problem is in the mousemove code which works fine also until I try to add scrolling as I drag. And this is because I can’t figure out how to correctly use the ViewX, ExtendX, OffsetX, Ruler parameters, etc. This just shouldn’t be this difficult, but I’m just not following your mapping between screen/windows coordinates and bitmaps coordinates, and related relationships. When I draw these rectangles, I can be zoomed such that the image is well inside the TImageEnView object (I call it ImgEdit in my code) and/or zoomed way in so the image has to scrolled.

Any help you can provide is appreciated.

Tim

Some of the code discussed above follows:

The code to draw the rectangle:

// The following instance variables are used when drawing the Region of Interest (ROI).
    FDrawingROI : Boolean;
    FROIStart : TPoint;
    FROIEnd : TPoint;

procedure TImageEnWindowForm.ImgEditDrawCanvas(Sender: TObject;
  ACanvas: TCanvas; ARect: TRect);
var
   P : TPoint;
  roiRect : TRect;
begin
   // Unrelated code snipped

   if FDrawingROI then
   begin
      // This is the section that draws a rectangle…
      roiRect.TopLeft.X := ImgEdit.XBmp2Scr(FROIStart.X);
      roiRect.TopLeft.Y := ImgEdit.YBmp2Scr(FROIStart.Y);
      roiRect.BottomRight.X := ImgEdit.XBmp2Scr(FROIEnd.X);
      roiRect.BottomRight.Y := ImgEdit.YBmp2Scr(FROIEnd.Y);
      roiRect.NormalizeRect;

      ACanvas.Brush.Style := bsClear;
      ACanvas.Pen.Width := 1;
      ACanvas.Pen.Style := psSolid;
      ACanvas.Pen.Mode := pmNot;
      ACanvas.Rectangle(roiRect.TopLeft.X, roiRect.TopLeft.Y, roiRect.BottomRight.X, roiRect.BottomRight.Y);
   end;
end;

The disastrous MouseMove event…

procedure TImageEnWindowForm.ImgEditMouseMove (Sender: TObject;
                                             Shift: TShiftState;
                                             X, Y: Integer);
var
   ImageRow    : Single;
   ImageColumn : Single;
   ImageRowInt    : Integer;
   ImageColumnInt : Integer;
   IsVisible : Boolean;
   scrnBmpX00, scrnBmpY00 : Integer;
   scrnBmpX0, scrnBmpY0 : Integer;
   scrnBmpX2, scrnBmpY2 : Integer;
   scrnBmpLeft, scrnBmpTop : Integer;
   scrnBmpRight, scrnBmpBottom : Integer;
   BmpLeftEdge, BmpTopEdge : Integer;
   BmpRightEdge, BmpBottomEdge : Integer;
   viewChanged : Boolean;
begin
//   ImgEdit.Invalidate;  // Do this so ImgEditDrawCanvas executes.

   // Convert X and Y to bitmap row and column:
   ImageColumnInt := ImgEdit.XScr2Bmp(X, False);
   ImageRowInt := ImgEdit.YScr2Bmp(Y, False);
   // Convert X and Y to bitmap subpixel-level accuracy row and column:
   IsVisible := ConvertClientToImage (X, Y, ImageRow, ImageColumn);

   viewChanged := false;

   if FDrawingROI then
   begin
      if ImageRowInt < 0 then
         FROIEnd.Y := 0
      else if ImageRowInt >= ImgEdit.IEBitmap.Height then
         FROIEnd.Y := ImgEdit.IEBitmap.Height - 1
      else
         FROIEnd.Y := ImageRowInt;

      if ImageColumnInt < 0 then
         FROIEnd.X := 0
      else if ImageColumnInt >= ImgEdit.IEBitmap.Width then
         FROIEnd.X := ImgEdit.IEBitmap.Width - 1
      else
         FROIEnd.X := Trunc(ImageColumnInt);


      // Scroll to the mouse if needed...
      if ImgEdit.OffsetX > 0 then
      begin

// FOLLOWING IS WHERE THE PROBLEMS BEGIN:
// I have tried so many things that the state of the following is a disaster.


//         scrnBmpLeft := ImgEdit.XBmp2Scr(0);
//         scrnBmpRight := ImgEdit.XBmp2Scr(ImgEdit.IEBitmap.Width-1);
//
//         BmpLeftEdge := ImgEdit.XScr2Bmp(ImgEdit.ViewX);
//         BmpRightEdge := ImgEdit.XScr2Bmp(ImgEdit.ViewX + ImgEdit.ExtentX - 1);
//
//         if (BmpLeftEdge <= ImageColumnInt) AND (ImageColumnInt <= BmpRightEdge) then
//         begin
//            ; // do nothing
//         end
//         else if BmpLeftEdge > ImageColumnInt then
//         begin
//            ImgEdit.ViewX := iMax(X,scrnBmpLeft);
//            viewChanged := true;
//         end
//         else if ImageColumnInt > BmpRightEdge then
//         begin
//            ImgEdit.ViewX := iMin(X,scrnBmpRight) - ImgEdit.ExtentX;
//            viewChanged := true;
//         end;



         scrnBmpX00 := ImgEdit.XBmp2Scr(0);
         scrnBmpX0 := ImgEdit.OffsetX + ImgEdit.RulerParams.RulerAreaLeft;
         scrnBmpLeft := scrnBmpX0 + ImgEdit.ViewX;
         scrnBmpRight := scrnBmpLeft + ImgEdit.ExtentX - 1;
         scrnBmpX2 := ImgEdit.XBmp2Scr(ImgEdit.IEBitmap.Width-1);

//         if (scrnBmpLeft <= X) AND (X <= scrnBmpRight) then
         if (ImgEdit.ViewX <= X) AND (X <= ImgEdit.ViewX + ImgEdit.ExtentX - 1) then
         begin
            ; // do nothing
         end
//         else if scrnBmpLeft > X then
         else if ImgEdit.ViewX > X then
         begin
            if ImgEdit.ViewX > 0 then
            begin
               ImgEdit.ViewX := iMax(X,scrnBmpX00); // iMax(X,scrnBmpX0);
               viewChanged := true;
            end;
         end
//         else if X > scrnBmpRight then
         else if X >= ImgEdit.ViewX + ImgEdit.ExtentX then
         begin
            ImgEdit.ViewX := iMin(X,scrnBmpX2) - ImgEdit.ExtentX;
            viewChanged := true;
         end;

//         if (ImgEdit.ViewX <= X) AND (X < ImgEdit.ViewX + ImgEdit.ExtentX) then
//         begin
//            ; // do nothing
//         end
//         else if ImgEdit.ViewX > X then
//            ImgEdit.ViewX := iMax(X,scrnBmpLeft)
//         else if ImgEdit.ViewX + ImgEdit.ExtentX < X then
//            ImgEdit.ViewX := iMin(X,scrnBmpRight) - ImgEdit.ExtentX;
      end;



      if ImgEdit.OffsetY > 0 then
      begin
//         scrnBmpTop    := ImgEdit.YBmp2Scr(0);
//         scrnBmpBottom := ImgEdit.YBmp2Scr(ImgEdit.IEBitmap.Height-1);
//
//         BmpTopEdge := ImgEdit.YScr2Bmp(ImgEdit.ViewY);
//         BmpBottomEdge := ImgEdit.YScr2Bmp(ImgEdit.ViewY + ImgEdit.ExtentY - 1);
//         if (BmpTopEdge <= ImageRowInt) AND (ImageRowInt <= BmpBottomEdge) then
//         begin
//            ; // do nothing
//         end
//         else if BmpTopEdge > ImageRowInt then
//         begin
//            ImgEdit.ViewY := iMax(Y,scrnBmpTop);
//            viewChanged := true;
//         end
//         else if ImageRowInt > BmpBottomEdge then
//         begin
//            ImgEdit.ViewY := iMin(Y,scrnBmpBottom) - ImgEdit.ExtentY;
//            viewChanged := true;
//         end;


         scrnBmpY00 := ImgEdit.YBmp2Scr(0);
         scrnBmpY0 := ImgEdit.OffsetY + ImgEdit.RulerParams.RulerAreaTop;
         scrnBmpTop := scrnBmpY0 + ImgEdit.ViewY;
         scrnBmpBottom := scrnBmpTop + ImgEdit.ExtentY - 1;
         scrnBmpY2 := ImgEdit.YBmp2Scr(ImgEdit.IEBitmap.Height-1);

         if (scrnBmpTop <= Y) AND (Y <= scrnBmpBottom) then
         begin
            ; // do nothing
//         end
//         else if scrnBmpTop > Y then
//         begin
//            ImgEdit.ViewY := iMax(Y,scrnBmpY0);
//            viewChanged := true;
//         end
//         else if X > scrnBmpBottom then
//         begin
//            ImgEdit.ViewX := iMin(Y,scrnBmpY2) - ImgEdit.ExtentY;
//            viewChanged := true;
         end;




//         if (ImgEdit.ViewY <= Y) AND (Y <= ImgEdit.ViewY + ImgEdit.ExtentY) then
//         begin
//            ; // do nothing
//         end
//         else if ImgEdit.ViewY > Y then
//            ImgEdit.ViewY := iMax(Y,scrnBmpTop)
//         else if ImgEdit.ViewY + ImgEdit.ExtentY < Y then
//            ImgEdit.ViewY := iMin(Y,scrnBmpBottom) - ImgEdit.ExtentY;
      end;

   end;

   if viewChanged then
      ImgEdit.update
   else
      ImgEdit.Invalidate;  // Do this so ImgEditDrawCanvas executes.
// And a bunch of unrelated code that I cut.
end;



Tim F
Go to Top of Page

xequte

38586 Posts

Posted - Oct 08 2024 :  21:32:31  Show Profile  Reply
Hi Tim

Sorry, I may not be understanding the purpose of your code well enough, but why wouldn't the MouseMove code just be:

FROIEnd.X := ImageEnView1.XScr2Bmp(X, False);
FROIEnd.Y := ImageEnView1.YScr2Bmp(Y, False);


Here's my test project:

attach/xequte/2024108213219_ROI.zip
68.39 KB



procedure TForm1.ImageEnView1DrawCanvas(Sender: TObject; ACanvas: TCanvas;
    ARect: TRect);
var
  roiRect : TRect;
begin
  if FDrawingROI then
  begin
    roiRect.TopLeft.X := ImageEnView1.XBmp2Scr(FROIStart.X);
    roiRect.TopLeft.Y := ImageEnView1.YBmp2Scr(FROIStart.Y);
    roiRect.BottomRight.X := ImageEnView1.XBmp2Scr(FROIEnd.X);
    roiRect.BottomRight.Y := ImageEnView1.YBmp2Scr(FROIEnd.Y);
    roiRect.NormalizeRect;

    ACanvas.Brush.Style := bsClear;
    ACanvas.Pen.Width   := 1;
    ACanvas.Pen.Style   := psSolid;
    ACanvas.Pen.Mode    := pmNot;
    ACanvas.Rectangle(roiRect.TopLeft.X, roiRect.TopLeft.Y, roiRect.BottomRight.X, roiRect.BottomRight.Y);
  end;
end;

procedure TForm1.ImageEnView1MouseDown(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer);
begin
  FDrawingROI := True;
  FROIStart.X := ImageEnView1.XScr2Bmp(X, False);
  FROIStart.Y := ImageEnView1.YScr2Bmp(Y, False);
end;

procedure TForm1.ImageEnView1MouseMove(Sender: TObject; Shift: TShiftState; X,
    Y: Integer);
begin
  if FDrawingROI then
  begin
    FROIEnd.X := ImageEnView1.XScr2Bmp(X, False);
    FROIEnd.Y := ImageEnView1.YScr2Bmp(Y, False);
    ImageEnView1.Invalidate();
  end;
end;

procedure TForm1.ImageEnView1MouseUp(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer);
begin
  FDrawingROI := False;
  ImageEnView1.Invalidate();
end;


Nigel
Xequte Software
www.imageen.com
Go to Top of Page

timfa

USA
8 Posts

Posted - Oct 09 2024 :  09:15:09  Show Profile  Reply
Nigel,

Thanks for the quick response. Basically, your code does what mine does except for ensuring the end point is constrained to be in the range of 0 to ImageEnView1.IEBitmap.Width-1 or ImageEnView1.IEBitmap.height-1. This part of my code works. However, I was hoping for guidance on how to make the ImageEnView pan/scroll when the mouse is dragged outside the image area. My thought was that in the mousemove event I need to change the ViewX and ViewY values to shift the image such that it scrolls to the cursor location or the image boundary, whichever come first. That's what the following code was attempting to do for the X value (a similar block is present for the Y value)...
if (ImgEdit.ViewX <= X) AND (X <= ImgEdit.ViewX + ImgEdit.ExtentX - 1) then
begin
; // Do nothing as the mouse is in the visible range of the image.
end
else if ImgEdit.ViewX > X then
begin
// Pan/Scroll the image to the right...
ImgEdit.ViewX := iMax(X,scrnBmpX00); // iMax(X,scrnBmpX0);
viewChanged := true;
end
else // if X >= ImgEdit.ViewX + ImgEdit.ExtentX then
begin
// Pan/Scroll the image to the left...
ImgEdit.ViewX := iMin(X,scrnBmpX2) - ImgEdit.ExtentX;
viewChanged := true;
end;

Unfortunately, this code doesn't work. Can you suggest how to fix it so panning/scrolling to the image boundaries works?

By the way, I normally, but they are optional, have horizontal and vertical rulers shown which may complicate some of this.

Thanks for any additional help you can provide.


Tim F
Go to Top of Page

timfa

USA
8 Posts

Posted - Oct 09 2024 :  11:48:54  Show Profile  Reply
Nigel,

I enhanced your example to demonstrate the issue better. My code is attached...

attach/timfa/2024109114024_2024108213219_ROI _Enhanced__20241009T1129.zip
138.17 KB

Build the updated project and with the left mouse button down, drag out a rectangle and release the mouse. A blue rectangle will be displayed. Repeat this in the image area. Now scroll the image so the top left isn't at the image origin. Try creating more rectangle regions. You should see some weird behavior where the rectangles are shifted horizontally and/or vertically from where you drew the rectangle. Repeat this with the image scrolled in various locations and perhaps altering the zoom setting. I think you will see unexpected behavior. I added a checkbox to include / exclude rulers and a button to clear the blue rectangles.

I appreciation you assistance.


Tim F
Go to Top of Page

xequte

38586 Posts

Posted - Oct 09 2024 :  20:50:01  Show Profile  Reply
Hi Tim

You need to consider whether you need x,y values in screen or bitmap values.

For MouseDown and drawing to the control canvas, you need screen value.

For drawing to the bitmap, or adding layers you need bitmap values (for layers the position and size is relative to the background layer, layer 0).

Your demo works if I change the LoadNormalizedRect method to:

procedure TForm1.LoadNormalizedRect(ForScreen: Boolean);
begin
  if ForScreen then
  begin
    FNormalizedRect.TopLeft.X := ImageEnView1.XBmp2Scr(FROIStart.X);
    FNormalizedRect.TopLeft.Y := ImageEnView1.YBmp2Scr(FROIStart.Y);
    FNormalizedRect.BottomRight.X := ImageEnView1.XBmp2Scr(FROIEnd.X);
    FNormalizedRect.BottomRight.Y := ImageEnView1.YBmp2Scr(FROIEnd.Y);
  end
  else
  begin
    FNormalizedRect.TopLeft := FROIStart;
    FNormalizedRect.BottomRight := FROIEnd;
  end;

  FNormalizedRect.NormalizeRect;
end;


In ImageEnView1DrawCanvas use LoadNormalizedRect( True );
In CreateRegionRect use LoadNormalizedRect( False );

Updated demo:
attach/xequte/2024109205048_roi2.zip
136.21 KB

Nigel
Xequte Software
www.imageen.com

Go to Top of Page

timfa

USA
8 Posts

Posted - Oct 10 2024 :  16:38:55  Show Profile  Reply
Nigel,

In my real code I did correctly handle the screen versus bitmap coordinates. I just missed that here. Thanks for catching that.

What I was really looking for was how to get the image to pan as I dragged out the rectangle area. It took me quite a while today to figure this out, but in the attached code I have figured it out. I added several local variables in my mousemove function to make it easier to analyze in the debugger what values were useful. In the online ImageEn documentation, or at least the documentation I've seen, it just isn't clear to me what coordinate system is being used for various properties/arguments. I'm fairly certain that you would have been able to do what I did much faster than it took me and with far less code. However, as I have said, the attached code works as desired now. Check it out and let me know if you have any feedback.

attach/timfa/20241010163715_2024109205048_roi2_Enhanced__20241010TT1616.zip
138.45 KB

I appreciate your help.


Tim F
Go to Top of Page

timfa

USA
8 Posts

Posted - Oct 10 2024 :  18:01:49  Show Profile  Reply
I integrated my changes from the sample project I uploaded earlier and when testing at a zoom of >~160% I back to bad things happening with the panning while dragging. I clearly am missing something.

Tim F
Go to Top of Page

xequte

38586 Posts

Posted - Nov 04 2024 :  20:05:50  Show Profile  Reply
Sorry Tim,

I'm not really following your code. Looking for issues, i see there are a number of situations where you multiply by the zoom only, e.g. ImageEnView1.ZoomX / 100.0. You need to check those references should not be ImageEnView1.XBmp2Scr(), for example.

I think you should email me for the latest beta. I have added a TImageEnView.AutoScrollTowardCursor() method, that you should be able to use instead of setting ViewX/ViewY.

You can use it like this:


procedure TForm1.ImageEnView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  fMyCustomSelectionActive := True;
  fMyCustomSelectionStart.X := ImageEnView1.XScr2Bmp(X, False);
  fMyCustomSelectionStart.Y := ImageEnView1.YScr2Bmp(Y, False);
end;

procedure TForm1.ImageEnView1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if fMyCustomSelectionActive then
  begin
    ImageEnView1.AutoScrollTowardCursor();
    fMyCustomSelectionCurr.X := ImageEnView1.XScr2Bmp(X, False);
    fMyCustomSelectionCurr.Y := ImageEnView1.YScr2Bmp(Y, False);
    ImageEnView1.Invalidate();
  end;
end;

procedure TForm1.ImageEnView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  fMyCustomSelectionActive := False;
end;

procedure TForm1.ImageEnView1DrawCanvas(Sender: TObject; ACanvas: TCanvas; ARect: TRect);
var
  scrRect: TRect;
begin
  if fMyCustomSelectionActive then
  begin
    scrRect.Left   := ImageEnView1.XBmp2Scr( fMyCustomSelectionStart.X );
    scrRect.Top    := ImageEnView1.YBmp2Scr( fMyCustomSelectionStart.Y );
    scrRect.Right  := ImageEnView1.XBmp2Scr( fMyCustomSelectionCurr.X );
    scrRect.Bottom := ImageEnView1.YBmp2Scr( fMyCustomSelectionCurr.Y );

    ACanvas.Brush.Style := bsClear;
    ACanvas.Pen.Width   := 3;
    ACanvas.Pen.Style   := psSolid;
    ACanvas.Pen.Mode    := pmNot;
    ACanvas.Rectangle( scrRect.Left, scrRect.Top, scrRect.Right, scrRect.Bottom);
  end;
end;



Nigel
Xequte Software
www.imageen.com
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
Jump To: