void DrawPathTool::updateDirtyRect() { QRectF rect; if (dragging) { rectIncludeSafe(rect, click_pos_map); rectInclude(rect, cur_pos_map); } if (editingInProgress() && previous_point_is_curve_point) { rectIncludeSafe(rect, previous_pos_map); rectInclude(rect, previous_drag_map); } if ((editingInProgress() && !dragging) || (!editingInProgress() && !shift_pressed && ctrl_pressed) || (!editingInProgress() && (picking_angle || picked_angle))) { angle_helper->includeDirtyRect(rect); } if (shift_pressed || (!editingInProgress() && ctrl_pressed)) snap_helper->includeDirtyRect(rect); includePreviewRects(rect); if (is_helper_tool) emit(dirtyRectChanged(rect)); else { if (rect.isValid()) map()->setDrawingBoundingBox(rect, qMax(qMax(dragging ? 1 : 0, angle_helper->getDisplayRadius()), snap_helper->getDisplayRadius()), true); else map()->clearDrawingBoundingBox(); } }
QRectF Template::calculateTemplateBoundingBox() const { // Create bounding box by calculating the positions of all corners of the transformed extent rect QRectF extent = getTemplateExtent(); QRectF bbox; rectIncludeSafe(bbox, templateToMap(extent.topLeft())); rectInclude(bbox, templateToMap(extent.topRight())); rectInclude(bbox, templateToMap(extent.bottomRight())); rectInclude(bbox, templateToMap(extent.bottomLeft())); return bbox; }
QRectF MapView::calculateViewBoundingBox(QRectF rect) const { auto top_left = mapToView(static_cast<MapCoordF>(rect.topLeft())); auto top_right = mapToView(static_cast<MapCoordF>(rect.topRight())); auto bottom_right = mapToView(static_cast<MapCoordF>(rect.bottomRight())); auto bottom_left = mapToView(static_cast<MapCoordF>(rect.bottomLeft())); rect = QRectF{ top_left, bottom_right }.normalized(); rectInclude(rect, top_right); rectInclude(rect, bottom_left); rect.adjust(-1.0, -1.0, +1.0, +1.0); return rect; }
QRectF MapView::calculateViewedRect(QRectF rect) const { auto top_left = viewToMapF(rect.topLeft()); auto top_right = viewToMapF(rect.topRight()); auto bottom_right = viewToMapF(rect.bottomRight()); auto bottom_left = viewToMapF(rect.bottomLeft()); rect = QRectF{ top_left, bottom_right }.normalized(); rectInclude(rect, top_right); rectInclude(rect, bottom_left); rect.adjust(-0.001, -0.001, +0.001, +0.001); return rect; }
int DrawPointTool::updateDirtyRectImpl(QRectF& rect) { Q_ASSERT(preview_object); int result = 0; rectIncludeSafe(rect, preview_object->getExtent()); if (isDragging() && preview_object->getSymbol()->asPoint()->isRotatable()) { rectInclude(rect, constrained_click_pos_map); rectInclude(rect, constrained_pos_map); result = qMax(3, angle_helper->getDisplayRadius()); } return result; }
QRectF MapPart::calculateExtent(bool include_helper_symbols) const { QRectF rect; int i = 0; int size = objects.size(); while (size > i && !rect.isValid()) { if ((include_helper_symbols || !objects[i]->getSymbol()->isHelperSymbol()) && !objects[i]->getSymbol()->isHidden()) { objects[i]->update(); rect = objects[i]->getExtent(); } ++i; } for (; i < size; ++i) { if ((include_helper_symbols || !objects[i]->getSymbol()->isHelperSymbol()) && !objects[i]->getSymbol()->isHidden()) { objects[i]->update(); rectInclude(rect, objects[i]->getExtent()); } } return rect; }
void TextSymbol::createBaselineRenderables( const TextObject* text_object, const VirtualCoordVector& coords, ObjectRenderables& output) const { const MapColor* dominant_color = guessDominantColor(); if (dominant_color && text_object->getNumLines() > 0) { // Insert text boundary LineSymbol line_symbol; line_symbol.setColor(dominant_color); line_symbol.setLineWidth(0); const TextObjectLineInfo* line = text_object->getLineInfo(0); QRectF text_bbox(line->line_x, line->line_y - line->ascent, line->width, line->ascent + line->descent); for (int i = 1; i < text_object->getNumLines(); ++i) { const TextObjectLineInfo* line = text_object->getLineInfo(i); rectInclude(text_bbox, QRectF(line->line_x, line->line_y - line->ascent, line->width, line->ascent + line->descent)); } Q_UNUSED(coords); // coords should be used for calcTextToMapTransform() QTransform text_to_map = text_object->calcTextToMapTransform(); PathObject path; path.addCoordinate(MapCoord(text_to_map.map(text_bbox.topLeft()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.topRight()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.bottomRight()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.bottomLeft()))); path.parts().front().setClosed(true, true); path.updatePathCoords(); LineRenderable* line_renderable = new LineRenderable(&line_symbol, path.parts().front(), false); output.insertRenderable(line_renderable); } }
int EditPointTool::updateDirtyRectImpl(QRectF& rect) { bool show_object_points = map()->selectedObjects().size() <= max_objects_for_handle_display; selection_extent = QRectF(); map()->includeSelectionRect(selection_extent); rectInclude(rect, selection_extent); int pixel_border = show_object_points ? (scaleFactor() * 6) : 1; // Control points if (show_object_points) { for (Map::ObjectSelection::const_iterator it = map()->selectedObjectsBegin(), end = map()->selectedObjectsEnd(); it != end; ++it) (*it)->includeControlPointsRect(rect); } // Text selection if (text_editor) text_editor->includeDirtyRect(rect); // Box selection if (isDragging() && box_selection) { rectIncludeSafe(rect, click_pos_map); rectIncludeSafe(rect, cur_pos_map); } return pixel_border; }
LineRenderable::LineRenderable(const LineSymbol* symbol, QPointF first, QPointF second) : Renderable(symbol->getColor()) , line_width(0.001f * symbol->getLineWidth()) , cap_style(Qt::FlatCap) , join_style(Qt::MiterJoin) { qreal half_line_width = (color_priority < 0) ? 0.0f : 0.5f * line_width; auto right = MapCoordF(second - first).perpRight(); right.normalize(); right *= half_line_width; extent.setTopLeft(first + right); rectInclude(extent, first - right); rectInclude(extent, second - right); rectInclude(extent, second + right); path.moveTo(first); path.lineTo(second); }
int DrawPointTool::updateDirtyRectImpl(QRectF& rect) { if (isDragging()) { rectIncludeSafe(rect, click_pos_map); rectInclude(rect, constrained_pos_map); if (preview_object) rectInclude(rect, preview_object->getExtent()); return qMax(1, angle_helper->getDisplayRadius()); } else { if (preview_object) { rectIncludeSafe(rect, preview_object->getExtent()); return 0; } else return -1; } }
void LineRenderable::extentIncludeJoin(quint32 i, float half_line_width, const LineSymbol* symbol, const VirtualPath& path) { auto coords = path.coords; if (symbol->getJoinStyle() == LineSymbol::RoundJoin) { rectInclude(extent, QPointF(coords[i].x() - half_line_width, coords[i].y() - half_line_width)); rectInclude(extent, QPointF(coords[i].x() + half_line_width, coords[i].y() + half_line_width)); return; } auto offset_length = half_line_width; auto params = path.calculateTangentScaling(i); auto& offset = params.first; if (symbol->getJoinStyle() == LineSymbol::MiterJoin) { offset_length *= qMin(params.second, 2.0 * LineSymbol::miterLimit()); } offset.setLength(offset_length); rectInclude(extent, coords[i] + offset); rectInclude(extent, coords[i] - offset); }
int CutoutTool::updateDirtyRectImpl(QRectF& rect) { rectInclude(rect, cutout_object->getExtent()); map()->includeSelectionRect(rect); // Box selection if (isDragging()) { rectIncludeSafe(rect, click_pos_map); rectIncludeSafe(rect, cur_pos_map); } return 0; }
void Template::drawOntoTemplate(MapCoordF* coords, int num_coords, QColor color, float width, QRectF map_bbox) { Q_ASSERT(canBeDrawnOnto()); Q_ASSERT(num_coords > 1); if (!map_bbox.isValid()) { map_bbox = QRectF(coords[0].x(), coords[0].y(), 0, 0); for (int i = 1; i < num_coords; ++i) rectInclude(map_bbox, coords[i]); } float radius = qMin(getTemplateScaleX(), getTemplateScaleY()) * qMax((width+1) / 2, 1.0f); QRectF radius_bbox = QRectF(map_bbox.left() - radius, map_bbox.top() - radius, map_bbox.width() + 2*radius, map_bbox.height() + 2*radius); drawOntoTemplateImpl(coords, num_coords, color, width); map->setTemplateAreaDirty(this, radius_bbox, 0); setHasUnsavedChanges(true); }
AreaRenderable::AreaRenderable(const AreaSymbol* symbol, const PathPartVector& path_parts) : Renderable(symbol->getColor()) { if (!path_parts.empty()) { auto part = begin(path_parts); if (part->size() > 2) { extent = part->path_coords.calculateExtent(); addSubpath(*part); auto last = end(path_parts); for (++part; part != last; ++part) { rectInclude(extent, part->path_coords.calculateExtent()); addSubpath(*part); } } } Q_ASSERT(extent.right() < 999999); // assert if bogus values are returned }
void LineRenderable::extentIncludeCap(quint32 i, float half_line_width, bool end_cap, const LineSymbol* symbol, const VirtualPath& path) { auto coords = path.coords; if (symbol->getCapStyle() == LineSymbol::RoundCap) { rectInclude(extent, QPointF(coords[i].x() - half_line_width, coords[i].y() - half_line_width)); rectInclude(extent, QPointF(coords[i].x() + half_line_width, coords[i].y() + half_line_width)); return; } auto right = path.calculateTangent(i).perpRight(); right.normalize(); rectInclude(extent, coords[i] + half_line_width * right); rectInclude(extent, coords[i] - half_line_width * right); if (symbol->getCapStyle() == LineSymbol::SquareCap) { auto back = right.perpRight(); float sign = end_cap ? -1 : 1; rectInclude(extent, coords[i] + half_line_width * (sign*back - right)); rectInclude(extent, coords[i] + half_line_width * (sign*back + right)); } }
LineRenderable::LineRenderable(const LineSymbol* symbol, const VirtualPath& virtual_path, bool closed) : Renderable(symbol->getColor()) , line_width(0.001f * symbol->getLineWidth()) { Q_ASSERT(virtual_path.size() >= 2); float half_line_width = (color_priority < 0) ? 0.0f : 0.5f * line_width; switch (symbol->getCapStyle()) { case LineSymbol::FlatCap: cap_style = Qt::FlatCap; break; case LineSymbol::RoundCap: cap_style = Qt::RoundCap; break; case LineSymbol::SquareCap: cap_style = Qt::SquareCap; break; case LineSymbol::PointedCap: cap_style = Qt::FlatCap; break; } switch (symbol->getJoinStyle()) { case LineSymbol::BevelJoin: join_style = Qt::BevelJoin; break; case LineSymbol::MiterJoin: join_style = Qt::MiterJoin; break; case LineSymbol::RoundJoin: join_style = Qt::RoundJoin; break; } auto& flags = virtual_path.coords.flags; auto& coords = virtual_path.coords; bool has_curve = false; bool hole = false; bool gap = false; QPainterPath first_subpath; auto i = virtual_path.first_index; path.moveTo(coords[i]); extent = QRectF(coords[i].x(), coords[i].y(), 0.0001f, 0.0001f); extentIncludeCap(i, half_line_width, false, symbol, virtual_path); for (++i; i <= virtual_path.last_index; ++i) { if (gap) { if (flags[i].isHolePoint()) { gap = false; hole = true; } else if (flags[i].isGapPoint()) { gap = false; if (first_subpath.isEmpty() && closed) { first_subpath = path; path = QPainterPath(); } path.moveTo(coords[i]); extentIncludeCap(i, half_line_width, false, symbol, virtual_path); } continue; } if (hole) { Q_ASSERT(!flags[i].isHolePoint() && "Two hole points in a row!"); if (first_subpath.isEmpty() && closed) { first_subpath = path; path = QPainterPath(); } path.moveTo(coords[i]); extentIncludeCap(i, half_line_width, false, symbol, virtual_path); hole = false; continue; } if (flags[i-1].isCurveStart()) { Q_ASSERT(i < virtual_path.last_index-1); has_curve = true; path.cubicTo(coords[i], coords[i+1], coords[i+2]); i += 2; } else path.lineTo(coords[i]); if (flags[i].isHolePoint()) hole = true; else if (flags[i].isGapPoint()) gap = true; if ((i < virtual_path.last_index && !hole && !gap) || (i == virtual_path.last_index && closed)) extentIncludeJoin(i, half_line_width, symbol, virtual_path); else extentIncludeCap(i, half_line_width, true, symbol, virtual_path); } if (closed) { if (first_subpath.isEmpty()) path.closeSubpath(); else path.connectPath(first_subpath); } // If we do not have the path coords, but there was a curve, calculate path coords. if (has_curve) { // This happens for point symbols with curved lines in them. const auto& path_coords = virtual_path.path_coords; Q_ASSERT(path_coords.front().param == 0.0); Q_ASSERT(path_coords.back().param == 0.0); for (auto i = path_coords.size()-1; i > 0; --i) { if (path_coords[i].param != 0.0) { const auto& pos = path_coords[i].pos; auto to_coord = pos - path_coords[i-1].pos; auto to_next = path_coords[i+1].pos - pos; to_coord.normalize(); to_next.normalize(); auto right = (to_coord + to_next).perpRight(); right.setLength(half_line_width); rectInclude(extent, pos + right); rectInclude(extent, pos - right); } } } Q_ASSERT(extent.right() < 999999); // assert if bogus values are returned }
TextRenderable::TextRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y, bool framing_line) : Renderable(color) { const QFont& font(symbol->getQFont()); const QFontMetricsF& metrics(symbol->getFontMetrics()); this->anchor_x = anchor_x; this->anchor_y = anchor_y; this->rotation = text_object->getRotation(); scale_factor = symbol->getFontSize() / TextSymbol::internal_point_size; this->framing_line = framing_line; framing_line_width = framing_line ? (2 * 0.001 * symbol->getFramingLineHalfWidth() / scale_factor) : 0; path.setFillRule(Qt::WindingFill); // Otherwise, when text and an underline intersect, holes appear int num_lines = text_object->getNumLines(); for (int i=0; i < num_lines; i++) { const TextObjectLineInfo* line_info = text_object->getLineInfo(i); double line_y = line_info->line_y; double underline_x0 = 0.0; double underline_y0 = line_info->line_y + metrics.underlinePos(); double underline_y1 = underline_y0 + metrics.lineWidth(); int num_parts = line_info->part_infos.size(); for (int j=0; j < num_parts; j++) { const TextObjectPartInfo& part(line_info->part_infos.at(j)); if (font.underline()) { if (j > 0) { // draw underline for gap between parts as rectangle // TODO: watch out for inconsistency between text and gap underline path.moveTo(underline_x0, underline_y0); path.lineTo(part.part_x, underline_y0); path.lineTo(part.part_x, underline_y1); path.lineTo(underline_x0, underline_y1); path.closeSubpath(); } underline_x0 = part.part_x; } path.addText(part.part_x, line_y, font, part.part_text); } } extent = path.controlPointRect(); extent = QRectF(scale_factor * (extent.left() - 0.5f * framing_line_width), scale_factor * (extent.top() - 0.5f * framing_line_width), scale_factor * (extent.width() + framing_line_width), scale_factor * (extent.height() + framing_line_width)); if (rotation != 0) { float rcos = cos(-rotation); float rsin = sin(-rotation); std::vector<QPointF> extent_corners; extent_corners.push_back(extent.topLeft()); extent_corners.push_back(extent.topRight()); extent_corners.push_back(extent.bottomRight()); extent_corners.push_back(extent.bottomLeft()); for (int i = 0; i < 4; ++i) { auto x = extent_corners[i].x() * rcos - extent_corners[i].y() * rsin; auto y = extent_corners[i].y() * rcos + extent_corners[i].x() * rsin; if (i == 0) extent = QRectF(x, y, 0, 0); else rectInclude(extent, QPointF(x, y)); } } extent = QRectF(extent.left() + anchor_x, extent.top() + anchor_y, extent.width(), extent.height()); Q_ASSERT(extent.right() < 999999); // assert if bogus values are returned }