Beispiel #1
0
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;
        }

    }
}
Beispiel #3
0
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));
}
Beispiel #4
0
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)
        }
    }
}