Page 2 of 2

Re: Zoom an vector drawn image

Posted: Tue Apr 04, 2017 2:19 am
by Amundo
Nice examples, guys.

Does anyone have an example that zooms AND rotates AND moves along the X/Y axis?

Re: Zoom an vector drawn image

Posted: Tue Apr 04, 2017 12:49 pm
by Little John
Amundo wrote:Does anyone have an example that zooms AND rotates AND moves along the X/Y axis?
Here you go! :-)

Besides rotating and moving, zooming now can also be done with the keyboard.

Code: Select all

; LJ 2017-04-04, <http://www.purebasic.fr/english/viewtopic.php?p=505458#p505458>
; successfully tested with PB 5.60 (x64) on Windows 10

CompilerIf #PB_Compiler_Version < 550
   MessageRequester("Error", "PureBasic version 5.50 or newer required because the new bounding box functions are used.")

CompilerElse
   EnableExplicit

   Structure PointD
      x.d
      y.d
   EndStructure

   Structure Polygon
      Array p.PointD(0)
      center.PointD
      color.i
      width.i
   EndStructure

   ;-----------------------------------------------------------------------

   Macro Rect2Polar_D (_x_, _y_)
      ; -- convert rectangular coordinates to polar coordinates
      ; in : _x_, _y_: rectangular coordinates
      ; out: corresponding distance

      Sqr((_x_)*(_x_) + (_y_)*(_y_))
   EndMacro

   Macro Rect2Polar_A (_x_, _y_)
      ; -- convert rectangular coordinates to polar coordinates
      ; in : _x_, _y_: rectangular coordinates
      ; out: corresponding angle, expressed in rad;
      ;      ]-Pi; +Pi]

      ATan2(_x_, _y_)
   EndMacro

   ;-----------------------------------------------------------------------

   Macro Polar2Rect_X (_distance_, _angle_)
      ; -- convert polar coordinates to rectangular coordinates
      ; in : _distance_: must be >= 0
      ;      _angle_   : expressed in rad
      ; out: corresponding x coordinate

      ((_distance_) * Cos(_angle_))
   EndMacro

   Macro Polar2Rect_Y (_distance_, _angle_)
      ; -- convert polar coordinates to rectangular coordinates
      ; in : _distance_: must be >= 0
      ;      _angle_   : expressed in rad
      ; out: corresponding y coordinate

      ((_distance_) * Sin(_angle_))
   EndMacro

   ;-----------------------------------------------------------------------

   Procedure DrawObject (canvas.i, *obj.Polygon)
      Protected.i i, last = ArraySize(*obj\p())

      If StartVectorDrawing(CanvasVectorOutput(canvas))
         ; clear canvas
         VectorSourceColor(RGBA(255, 255, 255, 255))
         FillVectorOutput()

         ; draw object and get the center of its bounding box
         With *obj
            MovePathCursor(\p(0)\x, \p(0)\y)
            For i = 1 To last
               AddPathLine(\p(i)\x, \p(i)\y)
            Next
            ClosePath()

            \center\x = PathBoundsX() + PathBoundsWidth()/2.0
            \center\y = PathBoundsY() + PathBoundsHeight()/2.0

            VectorSourceColor(\color)
            StrokePath(\width)
         EndWith

         StopVectorDrawing()
      EndIf
   EndProcedure


   Procedure ZoomObject (canvas.i, *obj.Polygon, factor.d)
      Protected.i i, last = ArraySize(*obj\p())

      With *obj
         For i = 0 To last
            \p(i)\x + factor * (\p(i)\x - \center\x)
            \p(i)\y + factor * (\p(i)\y - \center\y)
         Next
      EndWith

      DrawObject(canvas, *obj)
   EndProcedure


   Procedure RotateObject (canvas.i, *obj.Polygon, rotation.d)
      Protected.i i, last = ArraySize(*obj\p())
      Protected.d distance, angle
      
      rotation = Radian(rotation)
      With *obj
         For i = 0 To last
            distance = Rect2Polar_D(\p(i)\x - \center\x, \p(i)\y - \center\y)
            angle    = Rect2Polar_A(\p(i)\x - \center\x, \p(i)\y - \center\y)
            \p(i)\x = \center\x + Polar2Rect_X(distance, angle + rotation)
            \p(i)\y = \center\y + Polar2Rect_Y(distance, angle + rotation)
         Next
      EndWith

      DrawObject(canvas, *obj)
   EndProcedure


   Procedure MoveObject_Horizontal (canvas.i, *obj.Polygon, dx.d)
      Protected.i i, last = ArraySize(*obj\p())

      With *obj
         For i = 0 To last
            \p(i)\x + dx
         Next
      EndWith

      DrawObject(canvas, *obj)
   EndProcedure


   Procedure MoveObject_Vertical (canvas.i, *obj.Polygon, dy.d)
      Protected.i i, last = ArraySize(*obj\p())

      With *obj
         For i = 0 To last
            \p(i)\y + dy
         Next
      EndWith

      DrawObject(canvas, *obj)
   EndProcedure

   ;-======================================================================

   ; Windows
   Enumeration
      #WinMain
   EndEnumeration

   ; Gadgets
   Enumeration
      #Canvas
   EndEnumeration

   Define obj.Polygon, dw.i, event.i


   ;-- Create a window with a canvas gadget
   If OpenWindow(#WinMain, 0, 0, 400, 400, "Canvas demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) = 0
      MessageRequester("Fatal error", "Couldn't open main window.")
      End
   EndIf

   CanvasGadget(#Canvas, 0, 0, 400, 400, #PB_Canvas_Keyboard)
   SetActiveGadget(#Canvas)    ; set the keyboard focus on the canvas gadget

   ;-- Define a polygon
   With obj
      Dim \p(4)
      \p(0)\x = 100.0 : \p(0)\y = 250.0
      \p(1)\x = 250.0 : \p(1)\y = 250.0
      \p(2)\x = 250.0 : \p(2)\y = 170.0
      \p(3)\x = 175.0 : \p(3)\y = 140.0
      \p(4)\x = 100.0 : \p(4)\y = 170.0
      \color = RGBA(255, 0, 0, 255)
      \width = 2
   EndWith

   ;-- Show the polygon
   DrawObject(#Canvas, @obj)


   ;-- Process events
   Repeat
      event = WaitWindowEvent()

      If event = #PB_Event_Gadget
         Select EventGadget()
            Case #Canvas
               Select EventType()
                  Case #PB_EventType_MouseWheel
                     ;-- Zoom
                     dw = GetGadgetAttribute(#Canvas, #PB_Canvas_WheelDelta)
                     ZoomObject(#Canvas, @obj, 0.1*dw)

                  Case #PB_EventType_KeyDown
                     Select GetGadgetAttribute(#Canvas, #PB_Canvas_Key)
                           ;-- Zoom
                        Case #PB_Shortcut_Add
                           ZoomObject(#Canvas, @obj,  0.1)
                        Case #PB_Shortcut_Subtract
                           ZoomObject(#Canvas, @obj, -0.1)

                           ;-- Rotate (angle expressed in degrees)
                        Case #PB_Shortcut_PageDown
                           RotateObject(#Canvas, @obj,  6.0)
                        Case #PB_Shortcut_PageUp
                           RotateObject(#Canvas, @obj, -6.0)

                           ;-- Move
                        Case #PB_Shortcut_Right
                           MoveObject_Horizontal(#Canvas, @obj,  3)
                        Case #PB_Shortcut_Left
                           MoveObject_Horizontal(#Canvas, @obj, -3)
                        Case #PB_Shortcut_Down
                           MoveObject_Vertical(#Canvas, @obj,  3)
                        Case #PB_Shortcut_Up
                           MoveObject_Vertical(#Canvas, @obj, -3)
                     EndSelect
               EndSelect
         EndSelect
      EndIf
   Until event = #PB_Event_CloseWindow
CompilerEndIf

Re: Zoom an vector drawn image

Posted: Tue Apr 04, 2017 2:14 pm
by Little John
Here is another version, using the built-in vector commands TranslateCoordinates() and RotateCoordinates() for moving and rotating, respectively. For zooming, I didn't manage to use ScaleCoordinates() properly, since I want to use the center of the object as anchor point, not the upper left corner.
Now it doesn't make much sense anymore to write some parts of the code as separate procedures. However, I didn't change that, so that it's easier to compare this code with the previous one.

Code: Select all

; LJ 2017-04-04, <http://www.purebasic.fr/english/viewtopic.php?p=505463#p505463>
; successfully tested with PB 5.60 (x64) on Windows 10

CompilerIf #PB_Compiler_Version < 550
   MessageRequester("Error", "PureBasic version 5.50 or newer required because the new bounding box functions are used.")

CompilerElse
   EnableExplicit

   Structure PointD
      x.d
      y.d
   EndStructure

   Structure Polygon
      Array p.PointD(0)
      center.PointD
      color.i
      width.i
      angle.d
      offset.PointD
   EndStructure

   
   Procedure DrawObject (canvas.i, *obj.Polygon)
      Protected.i i, last = ArraySize(*obj\p())

      If StartVectorDrawing(CanvasVectorOutput(canvas))
         ; clear canvas
         VectorSourceColor(RGBA(255, 255, 255, 255))
         FillVectorOutput()
         
         ; draw object and get the center of its bounding box
         With *obj
            TranslateCoordinates(\offset\x, \offset\y)
            RotateCoordinates(\center\x, \center\y, \angle)
            
            MovePathCursor(\p(0)\x, \p(0)\y)
            For i = 1 To last
               AddPathLine(\p(i)\x, \p(i)\y)
            Next
            ClosePath()

            \center\x = PathBoundsX() + PathBoundsWidth()/2.0
            \center\y = PathBoundsY() + PathBoundsHeight()/2.0
            
            VectorSourceColor(\color)
            StrokePath(\width)
         EndWith

         StopVectorDrawing()
      EndIf
   EndProcedure


   Procedure ZoomObject (canvas.i, *obj.Polygon, factor.d)
      Protected.i i, last = ArraySize(*obj\p())

      With *obj
         For i = 0 To last
            \p(i)\x + factor * (\p(i)\x - \center\x)
            \p(i)\y + factor * (\p(i)\y - \center\y)
         Next
      EndWith

      DrawObject(canvas, *obj)
   EndProcedure


   Procedure RotateObject (canvas.i, *obj.Polygon, rotation.d)
      *obj\angle + rotation
      DrawObject(canvas, *obj)
   EndProcedure


   Procedure MoveObject_Horizontal (canvas.i, *obj.Polygon, dx.d)
      *obj\offset\x + dx
      DrawObject(canvas, *obj)
   EndProcedure


   Procedure MoveObject_Vertical (canvas.i, *obj.Polygon, dy.d)
      *obj\offset\y + dy
      DrawObject(canvas, *obj)
   EndProcedure

   ;-======================================================================

   ; Windows
   Enumeration
      #WinMain
   EndEnumeration

   ; Gadgets
   Enumeration
      #Canvas
   EndEnumeration

   Define obj.Polygon, dw.i, event.i


   ;-- Create a window with a canvas gadget
   If OpenWindow(#WinMain, 0, 0, 400, 400, "Canvas demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) = 0
      MessageRequester("Fatal error", "Couldn't open main window.")
      End
   EndIf

   CanvasGadget(#Canvas, 0, 0, 400, 400, #PB_Canvas_Keyboard)
   SetActiveGadget(#Canvas)    ; set the keyboard focus on the canvas gadget

   ;-- Define a polygon
   With obj
      Dim \p(4)
      \p(0)\x = 100.0 : \p(0)\y = 250.0
      \p(1)\x = 250.0 : \p(1)\y = 250.0
      \p(2)\x = 250.0 : \p(2)\y = 170.0
      \p(3)\x = 175.0 : \p(3)\y = 140.0
      \p(4)\x = 100.0 : \p(4)\y = 170.0
      \color = RGBA(255, 0, 0, 255)
      \width = 2
      \angle = 0.0
      \offset\x = 0.0
      \offset\y = 0.0
   EndWith

   ;-- Show the polygon
   DrawObject(#Canvas, @obj)


   ;-- Process events
   Repeat
      event = WaitWindowEvent()

      If event = #PB_Event_Gadget
         Select EventGadget()
            Case #Canvas
               Select EventType()
                  Case #PB_EventType_MouseWheel
                     ;-- Zoom
                     dw = GetGadgetAttribute(#Canvas, #PB_Canvas_WheelDelta)
                     ZoomObject(#Canvas, @obj, 0.1*dw)

                  Case #PB_EventType_KeyDown
                     Select GetGadgetAttribute(#Canvas, #PB_Canvas_Key)
                           ;-- Zoom
                        Case #PB_Shortcut_Add
                           ZoomObject(#Canvas, @obj,  0.1)
                        Case #PB_Shortcut_Subtract
                           ZoomObject(#Canvas, @obj, -0.1)
                           
                           ;-- Rotate (angle expressed in degrees)
                        Case #PB_Shortcut_PageDown
                           RotateObject(#Canvas, @obj,  6.0)
                        Case #PB_Shortcut_PageUp
                           RotateObject(#Canvas, @obj, -6.0)

                           ;-- Move
                        Case #PB_Shortcut_Right
                           MoveObject_Horizontal(#Canvas, @obj,  3)
                        Case #PB_Shortcut_Left
                           MoveObject_Horizontal(#Canvas, @obj, -3)
                        Case #PB_Shortcut_Down
                           MoveObject_Vertical(#Canvas, @obj,  3)
                        Case #PB_Shortcut_Up
                           MoveObject_Vertical(#Canvas, @obj, -3)
                     EndSelect
               EndSelect
         EndSelect
      EndIf
   Until event = #PB_Event_CloseWindow
CompilerEndIf

Re: Zoom an vector drawn image

Posted: Tue Apr 04, 2017 2:43 pm
by skywalk
Thanks for the comparison code Little John. 8)
Small problem. Rotating the object completely does not arrive at its original angle? Small skew.

Re: Zoom an vector drawn image

Posted: Tue Apr 04, 2017 3:12 pm
by Little John
Hello skywalk, many thanks for your bug report.
Obviously, choosing 0.1 radians (about 5.73 degrees) as step for rotating wasn't a good idea. :D :oops:
In both codes above, I've changed that now to 6.0 degrees, which should work fine (at least in theory).
Thanks again!

Re: Zoom an vector drawn image

Posted: Tue Apr 04, 2017 3:26 pm
by skywalk
Ah, makes sense now. I was thinking degrees(°).

Re: Zoom an vector drawn image

Posted: Wed Apr 05, 2017 1:58 am
by Amundo
LittleJohn, I can't thank you enough, I will use your code to move horizontally/vertically, which was the last piece I needed.

(I think I have the code to translate the coordinates necessary, but would have had to translate it for this situation).

Thanks a million for all your help!

Edit: Actually, I've completely lost the plot!! The only thing I need to fix is the zoom!