November 28, 2020, 02:02:12 AM

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

#### 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)