(Re)Fit a line to the stored points. Returns false if the line is degenerate. Althougth the TabVector code mostly doesn't care about the direction of lines, XAtY would give silly results for a horizontal line. The class is mostly aimed at use for vertical lines representing horizontal tab stops.
| 786 | // The class is mostly aimed at use for vertical lines representing |
| 787 | // horizontal tab stops. |
| 788 | bool TabVector::Fit(ICOORD vertical, bool force_parallel) { |
| 789 | needs_refit_ = false; |
| 790 | if (boxes_.empty()) { |
| 791 | // Don't refit something with no boxes, as that only happens |
| 792 | // in Evaluate, and we don't want to end up with a zero vector. |
| 793 | if (!force_parallel) |
| 794 | return false; |
| 795 | // If we are forcing parallel, then we just need to set the sort_key_. |
| 796 | ICOORD midpt = startpt_; |
| 797 | midpt += endpt_; |
| 798 | midpt /= 2; |
| 799 | sort_key_ = SortKey(vertical, midpt.x(), midpt.y()); |
| 800 | return startpt_.y() != endpt_.y(); |
| 801 | } |
| 802 | if (!force_parallel && !IsRagged()) { |
| 803 | // Use a fitted line as the vertical. |
| 804 | DetLineFit linepoints; |
| 805 | BLOBNBOX_C_IT it(&boxes_); |
| 806 | // Fit a line to all the boxes in the list. |
| 807 | for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { |
| 808 | BLOBNBOX* bbox = it.data(); |
| 809 | const TBOX& box = bbox->bounding_box(); |
| 810 | int x1 = IsRightTab() ? box.right() : box.left(); |
| 811 | ICOORD boxpt(x1, box.bottom()); |
| 812 | linepoints.Add(boxpt); |
| 813 | if (it.at_last()) { |
| 814 | ICOORD top_pt(x1, box.top()); |
| 815 | linepoints.Add(top_pt); |
| 816 | } |
| 817 | } |
| 818 | linepoints.Fit(&startpt_, &endpt_); |
| 819 | if (startpt_.y() != endpt_.y()) { |
| 820 | vertical = endpt_; |
| 821 | vertical -= startpt_; |
| 822 | } |
| 823 | } |
| 824 | int start_y = startpt_.y(); |
| 825 | int end_y = endpt_.y(); |
| 826 | sort_key_ = IsLeftTab() ? MAX_INT32 : -MAX_INT32; |
| 827 | BLOBNBOX_C_IT it(&boxes_); |
| 828 | // Choose a line parallel to the vertical such that all boxes are on the |
| 829 | // correct side of it. |
| 830 | mean_width_ = 0; |
| 831 | int width_count = 0; |
| 832 | for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { |
| 833 | BLOBNBOX* bbox = it.data(); |
| 834 | const TBOX& box = bbox->bounding_box(); |
| 835 | mean_width_ += box.width(); |
| 836 | ++width_count; |
| 837 | int x1 = IsRightTab() ? box.right() : box.left(); |
| 838 | // Test both the bottom and the top, as one will be more extreme, depending |
| 839 | // on the direction of skew. |
| 840 | int bottom_y = box.bottom(); |
| 841 | int top_y = box.top(); |
| 842 | int key = SortKey(vertical, x1, bottom_y); |
| 843 | if (IsLeftTab() == (key < sort_key_)) { |
| 844 | sort_key_ = key; |
| 845 | startpt_ = ICOORD(x1, bottom_y); |
no test coverage detected