Makefile: remove spurious tab
[xcsoar.git] / src / MapWindow / MapWindowGlideRange.cpp
blobddf0ebdb5ed42bac7605fcbc85d06f329ca0a5ea
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "MapWindow.hpp"
25 #include "Look/MapLook.hpp"
26 #include "Geo/GeoClip.hpp"
27 #include "Screen/Icon.hpp"
28 #include "Task/ProtectedRoutePlanner.hpp"
30 #ifdef ENABLE_OPENGL
31 #include "Screen/OpenGL/Triangulate.hpp"
32 #endif
34 #include <stdio.h>
35 #include "Util/StaticArray.hpp"
37 typedef std::vector<RasterPoint> RasterPointVector;
39 struct ProjectedFan {
40 /**
41 * The number of points of the associated ReachFan. The first
42 * ProjectedFan starts of ProjectedFans::points[0], followed by the
43 * second one at ProjectedFans::points[reach_fan0.size], etc.
45 unsigned size;
47 ProjectedFan() = default;
49 ProjectedFan(unsigned n):size(n) {
52 #ifdef ENABLE_OPENGL
53 void DrawFill(const RasterPoint *points, unsigned start) const {
54 /* triangulate the polygon */
55 AllocatedArray<GLushort> triangle_buffer;
57 unsigned idx_count = PolygonToTriangles(points + start, size,
58 triangle_buffer);
59 if (idx_count == 0)
60 return;
62 /* add offset to all vertex indices */
63 for (unsigned i = 0; i < idx_count; ++i)
64 triangle_buffer[i] += start;
66 glDrawElements(GL_TRIANGLES, idx_count, GL_UNSIGNED_SHORT,
67 triangle_buffer.begin());
70 void DrawOutline(unsigned start) const {
71 glDrawArrays(GL_LINE_LOOP, start, size);
73 #else
74 void DrawFill(Canvas &canvas, const RasterPoint *points) const {
75 canvas.DrawPolygon(&points[0], size);
78 void DrawOutline(Canvas &canvas, const RasterPoint *points) const {
79 canvas.DrawPolygon(&points[0], size);
81 #endif
84 struct ProjectedFans {
85 typedef StaticArray<ProjectedFan, FlatTriangleFanTree::REACH_MAX_FANS> ProjectedFanVector;
87 ProjectedFanVector fans;
89 /**
90 * All points of all ProjectedFan objects. The first one starts of
91 * points[0], followed by the second one at points[fans[0].size],
92 * etc.
94 RasterPointVector points;
96 #ifndef NDEBUG
97 unsigned remaining;
98 #endif
100 ProjectedFans()
101 #ifndef NDEBUG
102 :remaining(0)
103 #endif
105 /* try to guess the total number of vertices */
106 points.reserve(FlatTriangleFanTree::REACH_MAX_FANS * ROUTEPOLAR_POINTS / 10);
109 bool empty() const {
110 return fans.empty();
113 bool full() const {
114 return fans.full();
117 ProjectedFanVector::size_type size() const {
118 return fans.size();
121 ProjectedFan &Append(unsigned n) {
122 #ifndef NDEBUG
123 assert(remaining == 0);
124 remaining = n;
125 #endif
127 points.reserve(points.size() + n);
129 fans.push_back(ProjectedFan(n));
130 return fans.back();
133 void Append(const RasterPoint &pt) {
134 #ifndef NDEBUG
135 assert(remaining > 0);
136 --remaining;
137 #endif
139 points.push_back(pt);
142 #ifdef ENABLE_OPENGL
143 void Prepare() {
144 glVertexPointer(2, GL_VALUE, 0, &points[0]);
146 #endif
148 void DrawFill(Canvas &canvas) const {
149 assert(remaining == 0);
151 #ifdef ENABLE_OPENGL
152 unsigned start = 0;
153 const RasterPoint *points = &this->points[0];
154 for (auto i = fans.begin(), end = fans.end(); i != end; ++i) {
155 i->DrawFill(points, start);
156 start += i->size;
158 #else
159 const RasterPoint *points = &this->points[0];
160 for (auto i = fans.begin(), end = fans.end(); i != end; ++i) {
161 i->DrawFill(canvas, points);
162 points += i->size;
164 #endif
167 void DrawOutline(Canvas &canvas) const {
168 assert(remaining == 0);
170 #ifdef ENABLE_OPENGL
171 unsigned start = 0;
172 for (auto i = fans.begin(), end = fans.end(); i != end; ++i) {
173 i->DrawOutline(start);
174 start += i->size;
176 #else
177 const RasterPoint *points = &this->points[0];
178 for (auto i = fans.begin(), end = fans.end(); i != end; ++i) {
179 i->DrawOutline(canvas, points);
180 points += i->size;
182 #endif
186 typedef StaticArray<ProjectedFan, FlatTriangleFanTree::REACH_MAX_FANS> ProjectedFanVector;
188 class TriangleCompound: public TriangleFanVisitor {
189 /** Temporary container for TriangleFan processing */
190 StaticArray<GeoPoint, ROUTEPOLAR_POINTS+2> g;
191 /** Temporary container for TriangleFan clipping */
192 GeoPoint clipped[(ROUTEPOLAR_POINTS+2) * 3];
193 /** Projection to use for GeoPoint -> RasterPoint conversion */
194 const MapWindowProjection &proj;
195 /** GeoClip instance used for TriangleFan clipping */
196 const GeoClip clip;
198 public:
199 /** STL-Container of rasterized polygons */
200 ProjectedFans fans;
202 TriangleCompound(const MapWindowProjection& _proj)
203 :proj(_proj),
204 clip(_proj.GetScreenBounds().Scale(fixed(1.1)))
208 virtual void StartFan() {
209 // Clear the GeoPointVector for the next TriangleFan
210 g.clear();
213 virtual void AddPoint(const GeoPoint& p) {
214 // Add a new GeoPoint to the current TriangleFan
215 g.append(p);
218 virtual void
219 EndFan()
221 if (fans.full())
222 return;
224 // remove unnecessary inclusion of origin if next and last points are identical
225 unsigned start = 0;
226 const size_t gsize = g.size();
227 if (gsize > 2 && g[gsize - 1] == g[1])
228 start = 1;
230 if (gsize < start + 3)
231 return;
233 // Perform clipping on the GeoPointVector (Result: clipped)
234 unsigned size = clip.ClipPolygon(clipped, g.raw() + start, gsize - start);
235 // With less than three points we can't draw a polygon
236 if (size < 3)
237 return;
239 // Work directly on the RasterPoints in the fans vector
240 fans.Append(size);
242 // Convert GeoPoints to RasterPoints
243 for (unsigned i = 0; i < size; ++i)
244 fans.Append(proj.GeoToScreen(clipped[i]));
249 * Draw the final glide groundline (and shading) to the buffer
250 * and copy the transparent buffer to the canvas
251 * @param canvas The drawing canvas
252 * @param rc The area to draw in
253 * @param buffer The drawing buffer
255 void
256 MapWindow::DrawTerrainAbove(Canvas &canvas)
258 // Don't draw at all if
259 // .. no GPS fix
260 // .. not flying
261 // .. feature disabled
262 // .. feature inaccessible
263 if (!Basic().location_available
264 || !Calculated().flight.flying
265 || GetComputerSettings().features.final_glide_terrain == FeaturesSettings::FinalGlideTerrain::OFF
266 || route_planner == NULL)
267 return;
269 // Create a visitor for the Reach code
270 TriangleCompound visitor(render_projection);
272 // Fill the TriangleCompound with all TriangleFans in range
273 route_planner->AcceptInRange(render_projection.GetScreenBounds(), visitor);
275 // Exit early if not fans found
276 if (visitor.fans.empty())
277 return;
279 // @todo: update this rendering
281 // Don't draw shade if
282 // .. shade feature disabled
283 // .. pan mode activated
284 if (GetComputerSettings().features.final_glide_terrain == FeaturesSettings::FinalGlideTerrain::SHADE &&
285 IsNearSelf()) {
287 #ifdef ENABLE_OPENGL
289 visitor.fans.Prepare();
291 glEnable(GL_STENCIL_TEST);
292 glClear(GL_STENCIL_BUFFER_BIT);
294 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
296 glStencilFunc(GL_ALWAYS, 1, 1);
297 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
299 COLOR_WHITE.Set();
300 visitor.fans.DrawFill(canvas);
302 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
303 glStencilFunc(GL_NOTEQUAL, 1, 1);
304 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
306 glEnable(GL_BLEND);
307 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
309 canvas.Clear(Color(255, 255, 255, 77));
311 glDisable(GL_BLEND);
312 glDisable(GL_STENCIL_TEST);
314 #elif defined(USE_GDI)
316 // Get a buffer for drawing a mask
317 Canvas &buffer = buffer_canvas;
319 // Set the pattern colors
320 buffer.SetBackgroundOpaque();
321 buffer.SetBackgroundColor(COLOR_WHITE);
322 buffer.SetTextColor(Color(0xd0, 0xd0, 0xd0));
324 // Paint the whole buffer canvas with a pattern brush (small dots)
325 buffer.Clear(look.above_terrain_brush);
327 // Select the TerrainLine pen
328 buffer.SelectHollowBrush();
329 buffer.Select(look.reach_pen_thick);
330 buffer.SetBackgroundColor(Color(0xf0, 0xf0, 0xf0));
332 // Draw the TerrainLine polygons
333 visitor.fans.DrawOutline(buffer);
335 // Select a white brush (will later be transparent)
336 buffer.SelectNullPen();
337 buffer.SelectWhiteBrush();
339 // Draw the TerrainLine polygons to remove the
340 // brush pattern from the polygon areas
341 visitor.fans.DrawFill(buffer);
343 // Copy everything non-white to the buffer
344 canvas.CopyTransparentWhite(buffer);
346 /* skip the separate terrain line step below, because we have done
347 it already */
348 return;
350 #endif
354 if (visitor.fans.size() == 1) {
355 /* only one fan: we can draw a simple polygon */
357 #ifdef ENABLE_OPENGL
358 visitor.fans.Prepare();
359 look.reach_pen.Bind();
360 #else
361 // Select the TerrainLine pen
362 canvas.SelectHollowBrush();
363 canvas.Select(look.reach_pen);
364 canvas.SetBackgroundOpaque();
365 canvas.SetBackgroundColor(COLOR_WHITE);
367 // drop out extraneous line from origin
368 #endif
370 // Draw the TerrainLine polygon
372 visitor.fans.DrawOutline(canvas);
374 #ifdef ENABLE_OPENGL
375 look.reach_pen.Unbind();
376 #endif
377 } else {
378 /* more than one fan (turning reach enabled): we have to use a
379 stencil to draw the outline, because the fans may overlap */
381 #ifdef ENABLE_OPENGL
382 visitor.fans.Prepare();
384 glEnable(GL_STENCIL_TEST);
385 glClear(GL_STENCIL_BUFFER_BIT);
387 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
389 glStencilFunc(GL_ALWAYS, 1, 1);
390 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
392 COLOR_WHITE.Set();
393 visitor.fans.DrawFill(canvas);
395 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
396 glStencilFunc(GL_NOTEQUAL, 1, 1);
397 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
399 look.reach_pen_thick.Bind();
400 visitor.fans.DrawOutline(canvas);
401 look.reach_pen_thick.Unbind();
403 glDisable(GL_STENCIL_TEST);
405 #elif defined(USE_GDI)
407 // Get a buffer for drawing a mask
408 Canvas &buffer = buffer_canvas;
410 // Paint the whole buffer canvas white ( = transparent)
411 buffer.ClearWhite();
413 // Select the TerrainLine pen
414 buffer.SelectHollowBrush();
415 buffer.Select(look.reach_pen_thick);
416 buffer.SetBackgroundOpaque();
417 buffer.SetBackgroundColor(Color(0xf0, 0xf0, 0xf0));
419 // Draw the TerrainLine polygons
420 visitor.fans.DrawOutline(buffer);
422 // Select a white brush (will later be transparent)
423 buffer.SelectNullPen();
424 buffer.SelectWhiteBrush();
426 // Draw the TerrainLine polygons again to remove
427 // the lines connecting all the polygons
429 // This removes half of the TerrainLine line width !!
430 visitor.fans.DrawFill(buffer);
432 // Copy everything non-white to the buffer
433 canvas.CopyTransparentWhite(buffer);
435 #endif
440 void
441 MapWindow::DrawGlideThroughTerrain(Canvas &canvas) const
443 if (!Calculated().flight.flying ||
444 !Calculated().terrain_warning ||
445 Calculated().terrain_warning_location.Distance(Basic().location) < fixed(500.0))
446 return;
448 RasterPoint sc;
449 if (render_projection.GeoToScreenIfVisible(Calculated().terrain_warning_location,
450 sc))
451 look.terrain_warning_icon.Draw(canvas, sc.x, sc.y);