void PenCommandObject::on_press() { m_segment.setId(getSegmentId(m_startSegments)); m_segment.setMinY(0); m_segment.setMaxY(1); m_originalPress = m_state->currentPoint; m_minPress = m_originalPress; m_maxPress = m_originalPress; }
void CreatePointCommandObject::createPoint(std::vector<CurveSegmentData> &segments) { // Create a point where we clicked // By creating segments that goes to the closest points if we're in the empty, // or by splitting a segment if we're in the middle of it. // 1. Check if we're clicking in a place where there is a segment CurveSegmentData* middle = nullptr; CurveSegmentData* exactBefore = nullptr; CurveSegmentData* exactAfter = nullptr; for(auto& segment : segments) { if(segment.start.x() < m_state->currentPoint.x() && m_state->currentPoint.x() < segment.end.x()) middle = &segment; if(segment.end.x() == m_state->currentPoint.x()) exactBefore = &segment; if(segment.start.x() == m_state->currentPoint.x()) exactAfter = &segment; if(middle && exactBefore && exactAfter) break; } // Handle creation on an exact other point if(exactBefore || exactAfter) { if(exactBefore) { exactBefore->end = m_state->currentPoint; } if(exactAfter) { exactAfter->start = m_state->currentPoint; } } else if(middle) { // TODO refactor with MovePointState (line ~330) // The segment goes in the first half of "middle" CurveSegmentData newSegment{ getSegmentId(segments), middle->start, m_state->currentPoint, middle->previous, middle->id, middle->type, middle->specificSegmentData }; auto prev_it = find(segments, middle->previous); // TODO we shouldn't have to test for this, only test if middle->previous != id{} if(prev_it != segments.end()) { (*prev_it).following = newSegment.id; } middle->start = m_state->currentPoint; middle->previous = newSegment.id; segments.push_back(newSegment); } else { // ~ Creating in the void ~ ... Spooky! // This is of *utmost* importance : if we don't do this, // when we push_back, the pointers get invalidated because the memory // has been moved, which causes a wealth of uncanny bugs and random memory errors // in other threads. So we reserve from up front the size we'll need. segments.reserve(segments.size() + 2); double seg_closest_from_left_x = 0; CurveSegmentData* seg_closest_from_left{}; double seg_closest_from_right_x = 1.; CurveSegmentData* seg_closest_from_right{}; for(CurveSegmentData& segment : segments) { auto seg_start_x = segment.start.x(); if(seg_start_x > m_state->currentPoint.x() && seg_start_x < seg_closest_from_right_x) { seg_closest_from_right_x = seg_start_x; seg_closest_from_right = &segment; } auto seg_end_x = segment.end.x(); if(seg_end_x < m_state->currentPoint.x() && seg_end_x > seg_closest_from_left_x) { seg_closest_from_left_x = seg_end_x; seg_closest_from_left = &segment; } } // Create a curve segment for the left // We do this little dance because of id generation. { CurveSegmentData newLeftSegment; newLeftSegment.id = getSegmentId(segments); segments.push_back(newLeftSegment); } CurveSegmentData& newLeftSegment = segments.back(); newLeftSegment.type = PowerCurveSegmentData::key(); newLeftSegment.specificSegmentData = QVariant::fromValue(PowerCurveSegmentData{0}); newLeftSegment.start = {seg_closest_from_left_x, 0.}; newLeftSegment.end = m_state->currentPoint; { CurveSegmentData newRightSegment; newRightSegment.id = getSegmentId(segments); segments.push_back(newRightSegment); } CurveSegmentData& newRightSegment = segments.back(); newRightSegment.type = PowerCurveSegmentData::key(); newRightSegment.specificSegmentData = QVariant::fromValue(PowerCurveSegmentData{0}); newRightSegment.start = m_state->currentPoint; newRightSegment.end = {seg_closest_from_right_x, 0.}; newLeftSegment.following = newRightSegment.id; newRightSegment.previous = newLeftSegment.id; if(seg_closest_from_left) { newLeftSegment.start = seg_closest_from_left->end; newLeftSegment.previous = seg_closest_from_left->id; seg_closest_from_left->following = newLeftSegment.id; } if(seg_closest_from_right) { newRightSegment.end = seg_closest_from_right->start; newRightSegment.following = seg_closest_from_right->id; seg_closest_from_right->previous = newRightSegment.id; } } }
void PenCommandObject::move() { // getSegmentId *has* to be prior to filterSegments for middleBegin / // middleEnd auto point_array_id = getSegmentId(m_startSegments); auto segts_tpl = filterSegments(); auto& middleBegin = std::get<0>(segts_tpl); auto& middleEnd = std::get<1>(segts_tpl); auto& segts = std::get<2>(segts_tpl); auto dat_base = m_segment.toSegmentData(); dat_base.id = point_array_id; dat_base.start = m_minPress; dat_base.end = m_maxPress; segts.reserve(segts.size() + 3); optional<std::size_t> middle_begin_p{}; optional<std::size_t> middle_end_p{}; if (middleBegin) { segts.push_back(*std::move(middleBegin)); middle_begin_p = segts.size() - 1; } if (middleEnd) { segts.push_back(*std::move(middleEnd)); middle_end_p = segts.size() - 1; } segts.push_back(dat_base); SegmentData& dat = segts.back(); if (middle_begin_p && middle_end_p && segts[*middle_begin_p].id == segts[*middle_end_p].id) { segts[*middle_end_p].id = getSegmentId(segts); for (auto& seg : segts) { if (seg.id == segts[*middle_end_p].following) { seg.previous = segts[*middle_end_p].id; break; } } } if (middle_begin_p) { SegmentData& seg = segts[*middle_begin_p]; seg.end = m_minPress; seg.following = dat.id; dat.previous = seg.id; } if (middle_end_p) { SegmentData& seg = segts[*middle_end_p]; seg.start = m_maxPress; seg.previous = dat.id; dat.following = seg.id; } submit(std::move(segts)); }
void PenCommandObject::release_n(seg_tuple&& segts_tpl) { m_segment.simplify(100); auto lin_segments = m_segment.toPowerSegments(); auto& segts = std::get<2>(segts_tpl); segts.reserve(segts.size() + lin_segments.size() + 3); optional<std::size_t> middle_begin_p{}; optional<std::size_t> middle_end_p{}; if (auto& middleBegin = std::get<0>(segts_tpl)) { segts.push_back(*std::move(middleBegin)); middle_begin_p = segts.size() - 1; } if (auto& middleEnd = std::get<1>(segts_tpl)) { segts.push_back(*std::move(middleEnd)); middle_end_p = segts.size() - 1; } const std::size_t first_inserted_lin = segts.size(); const std::size_t N = lin_segments.size(); { // Put the first one SegmentData& lin = lin_segments[0]; lin.id = getSegmentId(segts); if (lin_segments.size() > 1) lin_segments[1].previous = lin.id; segts.push_back(std::move(lin)); } if (N > 1) { // Main loop for (std::size_t i = 1; i < N - 1; i++) { SegmentData& lin = lin_segments[i]; lin.id = getSegmentId(segts); segts[first_inserted_lin + i - 1].following = lin.id; lin_segments[i + 1].previous = lin.id; segts.push_back(std::move(lin)); } { // Put the last one SegmentData& lin = lin_segments.back(); lin.id = getSegmentId(segts); segts.back().following = lin.id; segts.push_back(std::move(lin)); } } // Handle the case of the whole drawn curve being // contained in a single original segment if (middle_begin_p && middle_end_p && segts[*middle_begin_p].id == segts[*middle_end_p].id) { auto& mb = segts[*middle_begin_p]; auto& me = segts[*middle_end_p]; me.id = getSegmentId(segts); for (auto& seg : segts) { if (seg.id == mb.following) { seg.previous = me.id; break; } } } // Link the new segments auto& first_lin = segts[first_inserted_lin]; if (middle_begin_p) { SegmentData& seg = segts[*middle_begin_p]; seg.end = first_lin.start; seg.following = first_lin.id; first_lin.previous = seg.id; } auto& last_lin = segts.back(); if (middle_end_p) { SegmentData& seg = segts[*middle_end_p]; seg.start = last_lin.end; seg.previous = last_lin.id; last_lin.following = seg.id; } // checkCoherent(segts); submit(std::move(segts)); m_dispatcher.commit(); m_segment.reset(); }
void MovePointCommandObject::handleCrossOnOverlap(CurveSegmentMap& segments) { double current_x = m_state->currentPoint.x(); auto& segments_by_id = segments.get<Segments::Hashed>(); // In this case we merge at the origins of the point and we create if it is in a new place. // First, if we go to the right. if(current_x > m_originalPress.x()) { // Get the segment we're in, if there's any auto middleSegmentIt = std::find_if(segments_by_id.begin(), segments_by_id.end(), [&] (const SegmentData& segment) { // Going to the right return segment.start.x() > m_originalPress.x() && segment.start.x() < current_x && segment.end.x() > current_x; }); auto middle = middleSegmentIt != segments_by_id.end() ? &*middleSegmentIt : nullptr; // First part : removal of the segments around the initial click // If we have a following segment and the current position > end of the following segment if(m_state->clickedPointId.following) { auto foll_seg_it = segments_by_id.find(m_state->clickedPointId.following); auto& foll_seg = *foll_seg_it; if(current_x > foll_seg.end.x()) { // If there was also a previous segment, it now goes to the end of the // presently removed segment. if(m_state->clickedPointId.previous) { auto prev_seg_it = segments_by_id.find(m_state->clickedPointId.previous); segments_by_id.modify(prev_seg_it, [&] (auto& seg) { seg.end = foll_seg.end; seg.following = foll_seg.following; }); } else { // Link to the previous segment, or to the zero, maybe ? } // If the one about to be deleted also had a following, we set its previous // to the previous of the clicked point if(foll_seg.following) { auto foll_foll_seg_it = segments_by_id.find(foll_seg.following); segments_by_id.modify(foll_foll_seg_it, [&] (auto& seg) { seg.previous = m_state->clickedPointId.previous; }); } segments_by_id.erase(foll_seg_it); } else { // We have not crossed a point setCurrentPoint(segments); } } else { // If we have crossed a point (after some "emptiness") bool crossed = false; for(const auto& segment : segments) { auto seg_start_x = segment.start.x(); if(seg_start_x < current_x && seg_start_x > m_originalPress.x()) { crossed = true; break; } } if(crossed) { // We remove the previous of the clicked point auto prev_seg_it = segments_by_id.find(m_state->clickedPointId.previous); auto& prev_seg = *prev_seg_it; if(prev_seg.previous) { // We set its following to null. auto prev_prev_seg_it = segments_by_id.find(prev_seg.previous); segments_by_id.modify(prev_prev_seg_it, [&] (auto& seg) { seg.following = Id<SegmentModel>{}; }); } segments_by_id.erase(prev_seg_it); } else { // We have not crossed a point setCurrentPoint(segments); } } // Second part : creation of a new segment where the cursor actually is if(middle) { // We insert a new element after the leftmost point from the current point. // Since we are in a segment we split it and create another with a new id. SegmentData newSegment{ getSegmentId(segments_by_id), middle->start, m_state->currentPoint, middle->previous, middle->id, middle->type, middle->specificSegmentData }; auto prev_it = segments_by_id.find(middle->previous); // TODO we shouldn't have to test for this, only test if middle->previous != id{} if(prev_it != segments_by_id.end()) { segments_by_id.modify(prev_it, [&] (auto& seg) { seg.following = newSegment.id; }); } segments_by_id.modify(middleSegmentIt, [&] (auto& seg) { seg.start = m_state->currentPoint; seg.previous = newSegment.id; }); segments.insert(newSegment); } else { /* // We're on the empty; we make a new linear segment from the end of the last point // or zero if there is none ? this can't happen unless we select a point on zero. double seg_closest_from_left_x = 0; CurveSegmentModel* seg_closest_from_left{}; for(CurveSegmentModel* segment : segments) { auto seg_end_x = segment.end.x(); if(seg_end_x < current_x && seg_end_x > seg_closest_from_left_x) { seg_closest_from_left_x = seg_end_x; seg_closest_from_left = segment; } } auto newSegment = new LinearCurveSegmentModel{getStrongId(segments), nullptr}; newSegment->setEnd(m_state->currentPoint); if(seg_closest_from_left) { newSegment->setStart(seg_closest_from_left->end()); newSegment->setPrevious(seg_closest_from_left->id()); } segments.append(newSegment); */ } } else if(current_x < m_originalPress.x()) { // Get the segment we're in, if there's any auto middleSegmentIt = std::find_if(segments_by_id.begin(), segments_by_id.end(), [&] (const auto& segment) { // Going to the left return segment.end.x() < m_originalPress.x() && segment.start.x() < current_x && segment.end.x() > current_x; }); auto middle = middleSegmentIt != segments_by_id.end() ? &*middleSegmentIt : nullptr; // First part : removal of the segments around the initial click // If we have a following segment and the current position > end of the following segment if(m_state->clickedPointId.previous) { auto prev_seg_it = segments_by_id.find(m_state->clickedPointId.previous); auto& prev_seg = *prev_seg_it; if(current_x < prev_seg.start.x()) { // If there was also a following segment to the click, it now goes to the start of the // presently removed segment. if(m_state->clickedPointId.following) { auto foll_seg_it = segments_by_id.find(m_state->clickedPointId.following); segments_by_id.modify(foll_seg_it, [&] (auto& seg) { seg.start = prev_seg.start; seg.previous = prev_seg.previous; }); } else { // Link to the following segment, or to the zero, maybe ? } // If the one about to be deleted also had a previous, we set its following // to the following of the clicked point if(prev_seg.previous) { auto prev_prev_seg_it = segments_by_id.find(prev_seg.previous); segments_by_id.modify(prev_prev_seg_it, [&] (auto& seg) { seg.following = m_state->clickedPointId.following; }); } segments_by_id.erase(prev_seg_it); } else { // We have not crossed a point setCurrentPoint(segments); } } else { // If we have crossed a point (after some "emptiness") bool crossed = false; for(const auto& segment : segments) { auto seg_end_x = segment.end.x(); if(seg_end_x > current_x && seg_end_x < m_originalPress.x()) { crossed = true; break; } } if(crossed) { // We remove the following of the clicked point auto foll_seg_it = segments_by_id.find(m_state->clickedPointId.following); auto& foll_seg = *foll_seg_it; if(foll_seg.following) { // We set its following to null. auto foll_foll_seg_it = segments_by_id.find(foll_seg.following); segments_by_id.modify(foll_foll_seg_it, [&] (auto& seg) { seg.previous = Id<SegmentModel>{}; }); } segments_by_id.erase(foll_seg_it); } else { // We have not crossed a point setCurrentPoint(segments); } } // Second part : creation of a new segment where the cursor actually is if(middle) { SegmentData newSegment{ getSegmentId(segments_by_id), m_state->currentPoint, middle->end, middle->id, middle->following, middle->type, middle->specificSegmentData }; auto foll_it = segments_by_id.find(middle->following); // TODO we shouldn't have to test for this, only test if middle->previous != id{} if(foll_it != segments_by_id.end()) { segments_by_id.modify(foll_it, [&] (auto& seg) { seg.previous = newSegment.id; }); } segments_by_id.modify(middleSegmentIt, [&] (auto& seg) { seg.end = m_state->currentPoint; seg.following = newSegment.id; }); segments_by_id.insert(newSegment); } else { // TODO (see the commented block on the symmetric part) } } }