It is currently Sun Jul 21, 2019 1:02 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Drawing an area on a Screen()
PostPosted: Thu May 30, 2019 1:59 pm 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 340
Location: France
Hi,

What is the best way to draw an irregular area over a map in 2D drawing? It's said in the documentation that displaying a sprite that is bigger than the screen is not recommended. Then how do you zoom in this area? because my idea is to have very big areas with details only appearing on a close zoom level. Should I directly draw on Screen, like I'd do on a Canvas? (it doesn't seem recommended either)

By irregular area, I mean picking random points and joining them using several Bézier curves.


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Thu May 30, 2019 2:41 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Sun Sep 11, 2016 2:17 pm
Posts: 441
U could cut one big image/sprite into small pieces and display only those that are inside the render area.
Or store offsets of the points u want to connect (multiplied by the zoom level) -
connect all lines inside the render area to the fist point that lays outside.


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Thu May 30, 2019 4:13 pm 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 340
Location: France
Mijikai wrote:
Or store offsets of the points u want to connect (multiplied by the zoom level) -
connect all lines inside the render area to the fist point that lays outside.


That would mean recreating the sprites at each new zoom, right? I have hard times understanding how sprites are supposed to be used when they display graphic elements bigger than the screen.


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Thu May 30, 2019 5:17 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Sun Sep 11, 2016 2:17 pm
Posts: 441
Joubarbe wrote:
That would mean recreating the sprites at each new zoom, right? I have hard times understanding how sprites are supposed to be used when they display graphic elements bigger than the screen.


Yes (at least with PureBasic) either beforehand or realtime -
TransformSprites() can be used for that.


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Fri May 31, 2019 12:14 am 
Offline
Addict
Addict
User avatar

Joined: Mon Jul 25, 2005 3:51 pm
Posts: 3560
Location: Utah, USA
I'd like to help but I am having some difficulty picturing what you would like to do. Can you supply a workup of what the effect might look like, either a hand drawn image or a mockup that illustrates your goal?

_________________
Image


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Fri May 31, 2019 7:14 am 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 340
Location: France
Image

Imagine a big map full of these overlapping areas, and extreme zoom in. They could probably be "filled" with oblique strokes.


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Fri May 31, 2019 1:30 pm 
Offline
Addict
Addict

Joined: Thu Aug 30, 2007 11:54 pm
Posts: 1008
Location: right here
Maybe use an additional tiling layer for the regions, similar to a tilemap. So the shapes are split up into tiles and you only draw the parts that are in screen.
I made a little test/demo. Moving the mouse to scroll, mouse buttons to zoom. Keys 1, 2, 3 for mode.
I used simple circles as regions.
Mode 1 draws all regions drectly to the screen in a single drawing block.
Mode 2 creates a sprite per region
Mode 3 tiles the world up into equal sized areas (sprites) each containing only parts of the circles

If your world is very large that will create a lot of sprites though, even if each of them is smaller than the screen and only the visible one are actually displayed. Anyway, just zooming sprites may not be enough if you want very large scale/zoom ratios or even infinite zoom. You may need a completely different approach without buffers/sprites, only store abstract shapes (like beziers curves) and rasterize it to pixels as a last step by drawing directly to screen.

Code:
EnableExplicit

InitSprite()
InitMouse()
InitKeyboard()

RandomSeed(1)

Define ww=800
Define wh=600
Define style
style | #PB_Window_ScreenCentered
style | #PB_Window_SystemMenu
style | #PB_Window_MinimizeGadget
Define win, event, em, quit
Define i, k

If 01
  win=OpenWindow(#PB_Any, 50,100, ww,wh, "", style)
  AddKeyboardShortcut(win, #PB_Shortcut_Escape, 10)
  OpenWindowedScreen(WindowID(win), 0,0, ww, wh)
  ;SetFrameRate(9999)
Else
  OpenScreen(ww,wh,32,"", #PB_Screen_NoSynchronization)
EndIf

#tileSize = 32
#tileMapSize = 100

#regionSize = 128
#regionMapSize = #tileMapSize * #tileSize / #regionSize

Define cursor = CreateSprite(#PB_Any, 10, 10, #PB_Sprite_AlphaBlending)
StartDrawing(SpriteOutput(cursor))
  DrawingMode(#PB_2DDrawing_AllChannels)
  Box(0, 0, 5, 5, $ffffffff)
StopDrawing()

Define tile = CreateSprite(#PB_Any, #tileSize, #tileSize, #PB_Sprite_AlphaBlending)
StartDrawing(SpriteOutput(tile))
  DrawingMode(#PB_2DDrawing_AllChannels)
  Box(1, 1, #tileSize-2, #tileSize-2, $ff886600)
StopDrawing()

Dim tileMap(#tileMapSize, #tileMapSize)
For k=0 To #tileMapSize
  For i=0 To #tileMapSize
    tileMap(k, i) = Random(1) * tile
  Next
Next

Dim regionMap(#regionMapSize, #regionMapSize)
For k=0 To #regionMapSize
  For i=0 To #regionMapSize
    regionMap(k, i) = CreateSprite(#PB_Any, #regionSize, #regionSize, #PB_Sprite_AlphaBlending)
  Next
Next

Define sprFR = CreateSprite(#PB_Any, 400,20)
Define FR, FRt, FR$

Define.f offsetX
Define.f offsetY
Define.f zoom.f = 1.0
Define mode = 1

Structure sRegion
  x.f
  y.f
  r.f
 
  ; only used by some modes
  spr.i
  sprW.i
  sprH.i
EndStructure

NewList region.sRegion()
For k=0 To #tileMapSize
  For i=0 To #tileMapSize
    If k%8=0 And i%8=0
      AddElement(region())
      region()\x = i * #tileSize - 20 + Random(40)
      region()\y = k * #tileSize - 20 + Random(40)
      region()\r = Random(#tileSize * 6)
    EndIf
  Next
Next

Define.f x
Define.f y
Define initialized2
Define initialized3
Define RegionMinX
Define RegionMaxX
Define RegionMinY
Define RegionMaxY


Repeat
  ExamineMouse()
  ExamineKeyboard()
 
  If KeyboardPushed(#PB_Key_1) : mode = 1 : EndIf
  If KeyboardPushed(#PB_Key_2) : mode = 2 : EndIf
  If KeyboardPushed(#PB_Key_3) : mode = 3 : EndIf
 
  ; events
  If IsWindow(win) ;{
    Repeat
      event = WindowEvent()
      em = EventMenu()
      Select event
        Case #PB_Event_CloseWindow
          quit = #True
        Case #PB_Event_Menu
          Select em
          Case 10
            quit = #True
          EndSelect
      EndSelect
    Until Not event
    ;}
  EndIf
 
  ; scroll/zoom
  offsetX + MouseDeltaX()
  offsetY + MouseDeltaY()
  If MouseButton(1)
    zoom + 0.01
  ElseIf MouseButton(2)
    zoom - 0.01
  EndIf
 
  TransformSprite(tile,
                  0, 0,
                  zoom * #tileSize, 0,
                  zoom * #tileSize, zoom * #tileSize,
                  0, zoom * #tileSize)
 
  ; draw tilemap
  For k=0 To #tileMapSize
    For i=0 To #tileMapSize
      x = zoom * i * #tileSize - offsetX
      y = zoom * k * #tileSize - offsetY
      If tileMap(k, i)
        If (x >= zoom*-#tileSize) And (x <= ww) And (y >= zoom*-#tileSize) And (y <= wh)
          DisplayTransparentSprite(tileMap(k, i), x, y)
        EndIf
      EndIf       
    Next
  Next
 
  Select mode
     
    Case 1
      ; draw all regions directly
     
      StartDrawing(ScreenOutput())
      ForEach region()
        DrawingMode(#PB_2DDrawing_Outlined)
        Circle(zoom * region()\x - offsetX,
               zoom * region()\y - offsetY,
               zoom * region()\r,
               $ffffbbbb)
      Next
      StopDrawing()
     
    Case 2
      ; use sprite per region
     
      If Not initialized2
        initialized2 = #True
        ; create sprite for each region
        ForEach region()
          region()\sprW = 2 * region()\r + 1
          region()\sprH = 2 * region()\r + 1
          region()\spr = CreateSprite(#PB_Any, region()\sprW, region()\sprH, #PB_Sprite_AlphaBlending)
          StartDrawing(SpriteOutput(region()\spr))
          DrawingMode(#PB_2DDrawing_Outlined | #PB_2DDrawing_AllChannels)
          Box(0, 0, OutputWidth(), OutputHeight(), $aa00aa00)
          Circle(region()\r,
                 region()\r,
                 region()\r,
                 $ffffbbbb)
          StopDrawing()
        Next
      EndIf
     
      ; display each region sprite
      ForEach region()
        TransformSprite(region()\spr,
                        0, 0,
                        zoom * region()\sprW, 0,
                        zoom * region()\sprW, zoom * region()\sprH,
                        0, zoom * region()\sprH)
        DisplayTransparentSprite(region()\spr,
                                 zoom*(region()\x - region()\sprW/2) - offsetX,
                                 zoom*(region()\y - region()\sprH/2) - offsetY)
      Next
     
    Case 3
      ; use regionmap
     
      If Not initialized3
        initialized3 = #True
        ; create regionmap
        For k=0 To #regionMapSize
          For i=0 To #regionMapSize
            If regionMap(k, i)
              x = zoom * i * #regionSize
              y = zoom * k * #regionSize
              StartDrawing(SpriteOutput(regionMap(k, i)))
              DrawingMode(#PB_2DDrawing_Outlined | #PB_2DDrawing_AllChannels)
              Box(0, 0, OutputWidth(), OutputHeight(), $aa00aa00)
              ForEach region()
                RegionMinX = region()\x - region()\r - 1
                RegionMaxX = region()\x + region()\r + 1
                RegionMinY = region()\y - region()\r - 1
                RegionMaxY = region()\y + region()\r + 1
                ; draw region if in current regionmap area
                If (x <= RegionMaxX) And ((x+#regionSize) >= RegionMinX) And (y <= RegionMaxY) And ((y+#regionSize) >= RegionMinY)
                  Circle(region()\x - x,
                         region()\y - y,
                         region()\r,
                         $ffffbbbb)
                EndIf
              Next
              StopDrawing()
            EndIf
          Next
        Next
      EndIf
     
      ; display each regionmap area sprite
      For k=0 To #regionMapSize
        For i=0 To #regionMapSize
          If regionMap(k, i)
            x.f = zoom * i * #regionSize - offsetX
            y.f = zoom * k * #regionSize - offsetY
            If (x >= zoom*-#regionSize) And (x <= ww) And (y >= zoom*-#regionSize) And (y <= wh)
             
              TransformSprite(regionMap(k, i),
                              0, 0,
                              zoom * #regionSize, 0,
                              zoom * #regionSize, zoom * #regionSize,
                              0, zoom * #regionSize)
             
              DisplayTransparentSprite(regionMap(k, i), x, y)
            EndIf
          EndIf
        Next
      Next
     
  EndSelect
 
  ; display cursor
  DisplayTransparentSprite(cursor, MouseX(), MouseY())
 
  ; update fps/info sprite
  FR+1
  If ElapsedMilliseconds()>FRt
    FRt=ElapsedMilliseconds()+500
    FR$=Str(FR*2)
    FR=0
    StartDrawing(SpriteOutput(sprFR))
      DrawingMode(#PB_2DDrawing_Transparent)
      Box(0,0, OutputWidth(), OutputHeight(), $ff0000)
      DrawText(0,0, "fps:" + FR$ + " mode:" + mode)
    StopDrawing()
  EndIf
 
  ; display fps/info
  DisplaySprite(sprFR, 0,0)
 
  FlipBuffers()
  ClearScreen($333333)
  If IsWindow(win)
    ;Delay(10)
  EndIf
Until quit Or KeyboardPushed(#PB_Key_Escape)
   


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Fri May 31, 2019 1:49 pm 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 340
Location: France
Wow, thanks a lot! It will be very useful.

#NULL wrote:
If your world is very large that will create a lot of sprites though, even if each of them is smaller than the screen and only the visible one are actually displayed. Anyway, just zooming sprites may not be enough if you want very large scale/zoom ratios or even infinite zoom. You may need a completely different approach without buffers/sprites, only store abstract shapes (like beziers curves) and rasterize it to pixels as a last step by drawing directly to screen.


About that... What is the problem with drawing on screen exactly? Instinctively, I'd say this abstract approach seems better for my project, the map being gigantic (it supposedly takes place in a galaxy).


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Fri May 31, 2019 3:59 pm 
Offline
Addict
Addict

Joined: Thu Aug 30, 2007 11:54 pm
Posts: 1008
Location: right here
Sprites are already in video memory so they are very efficient. But you can absolutely do stuff by drawing, especially if you have only a single drawing block. You can also use a canvas and the vector drawing library so you get nice antialiasing.


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Fri May 31, 2019 5:49 pm 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 340
Location: France
Canvas on a screen? My idea was to draw an image, then catch it in a sprite (the vector library is really cool!).


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Fri May 31, 2019 6:57 pm 
Offline
Addict
Addict

Joined: Thu Aug 30, 2007 11:54 pm
Posts: 1008
Location: right here
No, you would use just a canvas in a window, without a screen. You can't use sprites then, but could still draw images. And Yes, you can also verctordraw to an image and put it on a sprite, then display the sprite, or draw the image directly to the screen.


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Sat Jun 01, 2019 2:43 am 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 340
Location: France
Any reason you would use TransformSprite() instead of:

Code:
ZoomSprite(region()\spr, zoom * region()\sprW, zoom * region()\sprH)


Less headaches :)

Anyway, ScreenOutput() has really poor performances, and canvas too when it comes to real time (+ they have no alpha), I think the second mode is the way to go!


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Sat Jun 01, 2019 7:47 am 
Offline
Addict
Addict

Joined: Thu Aug 30, 2007 11:54 pm
Posts: 1008
Location: right here
I don't know if ZoomSprite() is just a convenience function or what's the difference. TransformSprite can do other things like skewing and mirroring/flipping. As for zooming only I guess ZoomSprite() is appropriate. :)


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Mon Jun 03, 2019 10:23 am 
Offline
Enthusiast
Enthusiast

Joined: Wed Sep 18, 2013 11:54 am
Posts: 340
Location: France
Sorry to bother again, I'm really not good at that... How would you do in your example to keep the camera centered? When you zoom in and out while keeping the top left at the center of the screen, the top left (0, 0) remains at the center. How to do that when another point is at the center of the screen?

In other words, how to avoid the full map going to the top left when zooming out?


Top
 Profile  
Reply with quote  
 Post subject: Re: Drawing an area on a Screen()
PostPosted: Mon Jun 03, 2019 12:39 pm 
Offline
Addict
Addict

Joined: Thu Aug 30, 2007 11:54 pm
Posts: 1008
Location: right here
You can do it with my code by changing the scroll/zoom part as follows:
Code:
  ; scroll/zoom
  offsetX + MouseDeltaX()
  offsetY + MouseDeltaY()
  Define.f zoomPrevious = zoom
  If MouseButton(1)
    zoom + 0.01
  ElseIf MouseButton(2)
    zoom - 0.01
  EndIf
  If zoom <> zoomPrevious
    offsetX - ((((offsetX + ww/2) / zoom) - ((offsetX + ww/2) / zoomPrevious)) * zoom)
    offsetY - ((((offsetY + wh/2) / zoom) - ((offsetY + wh/2) / zoomPrevious)) * zoom)
  EndIf
I added a variable zoomPrevious, and if the zoom changed I adapt the offset to center again the previously centered world coordinate.
((offsetX + ww/2) / zoom) is the world coordinate that is currently centered in the screen with the current/new zoom. Then I get the offset difference by substracting ((offsetX + ww/2) / zoomPrevious)), the world coordinate that was centered with the previous/old zoom. That difference is than rescaled back to the current zoom by * zoom and the offset is corrected by that value.


Last edited by #NULL on Mon Jun 03, 2019 1:00 pm, edited 2 times in total.

Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye