void MapGrid::calculateFinalParameters(double& final_horz_spacing, double& final_vert_spacing, double& final_horz_offset, double& final_vert_offset, double& final_rotation, Map* map) const { double factor = ((unit == MetersInTerrain) ? (1000.0 / map->getScaleDenominator()) : 1); final_horz_spacing = factor * horz_spacing; final_vert_spacing = factor * vert_spacing; final_horz_offset = factor * horz_offset; final_vert_offset = factor * vert_offset; final_rotation = additional_rotation + M_PI / 2; const Georeferencing& georeferencing = map->getGeoreferencing(); if (alignment == GridNorth) { final_rotation += georeferencing.getGrivation() * M_PI / 180; // Shift origin to projected coordinates origin double prev_horz_offset = MapCoordF::dotProduct(MapCoordF(0, -1).rotated(final_rotation), MapCoordF(georeferencing.getMapRefPoint().x(), -1 * georeferencing.getMapRefPoint().y())); double target_horz_offset = MapCoordF::dotProduct(MapCoordF(0, -1).rotated(additional_rotation + M_PI / 2), MapCoordF(georeferencing.getProjectedRefPoint().x(), georeferencing.getProjectedRefPoint().y())); final_horz_offset -= factor * target_horz_offset - prev_horz_offset; double prev_vert_offset = MapCoordF::dotProduct(MapCoordF(1, 0).rotated(final_rotation), MapCoordF(georeferencing.getMapRefPoint().x(), -1 * georeferencing.getMapRefPoint().y())); double target_vert_offset = MapCoordF::dotProduct(MapCoordF(1, 0).rotated(additional_rotation + M_PI / 2), MapCoordF(georeferencing.getProjectedRefPoint().x(), georeferencing.getProjectedRefPoint().y())); final_vert_offset += factor * target_vert_offset - prev_vert_offset; } else if (alignment == TrueNorth) final_rotation += georeferencing.getDeclination() * M_PI / 180; }
MapCoordF DrawRectangleTool::calculateClosingVector() const { int cur_point_index = angles.size(); auto close_vector = MapCoordF(1, 0); close_vector.rotate(-angles[0]); if (drawingParallelTo(angles[0])) close_vector = close_vector.perpRight(); if (MapCoordF::dotProduct(close_vector, MapCoordF(preview_path->getCoordinate(0) - preview_path->getCoordinate(cur_point_index - 1))) < 0) close_vector = -close_vector; return close_vector; }
void DrawRectangleTool::updateHover(bool mouse_down) { if (shift_pressed) constrained_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, cur_map_widget)); else constrained_pos_map = cur_pos_map; if (!editingInProgress()) { setPreviewPointsPosition(constrained_pos_map); updateDirtyRect(); if (mouse_down && ctrl_pressed) pickDirection(constrained_pos_map, cur_map_widget); else if (!mouse_down) angle_helper->setCenter(constrained_pos_map); } else { hidePreviewPoints(); if (mouse_down && !dragging && (cur_pos - click_pos).manhattanLength() >= Settings::getInstance().getStartDragDistancePx()) { // Start dragging dragging = true; } if (!mouse_down || dragging) updateRectangle(); } }
bool DrawRectangleTool::mouseDoubleClickEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) { Q_UNUSED(map_coord); Q_UNUSED(widget); if (event->button() != Qt::LeftButton) return false; if (editingInProgress()) { // Finish drawing by double click. // As the double click is also reported as two single clicks first // (in total: press - release - press - double - release), // need to undo two steps in general. if (angles.size() > 2 && !ctrl_pressed) { undoLastPoint(); updateHover(false); } if (angles.size() <= 1) { abortDrawing(); } else { constrained_pos_map = MapCoordF(preview_path->getCoordinate(angles.size() - 1)); undoLastPoint(); finishDrawing(); } no_more_effect_on_click = true; } return true; }
void TextSymbol::createLineBelowRenderables(const Object* object, ObjectRenderables& output) const { const TextObject* text_object = reinterpret_cast<const TextObject*>(object); if (text_object->getNumLines()) { double scale_factor = calculateInternalScaling(); AreaSymbol area_symbol; area_symbol.setColor(line_below_color); MapCoordVector line_flags(4); MapCoordVectorF line_coords(4); VirtualPath line_path = { line_flags, line_coords }; line_flags.back().setHolePoint(true); QTransform transform = text_object->calcTextToMapTransform(); for (int i = 0; i < text_object->getNumLines(); ++i) { const TextObjectLineInfo* line_info = text_object->getLineInfo(i); if (!line_info->paragraph_end) continue; double line_below_x0; double line_below_x1; if (text_object->hasSingleAnchor()) { line_below_x0 = line_info->line_x; line_below_x1 = line_below_x0 + line_info->width; } else { double box_width = text_object->getBoxWidth() * scale_factor; line_below_x0 = -0.5 * box_width; line_below_x1 = line_below_x0 + box_width; } double line_below_y0 = line_info->line_y + getLineBelowDistance() * scale_factor; double line_below_y1 = line_below_y0 + getLineBelowWidth() * scale_factor; line_coords[0] = MapCoordF(transform.map(QPointF(line_below_x0, line_below_y0))); line_coords[1] = MapCoordF(transform.map(QPointF(line_below_x1, line_below_y0))); line_coords[2] = MapCoordF(transform.map(QPointF(line_below_x1, line_below_y1))); line_coords[3] = MapCoordF(transform.map(QPointF(line_below_x0, line_below_y1))); line_path.path_coords.update(0); output.insertRenderable(new AreaRenderable(&area_symbol, line_path)); } } }
ConstrainAngleToolHelper::ConstrainAngleToolHelper() : active_angle(-1), center(MapCoordF(0, 0)), active(true), have_default_angles_only(false) { connect(&Settings::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); }
void DrawRectangleTool::pickDirection(MapCoordF coord, MapWidget* widget) { MapCoord snap_position; snap_helper->snapToDirection(coord, widget, angle_helper.data(), &snap_position); angle_helper->setActive(true, MapCoordF(snap_position)); updateDirtyRect(); picked_direction = true; }
inline bool operator()(Object* object, MapPart* part, int object_index) { Q_UNUSED(part); Q_UNUSED(object_index); // If there is a selection, only clip selected objects if (map->getNumSelectedObjects() > 0 && !map->isObjectSelected(object)) return true; // Don't clip object itself if (object == cutout_object) return true; // Early out if (!object->getExtent().intersects(cutout_object->getExtent())) { if (!cut_away) add_step->addObject(object, object); return true; } if (object->getType() == Object::Point || object->getType() == Object::Text) { // Simple check if the (first) point is inside the area if (cutout_object->isPointInsideArea(MapCoordF(object->getRawCoordinateVector().at(0))) == cut_away) add_step->addObject(object, object); } else if (object->getType() == Object::Path) { if (object->getSymbol()->getContainedTypes() & Symbol::Area) { // Use the Clipper library to clip the area BooleanTool boolean_tool(cut_away ? BooleanTool::Difference : BooleanTool::Intersection, map); BooleanTool::PathObjects in_objects; in_objects.push_back(cutout_object); in_objects.push_back(object->asPath()); BooleanTool::PathObjects out_objects; if (!boolean_tool.executeForObjects(object->asPath(), in_objects, out_objects)) return true; add_step->addObject(object, object); new_objects.insert(new_objects.end(), out_objects.begin(), out_objects.end()); } else { // Use some custom code to clip the line BooleanTool boolean_tool(cut_away ? BooleanTool::Difference : BooleanTool::Intersection, map); BooleanTool::PathObjects out_objects; boolean_tool.executeForLine(cutout_object, object->asPath(), out_objects); add_step->addObject(object, object); new_objects.insert(new_objects.end(), out_objects.begin(), out_objects.end()); } } return true; }
bool DrawRectangleTool::mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) { cur_pos = event->pos(); cur_pos_map = map_coord; if (shift_pressed) cur_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, widget)); constrained_pos_map = cur_pos_map; if (no_more_effect_on_click) { no_more_effect_on_click = false; return true; } if (ctrl_pressed && event->button() == Qt::LeftButton && !editingInProgress()) { pickDirection(cur_pos_map, widget); return true; } bool result = false; if (editingInProgress()) { if (isDrawingButton(event->button()) && dragging) { dragging = false; result = mousePressEvent(event, cur_pos_map, widget); } if (event->button() == Qt::RightButton && drawOnRightClickEnabled()) { if (!dragging) { constrained_pos_map = MapCoordF(preview_path->getCoordinate(angles.size() - 1)); undoLastPoint(); } if (editingInProgress()) // despite undoLastPoint() finishDrawing(); return true; } } return result; }
double ConstrainAngleToolHelper::getConstrainedCursorPosMap(const MapCoordF& in_pos, MapCoordF& out_pos) { MapCoordF to_cursor = in_pos - center; double in_angle = -1 * to_cursor.angle(); if (!active) { out_pos = in_pos; return in_angle; } double lower_angle = in_angle, lower_angle_delta = 999; double higher_angle = in_angle, higher_angle_delta = -999; for (std::set<double>::const_iterator it = angles.begin(), end = angles.end(); it != end; ++it) { double delta = in_angle - *it; if (delta < -M_PI) delta = in_angle - (*it - 2*M_PI); else if (delta > M_PI) delta = (in_angle - 2*M_PI) - *it; if (delta > 0) { if (delta < lower_angle_delta) { lower_angle_delta = delta; lower_angle = *it; } } else { if (delta > higher_angle_delta) { higher_angle_delta = delta; higher_angle = *it; } } } double new_active_angle; if (lower_angle_delta < -1 * higher_angle_delta) new_active_angle = lower_angle; else new_active_angle = higher_angle; if (new_active_angle != active_angle) { emitActiveAngleChanged(); active_angle = new_active_angle; } MapCoordF unit_direction = MapCoordF(cos(active_angle), -sin(active_angle)); to_cursor = MapCoordF::dotProduct(unit_direction, to_cursor) * unit_direction; out_pos = center + to_cursor; return active_angle; }
void RotateTool::initImpl() { // Set initial rotation center to the bounding box center of the selected objects if (map()->getNumSelectedObjects() > 0) { QRectF rect; map()->includeSelectionRect(rect); rotation_center = MapCoordF(rect.center()); rotation_center_set = true; } }
MapCoordF MapGrid::getClosestPointOnGrid(MapCoordF position, Map* map) const { double final_horz_spacing, final_vert_spacing; double final_horz_offset, final_vert_offset; double final_rotation; calculateFinalParameters(final_horz_spacing, final_vert_spacing, final_horz_offset, final_vert_offset, final_rotation, map); position.rotate(final_rotation - M_PI / 2); return MapCoordF(qRound((position.x() - final_horz_offset) / final_horz_spacing) * final_horz_spacing + final_horz_offset, qRound((position.y() - final_vert_offset) / final_vert_spacing) * final_vert_spacing + final_vert_offset).rotated(-1 * (final_rotation - M_PI / 2)); }
void DrawPathTool::finishFollowing() { following = false; int last = preview_path->getCoordinateCount() - 1; if (last >= 3 && preview_path->getCoordinate(last - 3).isCurveStart()) { MapCoord first = preview_path->getCoordinate(last - 1); MapCoord second = preview_path->getCoordinate(last); previous_point_is_curve_point = true; previous_point_direction = -atan2(second.x() - first.x(), first.y() - second.y()); previous_pos_map = MapCoordF(second); previous_drag_map = MapCoordF(second.x() + (second.x() - first.x()) / 2, second.y() + (second.y() - first.y()) / 2); } else previous_point_is_curve_point = false; updateAngleHelper(); }
void DrawRectangleTool::undoLastPoint() { if (angles.size() == 1) { abortDrawing(); return; } deleteClosePoint(); preview_path->deleteCoordinate(preview_path->getCoordinateCount() - 1, false); if (angles.size() == 2) preview_path->deleteCoordinate(preview_path->getCoordinateCount() - 1, false); angles.pop_back(); forward_vector = MapCoordF(1, 0); forward_vector.rotate(-angles[angles.size() - 1]); angle_helper->setCenter(MapCoordF(preview_path->getCoordinate(angles.size() - 1))); updateRectangle(); }
void DrawFreehandTool::checkLineSegment(int a, int b, std::vector< bool >& point_mask) { if (b <= a + 1) return; const MapCoord& start_coord = preview_path->getRawCoordinateVector()[a]; const MapCoord& end_coord = preview_path->getRawCoordinateVector()[b]; // Find point between a and b with highest distance from line segment float max_distance_sq = -1; int best_index = a + 1; for (int i = a + 1; i < b; ++ i) { const MapCoord& coord = preview_path->getRawCoordinateVector()[i]; MapCoordF to_coord = MapCoordF(coord.x() - start_coord.x(), coord.y() - start_coord.y()); MapCoordF to_next = MapCoordF(end_coord.x() - start_coord.x(), end_coord.y() - start_coord.y()); MapCoordF tangent = to_next; tangent.normalize(); float distance_sq; float dist_along_line = MapCoordF::dotProduct(to_coord, tangent); if (dist_along_line <= 0) { distance_sq = to_coord.lengthSquared(); } else { float line_length = MapCoordF(end_coord).distanceTo(MapCoordF(start_coord)); if (dist_along_line >= line_length) { distance_sq = MapCoordF(coord).distanceSquaredTo(MapCoordF(end_coord)); } else { auto right = tangent.perpRight(); distance_sq = qAbs(MapCoordF::dotProduct(right, to_coord)); distance_sq = distance_sq*distance_sq; } } if (distance_sq > max_distance_sq) { max_distance_sq = distance_sq; best_index = i; } } // Make new segment? const float split_distance_sq = 0.09f*0.09f; if (max_distance_sq > split_distance_sq) { point_mask[best_index] = true; checkLineSegment(a, best_index, point_mask); checkLineSegment(best_index, b, point_mask); } }
void BooleanTool::rebuildSegmentFromPathOnly( const ClipperLib::IntPoint& start_point, const ClipperLib::IntPoint& second_point, const ClipperLib::IntPoint& second_last_point, const ClipperLib::IntPoint& end_point, PathObject* object) { MapCoord start_point_c = MapCoord::fromNative64(start_point.X, start_point.Y); MapCoord second_point_c = MapCoord::fromNative64(second_point.X, second_point.Y); MapCoord second_last_point_c = MapCoord::fromNative64(second_last_point.X, second_last_point.Y); MapCoord end_point_c = MapCoord::fromNative64(end_point.X, end_point.Y); MapCoordF polygon_start_tangent = MapCoordF(second_point_c - start_point_c); polygon_start_tangent.normalize(); MapCoordF polygon_end_tangent = MapCoordF(second_last_point_c - end_point_c); polygon_end_tangent.normalize(); double tangent_length = BEZIER_HANDLE_DISTANCE * start_point_c.distanceTo(end_point_c); object->addCoordinate(MapCoord(MapCoordF(start_point_c) + tangent_length * polygon_start_tangent)); object->addCoordinate(MapCoord((MapCoordF(end_point_c) + tangent_length * polygon_end_tangent))); object->addCoordinate(end_point_c); }
PassPoint PassPoint::load(QXmlStreamReader& xml) { Q_ASSERT(xml.name() == QLatin1String("passpoint")); XmlElementReader passpoint{xml}; PassPoint p; p.error = passpoint.attribute<double>(QLatin1String("error")); while (xml.readNextStartElement()) { QStringRef name = xml.name(); while (xml.readNextStartElement()) { if (xml.name() == QLatin1String("coord")) { try { if (name == QLatin1String("source")) p.src_coords = MapCoordF(MapCoord::load(xml)); else if (name == QLatin1String("destination")) p.dest_coords = MapCoordF(MapCoord::load(xml)); else if (name == QLatin1String("calculated")) p.calculated_coords = MapCoordF(MapCoord::load(xml)); else xml.skipCurrentElement(); // unsupported } catch (std::range_error& e) { throw FileFormatException(MapCoord::tr(e.what())); } } else xml.skipCurrentElement(); // unsupported } } return p; }
void EditPointTool::dragMove() { if (no_more_effect_on_click) return; if (editingInProgress()) { if (snapped_to_pos && handle_offset != MapCoordF(0, 0)) { // Snapped to a position. Correct the handle offset, so the // object moves to this position exactly. click_pos_map += handle_offset; object_mover->setStartPos(click_pos_map); handle_offset = MapCoordF(0, 0); } object_mover->move(constrained_pos_map, !(active_modifiers & Qt::ShiftModifier)); updatePreviewObjectsAsynchronously(); } else if (box_selection) { updateDirtyRect(); } }
void RotateTool::initImpl() { // Set initial rotation center to the bounding box center of the selected objects if (map()->getNumSelectedObjects() > 0) { QRectF rect; map()->includeSelectionRect(rect); rotation_center = rect.center(); } else { rotation_center = MapCoordF(cur_map_widget->getMapView()->center()); } angle_helper->setCenter(rotation_center); }
bool DrawPathTool::pickAngle(MapCoordF coord, MapWidget* widget) { MapCoord snap_position; bool picked = snap_helper->snapToDirection(coord, widget, angle_helper.data(), &snap_position); if (picked) angle_helper->setCenter(MapCoordF(snap_position)); else { updateAngleHelper(); angle_helper->setCenter(constrained_pos_map); } hidePreviewPoints(); updateDirtyRect(); return picked; }
void ScaleTool::init() { // Set initial scaling center to the center of the bounding box of the selected objects if (map()->getNumSelectedObjects() > 0) { QRectF rect; map()->includeSelectionRect(rect); scaling_center = MapCoordF(rect.center()); scaling_center_set = true; } connect(map(), SIGNAL(objectSelectionChanged()), this, SLOT(objectSelectionChanged())); connect(map(), SIGNAL(selectedObjectEdited()), this, SLOT(updateDirtyRect())); updateDirtyRect(); updateStatusText(); MapEditorTool::init(); }
void DrawPathTool::updateAngleHelper() { if (picked_angle) return; if (!preview_path || (updatePreviewPath(), preview_path->parts().empty())) { angle_helper->clearAngles(); angle_helper->addDefaultAnglesDeg(0); return; } const auto& part = preview_path->parts().back(); bool rectangular_stepping = true; double angle; if (part.size() >= 2) { bool ok = false; MapCoordF tangent = part.calculateTangent(part.size()-1, true, ok); if (!ok) tangent = MapCoordF(1, 0); angle = -tangent.angle(); } else { if (previous_point_is_curve_point) angle = previous_point_direction; else { angle = 0; rectangular_stepping = false; } } if (!part.empty()) angle_helper->setCenter(part.coords.back()); angle_helper->clearAngles(); if (rectangular_stepping) angle_helper->addAnglesDeg(angle * 180 / M_PI, 45); else angle_helper->addDefaultAnglesDeg(angle * 180 / M_PI); }
void DrawPathTool::updateHover() { if (shift_pressed) constrained_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, cur_map_widget)); else constrained_pos_map = cur_pos_map; if (!editingInProgress()) { // Show preview objects at this position setPreviewPointsPosition(constrained_pos_map); if (picked_angle) angle_helper->setCenter(constrained_pos_map); updateDirtyRect(); } else // if (draw_in_progress) { updateDrawHover(); } }
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); }
void TemplateImage::updatePosFromGeoreferencing() { // Determine map coords of three image corner points // by transforming the points from one Georeferencing into the other bool ok; MapCoordF top_left = map->getGeoreferencing().toMapCoordF(georef.data(), MapCoordF(-0.5, -0.5), &ok); if (!ok) { qDebug() << "updatePosFromGeoreferencing() failed"; return; // TODO: proper error message? } MapCoordF top_right = map->getGeoreferencing().toMapCoordF(georef.data(), MapCoordF(image.width() - 0.5, -0.5), &ok); if (!ok) { qDebug() << "updatePosFromGeoreferencing() failed"; return; // TODO: proper error message? } MapCoordF bottom_left = map->getGeoreferencing().toMapCoordF(georef.data(), MapCoordF(-0.5, image.height() - 0.5), &ok); if (!ok) { qDebug() << "updatePosFromGeoreferencing() failed"; return; // TODO: proper error message? } // Calculate template transform as similarity transform from pixels to map coordinates PassPointList pp_list; PassPoint pp; pp.src_coords = MapCoordF(-0.5 * image.width(), -0.5 * image.height()); pp.dest_coords = top_left; pp_list.push_back(pp); pp.src_coords = MapCoordF(0.5 * image.width(), -0.5 * image.height()); pp.dest_coords = top_right; pp_list.push_back(pp); pp.src_coords = MapCoordF(-0.5 * image.width(), 0.5 * image.height()); pp.dest_coords = bottom_left; pp_list.push_back(pp); QTransform q_transform; if (!pp_list.estimateNonIsometricSimilarityTransform(&q_transform)) { qDebug() << "updatePosFromGeoreferencing() failed"; return; // TODO: proper error message? } qTransformToTemplateTransform(q_transform, &transform); updateTransformationMatrices(); }
void MapEditorToolBase::calcConstrainedPositions(MapWidget* widget) { if (snap_helper->getFilter() != SnappingToolHelper::NoSnapping) { SnappingToolHelperSnapInfo info; constrained_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, widget, &info, snap_exclude_object)); constrained_pos = widget->mapToViewport(constrained_pos_map).toPoint(); snapped_to_pos = info.type != SnappingToolHelper::NoSnapping; } else { constrained_pos_map = cur_pos_map; constrained_pos = cur_pos; snapped_to_pos = false; } if (angle_helper->isActive()) { QPointF temp_pos; angle_helper->getConstrainedCursorPositions(constrained_pos_map, constrained_pos_map, temp_pos, widget); constrained_pos = temp_pos.toPoint(); } }
bool PassPointList::estimateSimilarityTransformation(TemplateTransform* transform) { auto num_pass_points = int(size()); if (num_pass_points == 1) { PassPoint* point = &at(0); MapCoordF offset = point->dest_coords - point->src_coords; transform->template_x += qRound64(1000 * offset.x()); transform->template_y += qRound64(1000 * offset.y()); point->calculated_coords = point->dest_coords; point->error = 0; } else if (num_pass_points >= 2) { // Create linear equation system and solve using the pseuo inverse // Derivation: // (Attention: not by a mathematician. Please correct any errors.) // // Start by stating that the original coordinates (x, y) multiplied // with the transformation matrix should give the desired coordinates (X, Y): // // | a b c| |x| |X| // | d e f| * |y| = |Y| // |1| // // The parametrization of the transformation matrix should be simplified because // we want to have isotropic scaling. // With s = scaling, r = rotation and (x, y) = offset, it looks like this: // // | s*cos(r) s*sin(r) x| | a b c| // |-s*sin(r) s*cos(r) y| =: |-b a d| // // With this, reordering the matrices to have the unknowns // in the second matrix results in: // // | x y 1 0| |a| |X| // | y -x 0 1| * |b| = |Y| // |c| // |d| // // For every pass point, two rows like this result. The complete, stacked // equation system is then "solved" as good as possible using the // pseudo inverse. Finally, s, r, x and y are recovered from a, b, c and d. Matrix mat(2*num_pass_points, 4); Matrix values(2*num_pass_points, 1); for (int i = 0; i < num_pass_points; ++i) { PassPoint* point = &at(i); mat.set(2*i, 0, point->src_coords.x()); mat.set(2*i, 1, point->src_coords.y()); mat.set(2*i, 2, 1); mat.set(2*i, 3, 0); mat.set(2*i+1, 0, point->src_coords.y()); mat.set(2*i+1, 1, -point->src_coords.x()); mat.set(2*i+1, 2, 0); mat.set(2*i+1, 3, 1); values.set(2*i, 0, point->dest_coords.x()); values.set(2*i+1, 0, point->dest_coords.y()); } Matrix transposed; mat.transpose(transposed); Matrix mat_temp, mat_temp2, pseudo_inverse; transposed.multiply(mat, mat_temp); if (!mat_temp.invert(mat_temp2)) return false; mat_temp2.multiply(transposed, pseudo_inverse); // Calculate transformation parameters Matrix output; pseudo_inverse.multiply(values, output); double move_x = output.get(2, 0); double move_y = output.get(3, 0); double rotation = qAtan2((-1) * output.get(1, 0), output.get(0, 0)); double scale = output.get(0, 0) / qCos(rotation); // Calculate transformation matrix double cosr = cos(rotation); double sinr = sin(rotation); Matrix trans_change(3, 3); trans_change.set(0, 0, scale * cosr); trans_change.set(0, 1, scale * (-sinr)); trans_change.set(1, 0, scale * sinr); trans_change.set(1, 1, scale * cosr); trans_change.set(0, 2, move_x); trans_change.set(1, 2, move_y); // Transform the original transformation parameters to get the new transformation transform->template_scale_x *= scale; transform->template_scale_y *= scale; transform->template_rotation -= rotation; auto temp_x = qRound(1000.0 * (trans_change.get(0, 0) * (transform->template_x/1000.0) + trans_change.get(0, 1) * (transform->template_y/1000.0) + trans_change.get(0, 2))); transform->template_y = qRound(1000.0 * (trans_change.get(1, 0) * (transform->template_x/1000.0) + trans_change.get(1, 1) * (transform->template_y/1000.0) + trans_change.get(1, 2))); transform->template_x = temp_x; // Transform the pass points and calculate error for (auto& point : *this) { point.calculated_coords = MapCoordF(trans_change.get(0, 0) * point.src_coords.x() + trans_change.get(0, 1) * point.src_coords.y() + trans_change.get(0, 2), trans_change.get(1, 0) * point.src_coords.x() + trans_change.get(1, 1) * point.src_coords.y() + trans_change.get(1, 2)); point.error = point.calculated_coords.distanceTo(point.dest_coords); } } return true; }
void EditPointTool::updateHoverState(MapCoordF cursor_pos) { HoverState new_hover_state = OverNothing; const Object* new_hover_object = nullptr; MapCoordVector::size_type new_hover_point = no_point; if (text_editor) { handle_offset = MapCoordF(0, 0); } else if (!map()->selectedObjects().empty()) { if (map()->selectedObjects().size() <= max_objects_for_handle_display) { // Try to find object node. auto best_distance_sq = std::numeric_limits<double>::max(); for (const auto object : map()->selectedObjects()) { MapCoordF handle_pos; auto hover_point = findHoverPoint(cur_map_widget->mapToViewport(cursor_pos), cur_map_widget, object, true, &handle_pos); if (hover_point == no_point) continue; auto distance_sq = cursor_pos.distanceSquaredTo(handle_pos); if (distance_sq < best_distance_sq) { new_hover_state |= OverObjectNode; new_hover_object = object; new_hover_point = hover_point; best_distance_sq = distance_sq; handle_offset = handle_pos - cursor_pos; } } if (!new_hover_state.testFlag(OverObjectNode)) { // No object node found. Try to find path object edge. /// \todo De-duplicate: Copied from EditLineTool auto click_tolerance_sq = qPow(0.001 * cur_map_widget->getMapView()->pixelToLength(clickTolerance()), 2); for (auto object : map()->selectedObjects()) { if (object->getType() == Object::Path) { PathObject* path = object->asPath(); float distance_sq; PathCoord path_coord; path->calcClosestPointOnPath(cursor_pos, distance_sq, path_coord); if (distance_sq >= +0.0 && distance_sq < best_distance_sq && distance_sq < qMax(click_tolerance_sq, qPow(path->getSymbol()->calculateLargestLineExtent(map()), 2))) { new_hover_state |= OverPathEdge; new_hover_object = path; new_hover_point = path_coord.index; best_distance_sq = distance_sq; handle_offset = path_coord.pos - cursor_pos; } } } } } if (!new_hover_state.testFlag(OverObjectNode) && selection_extent.isValid()) { QRectF selection_extent_viewport = cur_map_widget->mapToViewport(selection_extent); if (pointOverRectangle(cur_map_widget->mapToViewport(cursor_pos), selection_extent_viewport)) { new_hover_state |= OverFrame; handle_offset = closestPointOnRect(cursor_pos, selection_extent) - cursor_pos; } } } if (new_hover_state != hover_state || new_hover_object != hover_object || new_hover_point != hover_point) { hover_state = new_hover_state; // We have got a Map*, so we may get an non-const Object*. hover_object = const_cast<Object*>(new_hover_object); hover_point = new_hover_point; if (hover_state != OverNothing) start_drag_distance = 0; else start_drag_distance = Settings::getInstance().getStartDragDistancePx(); updateDirtyRect(); } Q_ASSERT((hover_state.testFlag(OverObjectNode) || hover_state.testFlag(OverPathEdge)) == (hover_object != nullptr)); }
void DrawPathTool::undoLastPoint() { Q_ASSERT(editingInProgress()); if (preview_path->getCoordinateCount() <= (preview_path->parts().front().isClosed() ? 3 : (path_has_preview_point ? 2 : 1))) { abortDrawing(); return; } auto& part = preview_path->parts().back(); auto last_index = part.last_index; auto prev_coord_index = part.prevCoordIndex(part.last_index); auto prev_coord = preview_path->getCoordinate(prev_coord_index); // Pre-undo preparation if (path_has_preview_point) { if (prev_coord.isCurveStart()) { // Undo just the preview point path_has_preview_point = false; } else { // Remove the preview point from a straight edge, preparing for re-adding. Q_ASSERT(!previous_point_is_curve_point); preview_path->deleteCoordinate(last_index, false); last_index = prev_coord_index; prev_coord_index = part.prevCoordIndex(part.last_index); prev_coord = preview_path->getCoordinate(prev_coord_index); path_has_preview_point = !prev_coord.isCurveStart(); } } if (prev_coord.isCurveStart()) { // Removing last point of a curve, no re-adding of preview point. MapCoord prev_drag = preview_path->getCoordinate(prev_coord_index+1); previous_point_direction = -atan2(prev_drag.x() - prev_coord.x(), prev_coord.y() - prev_drag.y()); previous_pos_map = MapCoordF(prev_coord); previous_drag_map = MapCoordF((prev_coord.x() + prev_drag.x()) / 2, (prev_coord.y() + prev_drag.y()) / 2); previous_point_is_curve_point = true; path_has_preview_point = false; } else if (!path_has_preview_point) { // Removing last point from a straight edge, no re-adding of preview point. previous_point_is_curve_point = false; } // Actually delete the last point of the edge. preview_path->deleteCoordinate(last_index, false); if (preview_path->getRawCoordinateVector().empty()) { // Re-add first point. prev_coord.setCurveStart(false); preview_path->addCoordinate(prev_coord); } // Post-undo if (path_has_preview_point) { // Re-add preview point. preview_path->addCoordinate(MapCoord(cur_pos_map)); } else if (previous_point_is_curve_point && dragging) { cur_pos = click_pos; cur_pos_map = click_pos_map; } dragging = false; updateHover(); updatePreviewPath(); updateAngleHelper(); updateDirtyRect(); }
bool DrawPathTool::mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget) { cur_map_widget = widget; created_point_at_last_mouse_press = false; if (editingInProgress() && ((event->button() == Qt::RightButton) && !drawOnRightClickEnabled())) { finishDrawing(); return true; } else if (editingInProgress() && ((event->button() == Qt::RightButton && event->buttons() & Qt::LeftButton) || (event->button() == Qt::LeftButton && event->buttons() & Qt::RightButton))) { if (!previous_point_is_curve_point) undoLastPoint(); if (editingInProgress()) finishDrawing(); return true; } else if (isDrawingButton(event->button())) { dragging = false; bool start_appending = false; if (shift_pressed) { SnappingToolHelperSnapInfo snap_info; MapCoord snap_coord = snap_helper->snapToObject(map_coord, widget, &snap_info); click_pos_map = MapCoordF(snap_coord); cur_pos_map = click_pos_map; click_pos = widget->mapToViewport(click_pos_map).toPoint(); // Check for following and appending if (!is_helper_tool) { if (!editingInProgress()) { if (snap_info.type == SnappingToolHelper::ObjectCorners && (snap_info.coord_index == 0 || snap_info.coord_index == snap_info.object->asPath()->getCoordinateCount() - 1) && snap_info.object->getSymbol() == editor->activeSymbol()) { // Appending to another path start_appending = true; startAppending(snap_info); } // Setup angle helper if (snap_helper->snapToDirection(map_coord, widget, angle_helper.data())) picked_angle = true; } else if (editingInProgress() && (snap_info.type == SnappingToolHelper::ObjectCorners || snap_info.type == SnappingToolHelper::ObjectPaths) && snap_info.object->getType() == Object::Path) { // Start following another path startFollowing(snap_info, snap_coord); return true; } } } else if (!editingInProgress() && ctrl_pressed) { // Start picking direction of an existing object picking_angle = true; pickAngle(map_coord, widget); return true; } else { click_pos = event->pos(); click_pos_map = map_coord; cur_pos_map = map_coord; } if (!editingInProgress()) { // Start a new path startDrawing(); angle_helper->setCenter(click_pos_map); updateSnapHelper(); path_has_preview_point = false; previous_point_is_curve_point = false; appending = start_appending; } else { if (!shift_pressed) angle_helper->getConstrainedCursorPosMap(click_pos_map, click_pos_map); cur_pos_map = click_pos_map; } // Set path point auto coord = MapCoord { click_pos_map }; if (draw_dash_points) coord.setDashPoint(true); if (preview_path->getCoordinateCount() > 0 && picked_angle) picked_angle = false; if (previous_point_is_curve_point) { // Do nothing yet, wait until the user drags or releases the mouse button } else if (path_has_preview_point) { preview_path->setCoordinate(preview_path->getCoordinateCount() - 1, coord); updateAngleHelper(); created_point_at_last_mouse_press = true; } else { if (preview_path->getCoordinateCount() == 0 || !preview_path->getCoordinate(preview_path->getCoordinateCount() - 1).isPositionEqualTo(coord)) { preview_path->addCoordinate(coord); updatePreviewPath(); if (!start_appending) updateAngleHelper(); created_point_at_last_mouse_press = true; } } path_has_preview_point = false; create_segment = true; updateDirtyRect(); updateStatusText(); return true; } return false; }