1 | ///
|
---|
2 | /// This file is part of ILNumerics Community Edition.
|
---|
3 | ///
|
---|
4 | /// ILNumerics Community Edition - high performance computing for applications.
|
---|
5 | /// Copyright (C) 2006 - 2012 Haymo Kutschbach, http://ilnumerics.net
|
---|
6 | ///
|
---|
7 | /// ILNumerics Community Edition is free software: you can redistribute it and/or modify
|
---|
8 | /// it under the terms of the GNU General Public License version 3 as published by
|
---|
9 | /// the Free Software Foundation.
|
---|
10 | ///
|
---|
11 | /// ILNumerics Community Edition is distributed in the hope that it will be useful,
|
---|
12 | /// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
14 | /// GNU General Public License for more details.
|
---|
15 | ///
|
---|
16 | /// You should have received a copy of the GNU General Public License
|
---|
17 | /// along with ILNumerics Community Edition. See the file License.txt in the root
|
---|
18 | /// of your distribution package. If not, see <http://www.gnu.org/licenses/>.
|
---|
19 | ///
|
---|
20 | /// In addition this software uses the following components and/or licenses:
|
---|
21 | ///
|
---|
22 | /// =================================================================================
|
---|
23 | /// The Open Toolkit Library License
|
---|
24 | ///
|
---|
25 | /// Copyright (c) 2006 - 2009 the Open Toolkit library.
|
---|
26 | ///
|
---|
27 | /// Permission is hereby granted, free of charge, to any person obtaining a copy
|
---|
28 | /// of this software and associated documentation files (the "Software"), to deal
|
---|
29 | /// in the Software without restriction, including without limitation the rights to
|
---|
30 | /// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
---|
31 | /// the Software, and to permit persons to whom the Software is furnished to do
|
---|
32 | /// so, subject to the following conditions:
|
---|
33 | ///
|
---|
34 | /// The above copyright notice and this permission notice shall be included in all
|
---|
35 | /// copies or substantial portions of the Software.
|
---|
36 | ///
|
---|
37 | /// =================================================================================
|
---|
38 | ///
|
---|
39 |
|
---|
40 | |
---|
41 | |
---|
42 | using System; |
---|
43 | using System.Collections.Generic; |
---|
44 | using System.Text; |
---|
45 | using System.Diagnostics; |
---|
46 | |
---|
47 | namespace ILNumerics.Drawing { |
---|
48 | /// <summary> |
---|
49 | /// This class represents the camera's positioning and aiming direction. |
---|
50 | /// </summary> |
---|
51 | [DebuggerDisplay("r:{m_distance} Ï:{m_phiDebugDisp}° Ï:{m_rhoDebugDisp}° - P:{Position} - L:{LookAt}")] |
---|
52 | public class ILCamera { |
---|
53 | |
---|
54 | #region event handling |
---|
55 | /// <summary> |
---|
56 | /// fires, if the position of the camera has changed |
---|
57 | /// </summary> |
---|
58 | public event EventHandler Changed; |
---|
59 | /// <summary> |
---|
60 | /// Fires a Changed event |
---|
61 | /// </summary> |
---|
62 | public virtual void OnChange() { |
---|
63 | if (Changed != null && !m_suspended) |
---|
64 | Changed(this,new EventArgs()); |
---|
65 | } |
---|
66 | #endregion |
---|
67 | |
---|
68 | #region attributes |
---|
69 | // input attributes (writable) |
---|
70 | private bool m_suspended = false; |
---|
71 | protected float m_posX; |
---|
72 | protected float m_posY; |
---|
73 | protected float m_posZ; |
---|
74 | protected float m_topX; |
---|
75 | protected float m_topY; |
---|
76 | protected float m_topZ; |
---|
77 | protected float m_lookAtX; |
---|
78 | protected float m_lookAtY; |
---|
79 | protected float m_lookAtZ; |
---|
80 | // output/cached attributes (readonly) |
---|
81 | // polar coordinates |
---|
82 | private float m_distance = 1.0f; |
---|
83 | private float m_phi = 0; |
---|
84 | private float m_rho = 0.0f; |
---|
85 | /// <summary> |
---|
86 | /// Offset angle for 2nd cached triangular phi value (needed for surface plots) |
---|
87 | /// </summary> |
---|
88 | internal float Offset = (float)(Math.PI / 4); |
---|
89 | /// <summary> |
---|
90 | /// cachced triangular phi value with offset (needed for surface plots) |
---|
91 | /// </summary> |
---|
92 | internal float CosPhiShift; |
---|
93 | /// <summary> |
---|
94 | /// cachced triangular phi value with offset (needed for surface plots) |
---|
95 | /// </summary> |
---|
96 | internal float SinPhiShift; |
---|
97 | /// <summary> |
---|
98 | /// cached value for cosine of phi - this is readonly and for performance only. |
---|
99 | /// </summary> |
---|
100 | internal float CosPhi; |
---|
101 | /// <summary> |
---|
102 | /// cached value for sine of phi - this is readonly and for performance only. |
---|
103 | /// </summary> |
---|
104 | internal float SinPhi; |
---|
105 | /// <summary> |
---|
106 | /// cached value for cosine of rho - this is readonly and for performance only. |
---|
107 | /// </summary> |
---|
108 | internal float CosRho; |
---|
109 | /// <summary> |
---|
110 | /// cached value for sine of rho - this is readonly and for performance only. |
---|
111 | /// </summary> |
---|
112 | internal float SinRho; |
---|
113 | private CameraQuadrant m_quadrant; |
---|
114 | #endregion |
---|
115 | |
---|
116 | #region properties |
---|
117 | |
---|
118 | /// <summary> |
---|
119 | /// point, the camera is aiming at (world coords) |
---|
120 | /// </summary> |
---|
121 | public ILPoint3Df LookAt { |
---|
122 | get { return new ILPoint3Df(m_lookAtX,m_lookAtY,m_lookAtZ); } |
---|
123 | set { |
---|
124 | //m_posX += (value.X - m_lookAtX); |
---|
125 | //m_posY += (value.Y - m_lookAtY); |
---|
126 | //m_posZ += (value.Z - m_lookAtZ); |
---|
127 | m_lookAtX = value.X; |
---|
128 | m_lookAtY = value.Y; |
---|
129 | m_lookAtZ = value.Z; |
---|
130 | |
---|
131 | updatePosition(); |
---|
132 | OnChange(); |
---|
133 | } |
---|
134 | } |
---|
135 | |
---|
136 | /// <summary> |
---|
137 | /// distance from the scene (readonly) |
---|
138 | /// </summary> |
---|
139 | public float Distance { |
---|
140 | get { |
---|
141 | return m_distance; |
---|
142 | } |
---|
143 | set { |
---|
144 | m_distance = Math.Abs(value); |
---|
145 | updateCachedVars(); |
---|
146 | updatePosition(); |
---|
147 | OnChange(); |
---|
148 | } |
---|
149 | } |
---|
150 | /// <summary> |
---|
151 | /// debugger helper: display phi in degrees (readonly) |
---|
152 | /// </summary> |
---|
153 | private int m_phiDebugDisp { |
---|
154 | get { |
---|
155 | return (int)Math.Round(m_phi * 180 / Math.PI); |
---|
156 | } |
---|
157 | } |
---|
158 | /// <summary> |
---|
159 | /// debugger helper: display rho in degrees |
---|
160 | /// </summary> |
---|
161 | private int m_rhoDebugDisp { |
---|
162 | get { |
---|
163 | return (int)Math.Round(m_rho * 180 / Math.PI); |
---|
164 | } |
---|
165 | } |
---|
166 | /// <summary> |
---|
167 | /// rotation of the scene (seen from above) [radians, readlony, rel. to lookat] |
---|
168 | /// </summary> |
---|
169 | public float Phi { |
---|
170 | get { |
---|
171 | return m_phi; |
---|
172 | } |
---|
173 | set { |
---|
174 | m_phi = (float)((value + (Math.PI * 2)) % (Math.PI * 2)); |
---|
175 | updateCachedVars(); |
---|
176 | updatePosition(); |
---|
177 | OnChange(); |
---|
178 | } |
---|
179 | } |
---|
180 | /// <summary> |
---|
181 | /// pitch of the scene [radians], setting moves camera around lookat point |
---|
182 | /// </summary> |
---|
183 | public float Rho { |
---|
184 | get { |
---|
185 | return m_rho; |
---|
186 | } |
---|
187 | set { |
---|
188 | if (value < 0.0f) |
---|
189 | m_rho = 0.0f; |
---|
190 | else if (value > Math.PI) |
---|
191 | m_rho = (float)Math.PI; |
---|
192 | else |
---|
193 | m_rho = value; |
---|
194 | updateCachedVars(); |
---|
195 | updatePosition(); |
---|
196 | OnChange(); |
---|
197 | } |
---|
198 | } |
---|
199 | |
---|
200 | /// <summary> |
---|
201 | /// Quadrant the camera is currently watching the scene from |
---|
202 | /// </summary> |
---|
203 | public CameraQuadrant Quadrant { |
---|
204 | get{ |
---|
205 | return m_quadrant; |
---|
206 | } |
---|
207 | } |
---|
208 | /// <summary> |
---|
209 | /// Determine, if camera is placed in an upper quadrant of the scene |
---|
210 | /// </summary> |
---|
211 | public bool LooksFromTop { |
---|
212 | get { |
---|
213 | return m_rho < Math.PI/2; |
---|
214 | } |
---|
215 | } |
---|
216 | /// <summary> |
---|
217 | /// Determine, if camera is located in an left quadrant of the scene |
---|
218 | /// </summary> |
---|
219 | public bool LooksFromLeft { |
---|
220 | get { |
---|
221 | return Math.Sin(m_phi) < 0; |
---|
222 | } |
---|
223 | } |
---|
224 | /// <summary> |
---|
225 | /// Determine, if camera is located in an front quadrant of the scene |
---|
226 | /// </summary> |
---|
227 | public bool LooksFromFront { |
---|
228 | get { |
---|
229 | return Math.Cos(m_phi) >= 0; |
---|
230 | } |
---|
231 | } |
---|
232 | /// <summary> |
---|
233 | /// true, when looking from top on the un-rotated scene (common for 2D plots) |
---|
234 | /// </summary> |
---|
235 | public bool Is2DView { |
---|
236 | get { |
---|
237 | return Math.Abs(SinPhi) < 1e-5 && Math.Abs(SinRho) < 1e-5; |
---|
238 | } |
---|
239 | } |
---|
240 | /// <summary> |
---|
241 | /// get/set camera position, absolute cartesian coordinates |
---|
242 | /// </summary> |
---|
243 | /// <remarks>Keep in mind, the angle for phi points towards negative Y axis! The cartesian property |
---|
244 | /// <paramref name="Position"/> handles the camera position in absolute world coordinates, while the |
---|
245 | /// polar coordinates (Rho, Phi, Distance) supress the camera position by means of coordinates |
---|
246 | /// relative to the LookAt point (i.e. usually the center of the viewing cube)!</remarks> |
---|
247 | public ILPoint3Df Position { |
---|
248 | get { |
---|
249 | ILPoint3Df ret = new ILPoint3Df(m_posX, m_posY, m_posZ); |
---|
250 | return ret; |
---|
251 | } |
---|
252 | set { |
---|
253 | m_posX = value.X; |
---|
254 | m_posY = value.Y; |
---|
255 | m_posZ = value.Z; |
---|
256 | cart2Pol(value - new ILPoint3Df(m_lookAtX, m_lookAtY, m_lookAtZ)); |
---|
257 | updateCachedVars(); |
---|
258 | OnChange(); |
---|
259 | } |
---|
260 | } |
---|
261 | /// <summary> |
---|
262 | /// orientation of the camera, normalized, readonly |
---|
263 | /// </summary> |
---|
264 | /// <remarks>This vector is readonly always points 'upwards'.</remarks> |
---|
265 | public ILPoint3Df Top |
---|
266 | { |
---|
267 | get |
---|
268 | { |
---|
269 | ILPoint3Df ret = new ILPoint3Df(m_topX, m_topY, m_topZ); |
---|
270 | return ret; |
---|
271 | } |
---|
272 | } |
---|
273 | #endregion |
---|
274 | |
---|
275 | #region constructors |
---|
276 | public ILCamera (ILCamera vport) { |
---|
277 | m_rho = vport.m_rho; |
---|
278 | m_phi = vport.m_phi; |
---|
279 | m_distance = vport.m_distance; |
---|
280 | m_lookAtX = vport.m_lookAtX; |
---|
281 | m_lookAtY = vport.m_lookAtY; |
---|
282 | m_lookAtZ = vport.m_lookAtZ; |
---|
283 | m_posX = vport.m_posX; |
---|
284 | m_posY = vport.m_posY; |
---|
285 | m_posZ = vport.m_posZ; |
---|
286 | updateCachedVars(); |
---|
287 | computeQuadrant(); |
---|
288 | } |
---|
289 | /// <summary> |
---|
290 | /// Create a viewport: view at scene from top, no rotation |
---|
291 | /// </summary> |
---|
292 | public ILCamera () { |
---|
293 | m_posX = 0; |
---|
294 | m_posY = 0; |
---|
295 | m_posZ = 10; |
---|
296 | m_quadrant = CameraQuadrant.TopLeftFront; |
---|
297 | updateCachedVars(); |
---|
298 | } |
---|
299 | public ILCamera (float Phi, float Rho, float Distance) { |
---|
300 | m_rho = Rho; |
---|
301 | m_phi = Phi; |
---|
302 | m_distance = Distance; |
---|
303 | updateCachedVars(); |
---|
304 | updatePosition(); |
---|
305 | computeQuadrant(); |
---|
306 | } |
---|
307 | #endregion |
---|
308 | |
---|
309 | #region public interface |
---|
310 | /// <summary> |
---|
311 | /// suspend the firing of events until EventingResume() was called |
---|
312 | /// </summary> |
---|
313 | public void EventingSuspend() { |
---|
314 | m_suspended = true; |
---|
315 | } |
---|
316 | /// <summary> |
---|
317 | /// Resume firing 'Change' events after it has been suspended |
---|
318 | /// </summary> |
---|
319 | public void EventingResume() { |
---|
320 | EventingResume(true); |
---|
321 | } |
---|
322 | /// <summary> |
---|
323 | /// Resume firing 'Change' events, optionally skip pending events |
---|
324 | /// </summary> |
---|
325 | internal void EventingResume(bool fireEvents) { |
---|
326 | m_suspended = false; |
---|
327 | if (fireEvents) |
---|
328 | OnChange(); |
---|
329 | } |
---|
330 | /// <summary> |
---|
331 | /// Set both angles and distance at once |
---|
332 | /// </summary> |
---|
333 | /// <param name="phi">Rotation, radians</param> |
---|
334 | /// <param name="rho">Pitch, radians</param> |
---|
335 | /// <param name="distance">Distance from scene</param> |
---|
336 | public void Set(float phi, float rho, float distance) { |
---|
337 | if (distance < 0) |
---|
338 | throw new Exceptions.ILArgumentException("Camera distance must be positive!"); |
---|
339 | m_phi = (float)(phi % (Math.PI * 2)); |
---|
340 | m_rho = (float)(rho % (Math.PI)); |
---|
341 | m_distance = distance; |
---|
342 | updateCachedVars(); |
---|
343 | OnChange(); |
---|
344 | } |
---|
345 | /// <summary> |
---|
346 | /// Set complete camera position (angles and distance) at once |
---|
347 | /// </summary> |
---|
348 | /// <param name="phi">Rotation (degrees)</param> |
---|
349 | /// <param name="rho">Pitch (degrees)</param> |
---|
350 | /// <param name="distance">Distance from scene</param> |
---|
351 | public void SetDeg(float phi,float rho, float distance) { |
---|
352 | Set ((float)(phi/180.0 * Math.PI),(float)(rho / 180.0f * Math.PI),distance); |
---|
353 | } |
---|
354 | /// <summary> |
---|
355 | /// Convert camera position to string |
---|
356 | /// </summary> |
---|
357 | /// <returns>string display with distance,roatation and pitch</returns> |
---|
358 | public override string ToString() { |
---|
359 | return String.Format("r:{0} Ï:{1}° Ï:{2}° - P:{3} - L:{4}", |
---|
360 | m_distance,m_phiDebugDisp,m_rhoDebugDisp,Position,LookAt); |
---|
361 | } |
---|
362 | #endregion |
---|
363 | |
---|
364 | #region private helper |
---|
365 | /// <summary> |
---|
366 | /// update internal cartesian (absolut) coordinates of position relative |
---|
367 | /// to lookAt point. To be called after any polar coordinates were changed. |
---|
368 | /// </summary> |
---|
369 | private void updatePosition() { |
---|
370 | m_posX = m_lookAtX + (m_distance * SinRho * SinPhi); |
---|
371 | m_posY = m_lookAtY + (m_distance * SinRho * -CosPhi); |
---|
372 | m_posZ = m_lookAtZ + (m_distance * CosRho); |
---|
373 | } |
---|
374 | private void cart2Pol(ILPoint3Df cartVec) { |
---|
375 | cartVec.ToPolar(out m_distance, out m_phi, out m_rho); |
---|
376 | } |
---|
377 | ///// <summary> |
---|
378 | ///// update position, cons. polar coord and lookat as new |
---|
379 | ///// </summary> |
---|
380 | //private void pol2Cart() { |
---|
381 | |
---|
382 | //} |
---|
383 | |
---|
384 | private void computeQuadrant() { |
---|
385 | //if (m_phi == 0.0 && m_rho == 0.0) { |
---|
386 | // m_quadrant = CameraQuadrant.TopLeftFront; |
---|
387 | // return; |
---|
388 | //} |
---|
389 | if (m_rho < System.Math.PI / 2) { |
---|
390 | // top |
---|
391 | if (m_phi < Math.PI) { |
---|
392 | // right |
---|
393 | if (m_phi < Math.PI / 2) { |
---|
394 | // front |
---|
395 | m_quadrant = CameraQuadrant.TopRightFront; |
---|
396 | } else { |
---|
397 | // back |
---|
398 | m_quadrant = CameraQuadrant.TopRightBack; |
---|
399 | } |
---|
400 | } else { |
---|
401 | // left |
---|
402 | if (m_phi > Math.PI / 2 * 3) { |
---|
403 | // front |
---|
404 | m_quadrant = CameraQuadrant.TopLeftFront; |
---|
405 | } else { |
---|
406 | // back |
---|
407 | m_quadrant = CameraQuadrant.TopLeftBack; |
---|
408 | } |
---|
409 | } |
---|
410 | } else { |
---|
411 | // bottom |
---|
412 | if (m_phi < Math.PI) { |
---|
413 | // right |
---|
414 | if (m_phi < Math.PI / 2) { |
---|
415 | // front |
---|
416 | m_quadrant = CameraQuadrant.BottomRightFront; |
---|
417 | } else { |
---|
418 | // back |
---|
419 | m_quadrant = CameraQuadrant.BottomRightBack; |
---|
420 | } |
---|
421 | } else { |
---|
422 | // left |
---|
423 | if (m_phi > Math.PI / 2 * 3) { |
---|
424 | // front |
---|
425 | m_quadrant = CameraQuadrant.BottomLeftFront; |
---|
426 | } else { |
---|
427 | // back |
---|
428 | m_quadrant = CameraQuadrant.BottomLeftBack; |
---|
429 | } |
---|
430 | } |
---|
431 | } |
---|
432 | } |
---|
433 | private void updateCachedVars () { |
---|
434 | CosPhi = (float)Math.Cos(m_phi); |
---|
435 | SinPhi = (float)Math.Sin(m_phi); |
---|
436 | SinPhiShift = (float)Math.Sin(m_phi + Offset); |
---|
437 | CosPhiShift = (float)Math.Cos(m_phi + Offset); |
---|
438 | CosRho = (float)Math.Cos(m_rho); |
---|
439 | SinRho = (float)Math.Sin(m_rho); |
---|
440 | // update top |
---|
441 | ILPoint3Df top = ILPoint3Df.normalize(-SinPhi * CosRho, CosPhi * CosRho, SinRho); |
---|
442 | m_topX = top.X; |
---|
443 | m_topY = top.Y; |
---|
444 | m_topZ = top.Z; |
---|
445 | |
---|
446 | computeQuadrant(); |
---|
447 | } |
---|
448 | #endregion
|
---|
449 |
|
---|
450 | } |
---|
451 | } |
---|