bool FlarmDevice::DownloadFlight(Path path, OperationEnvironment &env) { FileOutputStream fos(path); BufferedOutputStream os(fos); if (env.IsCancelled()) return false; env.SetProgressRange(100); while (true) { // Create header for getting IGC file data FLARM::FrameHeader header = PrepareFrameHeader(FLARM::MT_GETIGCDATA); // Send request if (!SendStartByte() || !SendFrameHeader(header, env, 1000) || env.IsCancelled()) return false; // Wait for an answer and save the payload for further processing AllocatedArray<uint8_t> data; uint16_t length; bool ack = WaitForACKOrNACK(header.GetSequenceNumber(), data, length, env, 10000) == FLARM::MT_ACK; // If no ACK was received if (!ack || length <= 3 || env.IsCancelled()) return false; length -= 3; // Read progress (in percent) uint8_t progress = *(data.begin() + 2); env.SetProgressPosition(std::min((unsigned)progress, 100u)); const char *last_char = (const char *)data.end() - 1; bool is_last_packet = (*last_char == 0x1A); if (is_last_packet) length--; // Read IGC data const char *igc_data = (const char *)data.begin() + 3; os.Write(igc_data, length); if (is_last_packet) break; } os.Flush(); fos.Commit(); return true; }
FLARM::MessageType FlarmDevice::WaitForACKOrNACK(uint16_t sequence_number, AllocatedArray<uint8_t> &data, uint16_t &length, OperationEnvironment &env, unsigned timeout_ms) { const TimeoutClock timeout(timeout_ms); // Receive frames until timeout or expected frame found while (!timeout.HasExpired()) { // Wait until the next start byte comes around if (!WaitForStartByte(env, timeout.GetRemainingOrZero())) continue; // Read the following FrameHeader FLARM::FrameHeader header; if (!ReceiveFrameHeader(header, env, timeout.GetRemainingOrZero())) continue; // Read and check length of the FrameHeader length = header.GetLength(); if (length <= sizeof(header)) continue; // Calculate payload length length -= sizeof(header); // Read payload and check length data.GrowDiscard(length); if (!ReceiveEscaped(data.begin(), length, env, timeout.GetRemainingOrZero())) continue; // Verify CRC if (header.GetCRC() != FLARM::CalculateCRC(header, data.begin(), length)) continue; // Check message type if (header.type != FLARM::MT_ACK && header.type != FLARM::MT_NACK) continue; // Check payload length if (length < 2) continue; // Check whether the received ACK is for the right sequence number if (FromLE16(*((const uint16_t *)(const void *)data.begin())) == sequence_number) return (FLARM::MessageType)header.type; } return FLARM::MT_ERROR; }
bool FlarmDevice::ReadFlightInfo(RecordedFlightInfo &flight, OperationEnvironment &env) { // Create header for getting record information FLARM::FrameHeader header = PrepareFrameHeader(FLARM::MT_GETRECORDINFO); // Send request if (!SendStartByte() || !SendFrameHeader(header, env, 1000)) return false; // Wait for an answer and save the payload for further processing AllocatedArray<uint8_t> data; uint16_t length; uint8_t ack_result = WaitForACKOrNACK(header.GetSequenceNumber(), data, length, env, 1000); // If neither ACK nor NACK was received if (ack_result != FLARM::MT_ACK || length <= 2) return false; char *record_info = (char *)data.begin() + 2; return ParseRecordInfo(record_info, flight); }
unsigned PolygonToTriangles(const RasterPoint *points, unsigned num_points, AllocatedArray<GLushort> &triangles, unsigned min_distance) { triangles.GrowDiscard(3 * (num_points - 2)); return _PolygonToTriangles(points, num_points, triangles.begin(), min_distance); }
void FinishPolyline(Canvas &canvas) { if (mode != OUTLINE) { canvas.Select(*pen); mode = OUTLINE; } canvas.DrawPolyline(points.begin(), num_points); num_points = 0; }
void FinishPolygon(Canvas &canvas) { if (mode != SOLID) { canvas.SelectNullPen(); canvas.Select(*brush); mode = SOLID; } canvas.polygon(points.begin(), num_points); num_points = 0; }
void Canvas::polygon(const RasterPoint *points, unsigned num_points) { if (brush.is_hollow() && !pen.defined()) return; glVertexPointer(2, GL_VALUE, 0, points); if (!brush.is_hollow() && num_points >= 3) { brush.set(); static AllocatedArray<GLushort> triangle_buffer; triangle_buffer.grow_discard(3 * (num_points - 2)); unsigned idx_count = polygon_to_triangle(points, num_points, triangle_buffer.begin()); if (idx_count > 0) glDrawElements(GL_TRIANGLES, idx_count, GL_UNSIGNED_SHORT, triangle_buffer.begin()); } if (pen_over_brush()) { pen.set(); if (pen.get_width() <= 2) { glDrawArrays(GL_LINE_LOOP, 0, num_points); } else { vertex_buffer.grow_discard(2 * (num_points + 1)); unsigned vertices = line_to_triangle(points, num_points, vertex_buffer.begin(), pen.get_width(), true); if (vertices > 0) { glVertexPointer(2, GL_VALUE, 0, vertex_buffer.begin()); glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices); } } } }
void Canvas::DrawPolygon(const RasterPoint *points, unsigned num_points) { if (brush.IsHollow() && !pen.IsDefined()) return; #ifdef USE_GLSL OpenGL::solid_shader->Use(); #endif ScopeVertexPointer vp(points); if (!brush.IsHollow() && num_points >= 3) { brush.Bind(); std::unique_ptr<const GLBlend> blend; if(!brush.IsOpaque()) { blend = std::make_unique<const GLBlend>(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } static AllocatedArray<GLushort> triangle_buffer; unsigned idx_count = PolygonToTriangles(points, num_points, triangle_buffer); if (idx_count > 0) glDrawElements(GL_TRIANGLES, idx_count, GL_UNSIGNED_SHORT, triangle_buffer.begin()); } if (IsPenOverBrush()) { pen.Bind(); if (pen.GetWidth() <= 2) { glDrawArrays(GL_LINE_LOOP, 0, num_points); } else { unsigned vertices = LineToTriangles(points, num_points, vertex_buffer, pen.GetWidth(), true); if (vertices > 0) { vp.Update(vertex_buffer.begin()); glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices); } } pen.Unbind(); } }
void Canvas::DrawPolygon(const RasterPoint *points, unsigned num_points) { if (brush.IsHollow() && !pen.IsDefined()) return; glVertexPointer(2, GL_VALUE, 0, points); if (!brush.IsHollow() && num_points >= 3) { brush.Set(); static AllocatedArray<GLushort> triangle_buffer; unsigned idx_count = PolygonToTriangles(points, num_points, triangle_buffer); if (idx_count > 0) glDrawElements(GL_TRIANGLES, idx_count, GL_UNSIGNED_SHORT, triangle_buffer.begin()); } if (pen_over_brush()) { pen.Bind(); if (pen.GetWidth() <= 2) { glDrawArrays(GL_LINE_LOOP, 0, num_points); } else { unsigned vertices = LineToTriangles(points, num_points, vertex_buffer, pen.GetWidth(), true); if (vertices > 0) { glVertexPointer(2, GL_VALUE, 0, vertex_buffer.begin()); glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices); } } pen.Unbind(); } }
unsigned LineToTriangles(const PT *points, unsigned num_points, AllocatedArray<PT> &strip, unsigned line_width, bool loop, bool tcap) { // A line has to have at least two points if (num_points < 2) return 0; // allocate memory for triangle vertices // max. size: 2*(num_points + (int)(loop || tcap)) strip.GrowDiscard(2 * (num_points + 1)); // A closed line path needs to have at least three points if (loop && num_points < 3) // .. otherwise don't close it loop = false; float half_line_width = line_width * 0.5f; // strip will point to the start of the output array // s is the working pointer PT *s = strip.begin(); // a, b and c point to three consecutive points which are used to iterate // through the line given in 'points'. Where b is the current position, // a the previous point and c the next point. const PT *a, *b, *c; // pointer to the end of the original points array // used for faster loop conditions const auto points_end = points + num_points; // initialize a, b and c vertices if (loop) { b = points + num_points - 1; a = b - 1; // skip identical points before b while (a >= points && *a == *b) a--; if (a < points) // all points in the array are identical return 0; c = points; } else { a = points; b = a + 1; // skip identical points after a while (b != points_end && *a == *b) b++; if (b == points_end) // all points in the array are identical return 0; c = b + 1; } // skip identical points after b while (c != points_end && *b == *c) c++; if (!loop) { // add flat or triangle cap at beginning of line PT ba = *a - *b; Normalize(&ba, half_line_width); if (tcap) // add triangle cap coordinate to the output array AppendPoint(s, a->x + ba.x, a->y + ba.y); // add flat cap coordinates to the output array PT p; p.x = ba.y; p.y = -ba.x; AppendPoint(s, a->x - p.x, a->y - p.y); AppendPoint(s, a->x + p.x, a->y + p.y); } // add points by calculating the angle bisector of ab and bc int sign = 1; if (num_points >= 3) { while (c != points_end) { // skip zero or 180 degree bends // TODO: support 180 degree bends! if (!TriangleEmpty(*a, *b, *c)) { PT g = *b - *a, h = *c - *b; Normalize(&g, 1000.); Normalize(&h, 1000.); typename PT::product_type bisector_x = -g.y - h.y; typename PT::product_type bisector_y = g.x + h.x; float projected_length = (-g.y * bisector_x + g.x * bisector_y) * (1.f / 1000.f); if (projected_length < 400.f) { // acute angle, use the normal of the bisector instead projected_length = (g.x * bisector_x + g.y * bisector_y) * (1.f / 1000.f); std::swap(bisector_x, bisector_y); bisector_y *= -1; // the order of the triangles switches. keep track with 'sign' sign *= -1; } float scale = half_line_width / projected_length; if(std::is_integral<typename PT::product_type>::value) { bisector_x = sign * (typename PT::product_type) lround(bisector_x * scale); bisector_y = sign * (typename PT::product_type) lround(bisector_y * scale); } else { bisector_x = sign * bisector_x * scale; bisector_y = sign * bisector_y * scale; } AppendPoint(s, b->x - bisector_x, b->y - bisector_y); AppendPoint(s, b->x + bisector_x, b->y + bisector_y); } a = b; b = c; c++; while (c != points_end && *b == *c) // skip identical points c++; } } if (loop) { // repeat first two points at the end if (sign == 1) { AppendPoint(s, strip[0].x, strip[0].y); AppendPoint(s, strip[1].x, strip[1].y); } else { AppendPoint(s, strip[1].x, strip[1].y); AppendPoint(s, strip[0].x, strip[0].y); } } else { // add flat or triangle cap at end of line PT ab = *b - *a; Normalize(&ab, half_line_width); PT p; p.x = sign * -ab.y; p.y = sign * ab.x; AppendPoint(s, b->x - p.x, b->y - p.y); AppendPoint(s, b->x + p.x, b->y + p.y); if (tcap) AppendPoint(s, b->x + ab.x, b->y + ab.y); } return s - strip.begin(); }
/** * Grows an existing array, preserving data. */ T *grow(unsigned old_length, unsigned new_length) { array.grow_preserve(new_length, old_length); return array.begin(); }
/** * Obtains an array. Its values are undefined. */ T *get(unsigned _length) { array.grow_discard(_length); return array.begin(); }
const short *GetRow(unsigned y) const { return data.begin() + y * width; }
const short *GetData() const { return data.begin(); }
const TerrainHeight *GetRow(unsigned y) const { return data.begin() + y * width; }
const TerrainHeight *GetData() const { return data.begin(); }