November 28, 2020, 02:02:12 AM

Author Topic: [bmx] Position to Iso Index by beanage [ 1+ years ago ]  (Read 375 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
[bmx] Position to Iso Index by beanage [ 1+ years ago ]
« on: June 29, 2017, 12:28:39 AM »
Title : Position to Iso Index
Author : beanage
Posted : 1+ years ago

Description : Hey,

this is a solution I had a hard time to find myself, so I thought I'd share it with you. It basically allows you to convert from a 2d position to the x/y index of the underlying isometric tile. With a horrible amount of border cases to consider, you really won't wanna reinvent the wheel there. Feel free to use, modify, sell, marry my code :)

Theres propably a dozen approaches, what I can claim about mine is that it is fast [O(1)], numerically stable, and last but not least working perfectly accurate. The parameters processed can be observed in the demo.

Here is a screenshot of the testing application I wrote for the algorithm:


Here is the coresponding code. Check out the <getTileIndizesAtPosition> Method to see the algorithm. Eventually part of the code originates from an upcoming engine of mine, so don't wonder about identifiers like "isoWorld" :)


Code :
Code: BlitzMax
  1. SuperStrict
  2.  
  3. AppTitle = "Isometric Index Algorithm Demo"
  4.  
  5. Global TILESIZE:Int = 50
  6. Global DEBUGINFO:Double[7] '[n,y,m,x,h,m_,n_]
  7.  
  8. Type isoWorld
  9.         Field _terrain:isoTerritory[][] '[y][x]
  10.         Field _scale:Double = TILESIZE
  11.         Field _tile_w:Int = 2
  12.         Field _tile_h:Int = 1
  13.         Field _width:Double = 500.0
  14.         Field _height:Double = 500.0
  15.        
  16.         Method _initTerritory( divx_:Int, divy_:Int, load_instant_:Int )
  17.                 Local w_:Double = _width/ Double divx_
  18.                 Local h_:Double = _width/ Double divx_
  19.                 Local m_:Int = w_/ _scale
  20.                 Local n_:Int = h_/ _scale
  21.                
  22.                 _terrain = _terrain[..divy_ ]
  23.                 For Local j_:Int = 0 Until divy_
  24.                         _terrain[ j_ ] = _terrain[ j_][..divx_ ]
  25.                         For Local i_:Int = 0 Until divx_
  26.                                 _terrain[ j_][i_ ] = isoTerritory.Create(Self, i_, j_, m_, n_, w_, h_)
  27.                         Next
  28.                 Next
  29.         End Method
  30.        
  31.         Method getTileScale:Double()
  32.                 Return _scale
  33.         End Method
  34.        
  35.         Method getTileHWratio:Double()
  36.                 Return (Double _tile_h)/(Double _tile_w)
  37.         End Method
  38.        
  39.         Method getTerritory:isoTerritory( i_:Int, j_:Int )
  40.                 If (j_< _terrain.Length) And (j_>= 0)
  41.                         If (i_< _terrain[ j_ ].Length) And (i_>= 0)
  42.                                 Return _terrain[ j_][i_ ]
  43.                         End If
  44.                        
  45.                 End If
  46.         End Method
  47. End Type
  48.  
  49. Type isoTerritory
  50.         Field _world:isoWorld
  51.         Field _w:Double
  52.         Field _h:Double
  53.         Field _i:Int
  54.         Field _j:Int
  55.         Field _objects:Object[][] '[x][y] TList|isoObject
  56.        
  57.         Function Create:isoTerritory( world_:isoWorld, i_:Int, j_:Int, m_:Int, n_:Int, w_:Double, h_:Double )
  58.                 Local ret_:isoTerritory = New isoTerritory
  59.                 ret_._world = world_
  60.                 ret_._w = w_
  61.                 ret_._h = h_
  62.                 ret_._i = i_
  63.                 ret_._j = j_
  64.                 'init object list arrays
  65.                 ret_._objects = ret_._objects[..n_ ]
  66.                 For Local k_:Int = 0 Until n_
  67.                         ret_._objects[ k_ ] = ret_._objects[ k_ ][..m_ ]
  68.                 Next
  69.                
  70.                 Return ret_
  71.         End Function
  72.  
  73.         Method getNumTilesW:Int()
  74.                 Return _objects[0].Length
  75.         End Method
  76.        
  77.         Method getNumTilesH:Int()
  78.                 Return _objects.Length
  79.         End Method
  80.  
  81.         Function DoublesAreEqual:Int( d1_:Double, d2_:Double )
  82.                 Return Abs(d1_- d2_)< .00001!
  83.         End Function
  84.        
  85.         Method getTileIndizesAtPosition:Int( pos_:Double[], out_i_:Int Ptr, out_j_:Int Ptr ) 'returns false if no tile at that position. pos must be relative to the territory.
  86.                 If pos_.Length> 1 'just to make it sure
  87.                         Local halfsize_:Double = _world.getTileScale()/ 2
  88.                         Local y_:Double = pos_[1] 'y pos
  89.                         Local n_:Int = y_/ halfsize_ 'tile line index (presumption)
  90.                         If n_ Then n_:- 1
  91.                        
  92.                         '################
  93.                         DEBUGINFO[2] = n_
  94.                         '################
  95.                         y_ = (n_+ 1)* halfsize_ - y_ 'make y relative to tile
  96.                         '################
  97.                         DEBUGINFO[1] = y_
  98.                         '################
  99.                         Local off_:Double = ( n_ Mod 2 )* halfsize_ 'tile border function offset
  100.                         Local m_:Int = ( pos_[0]- off_ )/ _world.GetTileScale() 'tile row index (presumption)
  101.                         '################
  102.                         DEBUGINFO[4] = m_
  103.                         '################
  104.                         Local x_:Double = ( ( pos_[0] - off_ ) Mod _world.getTileScale() )+ off_
  105.                         '################
  106.                         DEBUGINFO[3] = x_
  107.                         '################
  108.                        
  109.                         out_i_[0] = m_
  110.                         out_j_[0] = n_
  111.                         If Not DoublesAreEqual(x_, halfsize_+ off_) 'calculate h
  112.                                 Local h_:Double
  113.                                
  114.                                 If x_< ( halfsize_+ off_ )
  115.                                         h_ = x_- off_
  116.                                         If Abs(y_)> h_ 'find neighbouring tile..
  117.                                                 If m_ And Not off_
  118.                                                         out_i_[0]:- 1
  119.                                                 ElseIf ..
  120.                                                         ( off_ And ( Abs(y_) < (off_- x_) ) And Not m_ ) .. 'this covers a special case where <n> is indented (off > 0) and <m> is zero.
  121.                                                         Or Not ( off_ Or m_ )
  122.                                                
  123.                                                         Return False
  124.                                                 End If
  125.                                         Else
  126.                                                 Return True
  127.                                                
  128.                                         End If
  129.                                 Else
  130.                                         h_ = halfsize_- ( x_- off_- halfsize_ )
  131.                                         If Abs(y_)> h_ 'find neighbouring tile..
  132.                                                 If off_ And ( m_ < ( getNumTilesW()- 1 ) )
  133.                                                         out_i_[0]:+ 1
  134.                                                 ElseIf off_
  135.                                                         Return False
  136.                                                 End If
  137.                                         Else
  138.                                                 Return True
  139.                                                
  140.                                         End If
  141.                                
  142.                                 End If
  143.                                 '################
  144.                                 DEBUGINFO[0] = h_
  145.                                 '################
  146.                                 If ( y_< 0 ) And ( n_< getNumTilesH()- 1 )
  147.                                         out_j_[0]:+ 1
  148.                                 ElseIf ( y_> 0 ) And n_
  149.                                         out_j_[0]:- 1
  150.                                 Else
  151.                                         Return False
  152.                                 End If
  153.                        
  154.                         '################
  155.                         Else
  156.                         DEBUGINFO[4] = -1
  157.                         '################
  158.                         End If
  159.                        
  160.                 End If
  161.                 Return True
  162.         End Method
  163. End Type
  164.  
  165. Global world:isoWorld = New isoWorld
  166. world._initTerritory(1, 1, 0)
  167. Global territory:isoTerritory = world.getTerritory(0, 0)
  168.  
  169. Function drawTerritory()
  170.         Local mpos_:Double[] = [Double MouseX(), Double MouseY()]
  171.         Local reti_:Int
  172.         Local retj_:Int
  173.        
  174.         If Not territory.getTileIndizesAtPosition(mpos_, VarPtr reti_, VarPtr retj_)
  175.                 reti_ = -1
  176.                 retj_ = -1
  177.                
  178.         End If
  179.         DEBUGINFO[5] = retj_
  180.         DEBUGINFO[6] = reti_
  181.         For Local j_:Int = 0 Until territory.getNumTilesH()
  182.                 For Local i_:Int = 0 Until territory.getNumTilesW()
  183.                         If ( reti_ = i_ ) And ( retj_ = j_ )
  184.                                 SetColor 255, 0, 0
  185.                         Else
  186.                                 SetColor 255, 255, 255
  187.                         End If
  188.                         Local x_:Int = i_* TILESIZE + ( j_ Mod 2 )* TILESIZE/ 2
  189.                         Local y_:Int = j_* TILESIZE/ 2
  190.                         Local swh_:Int[] = [TextWidth(String(i_)), TextHeight(String(i_))]
  191.                        
  192.                         DrawLine x_+ 1, y_+ TILESIZE/ 2, x_+ TILESIZE/ 2, y_+ 1
  193.                         DrawLine x_+ TILESIZE/ 2, y_+ 1, x_+ TILESIZE - 1, y_+ TILESIZE/ 2
  194.                         DrawLine x_+ TILESIZE - 1, y_+ TILESIZE/ 2, x_+ TILESIZE/ 2, y_+ TILESIZE - 1
  195.                         DrawLine x_+ 1, y_+ TILESIZE/ 2, x_+ TILESIZE/ 2, y_+ TILESIZE - 1
  196.                         DrawText i_, x_ + TILESIZE/ 2 - swh_[0]/ 2, y_ + TILESIZE/ 2 - swh_[1]/ 2
  197.                 Next
  198.         Next
  199. End Function
  200.  
  201. Function DrawDebugInfo:Int( x_:Int, y_:Int, boxw_:Int, caption_:String, value_:String )
  202.         caption_:+ ":"
  203.         Local w_:Int = TextWidth(caption_)
  204.         Local h_:Int = TextHeight(caption_)
  205.        
  206.         SetColor 128, 128, 128
  207.         DrawRect x_- 5, y_- 5, boxw_, h_+ 10
  208.         SetColor 255, 255, 255
  209.         DrawText caption_, x_, y_
  210.         SetColor 0, 0, 0
  211.         DrawText value_, x_+ boxw_- TextWidth(value_)- 10, y_
  212.         Return h_+ 10
  213. End Function
  214.  
  215. Graphics 800, 600
  216. Repeat
  217.         Cls
  218.         drawTerritory
  219.         Local y_:Int = 600- (TextHeight("X") + 10)
  220.        
  221.         For Local i_:Int = 0 To 6
  222.                 Local caption_:String
  223.                
  224.                 Select i_
  225.                         Case 6
  226.                                 caption_ = "Final horizontal index      "
  227.                         Case 5
  228.                                 caption_ = "Final vertical index"
  229.                         Case 2 'n
  230.                                 caption_ = "Vertical index guess"
  231.                         Case 1 'y
  232.                                 caption_ = "Y relative to y index guess"
  233.                         Case 4 'm
  234.                                 caption_ = "Horizontal index guess"
  235.                         Case 3 'x
  236.                                 caption_ = "X relative to tile"
  237.                         Case 0 'h
  238.                                 caption_ = "Calculated h(x)"
  239.                 End Select
  240.                 DrawDebugInfo 10, y_, 400, caption_, String(DEBUGINFO[i_])[..5]
  241.                 y_:- 30
  242.         Next
  243.         SetColor 0, 255, 0
  244.         DrawLine MouseX(), MouseY()- 10, MouseX(), MouseY()+ 10
  245.         DrawLine MouseX()- 10, MouseY(), MouseX()+ 10, MouseY()
  246.         Flip False
  247. Until AppTerminate() Or KeyHit(KEY_ESCAPE)


Comments : none...

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal