表格线识别通用库文档
载入中...
搜索中...
未找到
line.hpp
浏览该文件的文档.
1/*
2 * @Description: 线类 头文件及其实现
3 * @Version:
4 * @Autor: dreamy-xay
5 * @Date: 2023-12-01 15:02:12
6 * @LastEditors: dreamy-xay
7 * @LastEditTime: 2024-05-20
8 */
9
10#ifndef COMMON_LINE_HPP
11#define COMMON_LINE_HPP
12
13#include <iostream>
14#include <opencv2/core.hpp>
15
17#include "common/base/point.hpp"
18#include "common/enum.h"
19#include "common/macro.h"
20#include "common/type.h"
21
22namespace cm {
23
31class Line {
32 public:
53 struct GEEquation {
55 double a;
57 double b;
59 double c;
60
62 GEEquation() = default;
64 GEEquation(double a, double b, double c) : a(a), b(b), c(c) {}
66 GEEquation(const GEEquation& ge) = default;
67
68 double GetX(double y) const;
69 double GetY(double x) const;
70 };
71
96 struct SIEquation {
98 double slope;
100 double intercept;
103
105 SIEquation() = default;
107 SIEquation(double slope, double intercept, LineType line_type) : slope(slope), intercept(intercept), line_type((line_type != HLINE && line_type != VLINE) ? (throw Exception("The \"line type\" only supports \"cm::HLINE\" or \"cm::VLINE\" types!")) : line_type) {}
109 SIEquation(const SIEquation& ge) = default;
110
111 double GetX(double y) const;
112 double GetY(double x) const;
113 };
114
132 struct Hash {
133 size_t operator()(const Line& line) const;
134 };
135
160 private:
161 bool is_rough;
162
163 public:
164 explicit CompareByLength(bool is_rough = false);
165 bool operator()(const Line& line1, const Line& line2) const;
166 };
167
191 private:
192 int min_distance;
193 bool (*compare)(const Line&, const Line&, const int&);
194
195 public:
196 explicit CompareByCoordinate(LineType type, int min_distance = 10, bool use_pt1 = true);
197 bool operator()(const Line& line1, const Line& line2) const;
198 };
199
204
205 Line();
206 Line(int pt1_x, int pt1_y, int pt2_x, int pt2_y);
207 Line(const std::initializer_list<int>& initial_list);
208 Line(const Point2i& pt1, const Point2i& pt2);
209 Line(const cv::Vec4i& line);
210 Line(const std::vector<int>& list);
211 Line(const Line& line);
212 ~Line();
213
214 operator cv::Vec4i() const;
215 operator std::vector<int>() const;
216
217 friend bool operator==(const Line& line1, const Line& line2);
218 friend bool operator!=(const Line& line1, const Line& line2);
219 friend std::ostream& operator<<(std::ostream& out, const Line& line);
220
221 int& operator[](size_t index);
222 const int& operator[](size_t index) const;
223
224 LineType Type() const;
225 double Length(bool is_rough = false) const;
228
229 template <typename T>
230 double DistanceTo(const Point<T>& pt) const;
231 double DistanceTo(const Line& line, LineDistanceType type) const;
232 Line& Translate(int dx, int dy);
233 Line& Scale(double factor, bool round = true);
234 template <typename T>
235 Line& Rotate(double angle, const Point<T>& center = {0, 0});
236 Line& TidyLine();
237
238 Point2d Intersect(const Line& line) const;
239 Point2d Intersect(const GEEquation& ge) const;
240 Point2d Intersect(const SIEquation& si) const;
241 Point2d Midpoint() const;
242 double X(Statistic type) const;
243 double Y(Statistic type) const;
244 Line& LimitX(const Interval& interval);
245 Line& LimitY(const Interval& interval);
246
247 bool InInterval(const Interval& x_interval, const Interval& y_interval) const;
248
249 bool IsIntersect(const Line& line, IntersectType type = INTERSECT_XY, bool is_include_boundaries = false) const;
250 bool IsPoint() const;
251 Line SimpleConnect(const Line& line) const;
252 bool IsAligned(const Line& line, Position align_direction, int threshold = 5) const;
253 bool IsNear(const Line& line, double threshold = 5.0, Statistic type = MAXIMUM) const;
254};
255
269inline double Line::GEEquation::GetX(double y) const {
270 Cm_Assert(a != 0, "The x-coordinate of the horizontal line can be any value");
271
272 return -(b * y + c) / a;
273}
274
288inline double Line::GEEquation::GetY(double x) const {
289 Cm_Assert(b != 0, "The y-coordinate of the vertical line can be any value");
290
291 return -(a * x + c) / b;
292}
293
307inline double Line::SIEquation::GetX(double y) const {
308 switch (line_type) {
309 case HLINE: // 横线:y = kx + b
310 Cm_Assert(slope != 0, "The x-coordinate of the horizontal line can be any value");
311
312 return (y - intercept) / slope;
313 case VLINE: // 竖线:x = ky + b
314 return slope * y + intercept;
315 default:
316 throw Exception("The \"line type\" only supports \"cm::HLINE\" or \"cm::VLINE\" types!");
317 }
318}
319
333inline double Line::SIEquation::GetY(double x) const {
334 switch (line_type) {
335 case HLINE: // 横线:y = kx + b
336 Cm_Assert(slope != 0, "The x-coordinate of the horizontal line can be any value");
337
338 return slope * x + intercept;
339 case VLINE: // 竖线:x = ky + b
340 Cm_Assert(slope != 0, "The y-coordinate of the vertical line can be any value");
341
342 return (x - intercept) / slope;
343 default:
344 throw Exception("The \"line type\" only supports \"cm::HLINE\" or \"cm::VLINE\" types!");
345 }
346}
347
359inline size_t Line::Hash::operator()(const Line& line) const {
360 return std::hash<int>()(line.pt1.x) ^ std::hash<int>()(line.pt1.y) ^ std::hash<int>()(line.pt2.x) ^ std::hash<int>()(line.pt2.y);
361}
362
373 this->is_rough = is_rough;
374}
375
390inline bool Line::CompareByLength::operator()(const Line& line1, const Line& line2) const {
391 return line1.Length(is_rough) < line2.Length(is_rough);
392}
393
407inline Line::CompareByCoordinate::CompareByCoordinate(LineType type, int min_distance, bool use_pt1) {
408 this->min_distance = min_distance;
409
410 switch (type) {
411 case HLINE:
412 if (use_pt1)
413 compare = [](const Line& line1, const Line& line2, const int& min_distance) {
414 if (std::abs((line1.pt1.y + line1.pt2.y) - (line2.pt1.y + line2.pt2.y)) / 2.0 <= min_distance)
415 return line1.pt1.x < line2.pt1.x;
416 return line1.pt1.y < line2.pt1.y;
417 };
418 else
419 compare = [](const Line& line1, const Line& line2, const int& min_distance) {
420 if (std::abs((line1.pt1.y + line1.pt2.y) - (line2.pt1.y + line2.pt2.y)) / 2.0 <= min_distance)
421 return line1.pt2.x < line2.pt2.x;
422 return line1.pt2.y < line2.pt2.y;
423 };
424 break;
425 case VLINE:
426 if (use_pt1)
427 compare = [](const Line& line1, const Line& line2, const int& min_distance) {
428 if (std::abs((line1.pt1.x + line1.pt2.x) - (line2.pt1.x + line2.pt2.x)) / 2.0 <= min_distance)
429 return line1.pt1.y < line2.pt1.y;
430 return line1.pt1.x < line2.pt1.x;
431 };
432 else
433 compare = [](const Line& line1, const Line& line2, const int& min_distance) {
434 if (std::abs((line1.pt1.x + line1.pt2.x) - (line2.pt1.x + line2.pt2.x)) / 2.0 <= min_distance)
435 return line1.pt2.y < line2.pt2.y;
436 return line1.pt2.x < line2.pt2.x;
437 };
438 break;
439 default:
440 throw Exception("The \"line type\" only supports \"cm::HLINE\" or \"cm::VLINE\" types!");
441 }
442}
443
458inline bool Line::CompareByCoordinate::operator()(const Line& line1, const Line& line2) const {
459 return compare(line1, line2, min_distance);
460}
461
469inline Line::Line() : pt1{}, pt2{} {}
470
485inline Line::Line(int pt1_x, int pt1_y, int pt2_x, int pt2_y) : pt1(pt1_x, pt1_y), pt2(pt2_x, pt2_y) {
486 this->TidyLine(); // 整理线中第一和第二个点的坐标
487}
488
510inline Line::Line(const std::initializer_list<int>& initial_list) {
511 auto iter = initial_list.begin();
512
513 pt1.x = *(iter++);
514 pt1.y = *(iter++);
515 pt2.x = *(iter++);
516 pt2.y = *(iter++);
517
518 this->TidyLine(); // 整理线中第一和第二个点的坐标
519}
520
533inline Line::Line(const Point2i& pt1, const Point2i& pt2) : pt1(pt1), pt2(pt2) {
534 this->TidyLine(); // 整理线中第一和第二个点的坐标
535}
536
548inline Line::Line(const cv::Vec4i& line) : pt1(line[0], line[1]), pt2(line[2], line[3]) {
549 this->TidyLine(); // 整理线中第一和第二个点的坐标
550}
551
563inline Line::Line(const std::vector<int>& list) {
564 Cm_Assert(list.size() == 4, "list size must be 4!!!");
565
566 pt1.x = list[0];
567 pt1.y = list[1];
568 pt2.x = list[2];
569 pt2.y = list[3];
570
571 this->TidyLine(); // 整理线中第一和第二个点的坐标
572}
573
585inline Line::Line(const Line& line) : pt1(line.pt1), pt2(line.pt2) {
586 this->TidyLine(); // 整理线中第一和第二个点的坐标
587}
588
596inline Line::~Line() {}
597
607inline Line::operator cv::Vec4i() const {
608 return {pt1.x, pt1.y, pt2.x, pt2.y};
609}
610
620inline Line::operator std::vector<int>() const {
621 return {pt1.x, pt1.y, pt2.x, pt2.y};
622}
623
638inline bool operator==(const Line& line1, const Line& line2) {
639 return line1.pt1 == line2.pt1 && line1.pt2 == line2.pt2;
640}
641
656inline bool operator!=(const Line& line1, const Line& line2) {
657 return line1.pt1 != line2.pt1 || line1.pt2 != line2.pt2;
658}
659
672inline std::ostream& operator<<(std::ostream& out, const Line& line) {
673 out << "{ pt1: { x: " << line.pt1.x << ", y: " << line.pt1.y << " }, pt2: { x: " << line.pt2.x << ", y: " << line.pt2.y << " } }";
674
675 return out;
676}
677
691inline int& Line::operator[](size_t index) {
692 Cm_Assert(index < 4, "index must be less than 4!");
693
694 switch (index) {
695 case 0:
696 return pt1.x;
697 case 1:
698 return pt1.y;
699 case 2:
700 return pt2.x;
701 case 3:
702 return pt2.y;
703 default:
704 throw Exception("Index must be less than 4!");
705 }
706}
707
721inline const int& Line::operator[](size_t index) const {
722 Cm_Assert(index < 4, "index must be less than 4!");
723
724 switch (index) {
725 case 0:
726 return pt1.x;
727 case 1:
728 return pt1.y;
729 case 2:
730 return pt2.x;
731 case 3:
732 return pt2.y;
733 default:
734 throw Exception("Index must be less than 4!");
735 }
736}
737
749inline LineType Line::Type() const {
750 // 如果两点x坐标大于y坐标,则判定为横线,否则为竖线
751 if (std::abs(pt1.x - pt2.x) > std::abs(pt1.y - pt2.y))
752 return HLINE;
753 else
754 return VLINE;
755}
756
778inline double Line::Length(bool is_rough) const {
779 if (is_rough) // 线的粗糙长度
780 return std::max(std::abs(pt2.x - pt1.x), std::abs(pt2.y - pt1.y));
781 else // 线的精准长度,算两点距离
782 return pt1.DistanceTo(pt2);
783}
784
804 Cm_Assert(!this->IsPoint(), "the coordinates of the two points of the line segment are not the same!");
805
806 /*
807 point1: ax1 + by1 + c = 0 @1
808 point2: ax2 + by2 + c = 0 @2
809 其中 a b c 是线方程的系数,@1表示公式1,@2表示公式2
810
811 @1 - @2 => a(x1 - x2) + b(y1 - y2) = 0 => a / b = (y2 - y1) / (x1 - x2)
812
813 则有如下公式
814 a = y2 - y1
815 b = x1 - x2
816 c = x2y1 - x1y2
817 */
818
819 return {static_cast<double>(pt2.y - pt1.y), static_cast<double>(pt1.x - pt2.x), pt2.Cross(pt1)};
820}
821
841 Cm_Assert(!this->IsPoint(), "the coordinates of the two points of the line segment are not the same!");
842
843 if (this->Type() == HLINE) { // 横线
844 // (斜截式:y = kx + b => k = (y2 - y1) / (x2 - x1) => b = y1 - kx1)
845 double slope = static_cast<double>(pt2.y - pt1.y) / (pt2.x - pt1.x);
846
847 return {slope, pt1.y - slope * pt1.x, HLINE};
848 } else { // 竖线
849 // (斜截式:x = ky + b => k = (x2 - x1) / (y2 - y1) => b = x1 - ky1)
850 double slope = static_cast<double>(pt2.x - pt1.x) / (pt2.y - pt1.y);
851
852 return {slope, pt1.x - slope * pt1.y, VLINE};
853 }
854}
855
869template <typename T>
870inline double Line::DistanceTo(const Point<T>& pt) const {
871 /*
872 对于线的一般方程,有系数a,b,c
873
874 distance = |ax + by + c| / sqrt(a^2 + b^2)
875
876 其中 x1 y1 x2 y2 是线的两点坐标,x y 是需要计算的点的坐标
877 */
878
879 auto ge = std::move(this->GECoefficients()); // 获取横线一般方程的系数
880
881 return std::abs(ge.a * pt.x + ge.b * pt.y + ge.c) / std::sqrt(ge.a * ge.a + ge.b * ge.b);
882}
883
898inline double Line::DistanceTo(const Line& line, LineDistanceType type) const {
899 switch (type) {
900 case LDIS_HORIZONTAL: // 两竖线之间的水平距离
901 return std::abs((pt1.x + pt2.x) - (line.pt1.x + line.pt2.x)) / 2.0;
902 case LDIS_VERTICAL: // 两横线之间的垂直距离
903 return std::abs((pt1.y + pt2.y) - (line.pt1.y + line.pt2.y)) / 2.0;
904 case LDIS_HLINE_H: // 横线与横线之间的水平距离(注:要求已经 TidyLine 了)
905 return std::max(pt1.x, line.pt1.x) - std::min(pt2.x, line.pt2.x);
906 case LDIS_VLINE_V: // 竖线与竖线之间的垂直距离(注:要求已经 TidyLine 了)
907 return std::max(pt1.y, line.pt1.y) - std::min(pt2.y, line.pt2.y);
908 case LDIS_MAX_X: // 两线之间的最大x坐标距离
909 return std::max(std::max(pt1.x, pt2.x) - std::min(line.pt1.x, line.pt2.x), std::max(line.pt1.x, line.pt2.x) - std::min(pt1.x, pt2.x));
910 case LDIS_MAX_Y: // 两线之间的最大y坐标距离
911 return std::max(std::max(pt1.y, pt2.y) - std::min(line.pt1.y, line.pt2.y), std::max(line.pt1.y, line.pt2.y) - std::min(pt1.y, pt2.y));
912 case LDIS_MIN_X: // 两线之间的最小x坐标距离
913 return std::min(std::abs(std::min(pt1.x, pt2.x) - std::max(line.pt1.x, line.pt2.x)), std::abs(std::min(line.pt1.x, line.pt2.x) - std::max(pt1.x, pt2.x)));
914 case LDIS_MIN_Y: // 两线之间的最小y坐标距离
915 return std::min(std::abs(std::min(pt1.y, pt2.y) - std::max(line.pt1.y, line.pt2.y)), std::abs(std::min(line.pt1.y, line.pt2.y) - std::max(pt1.y, pt2.y)));
916 default:
917 throw Exception("The type(LineDistanceType) of param \"type\" is incorrect!");
918 }
919}
920
933inline Line& Line::Translate(int dx, int dy) {
934 pt1.Translate(dx, dy); // 线的平移本质上是两个点的平移
935 pt2.Translate(dx, dy);
936
937 return *this;
938}
939
952inline Line& Line::Scale(double factor, bool round) {
953 pt1.Scale(factor, round); // 线的缩放本质上是两个点的缩放
954 pt2.Scale(factor, round);
955
956 return *this;
957}
958
973template <typename T>
974inline Line& Line::Rotate(double angle, const Point<T>& center) {
975 pt1.Rotate(angle, center); // 线的旋转本质上是两个点的旋转
976 pt2.Rotate(angle, center);
977
978 return *this;
979}
980
991 if (this->Type() == HLINE) {
992 if (pt1.x > pt2.x)
993 std::swap(pt1, pt2); // 横线:pt1 在 pt2 的左侧
994 } else {
995 if (pt1.y > pt2.y)
996 std::swap(pt1, pt2); // 竖线:pt1 在 pt2 的上侧
997 }
998
999 return *this;
1000}
1001
1017inline Point2d Line::Intersect(const Line& line) const {
1018 auto ge = std::move(line.GECoefficients()); // 获取当前线一般方程的系数
1019
1020 return std::move(this->Intersect(ge)); // 使用一般方程求交点
1021}
1022
1039 /*
1040 对于线的一般方程,有系数a,b,c
1041
1042 line1: a1x + b1y + c1 = 0 @1
1043 line2: a2x + b2y + c2 = 0 @2
1044 其中 a1 a2 b1 b2 c1 c2 是两条线方程的系数,@1表示公式1,@2表示公式2
1045
1046 @1 * b2 - @2 * b1 => (a1b2x + b1b2y + b2c1) - (a2b1x + b1b2y + b1c2) = 0 => (a1b2 - a2b1)x + (b2c1 - b1c2) = 0
1047
1048 则有如下公式
1049 x = (b1c2 - b2c1) / (a1b2 - a2b1)
1050 y = (a2c1 - a1c2) / (a1b2 - a2b1)
1051
1052 该方程,只要两条线不平行即有解,即 a1b2 - a2b1 不为 0
1053 */
1054
1055 auto this_ge = std::move(this->GECoefficients()); // 获取当前横线一般方程的系数,当前横线作为线1
1056
1057 double divisor = this_ge.a * ge.b - ge.a * this_ge.b; // 公式中的被除数
1058
1059 Cm_Assert(divisor != 0, "two parallel lines do not intersect!"); // 两线平行触发断言
1060
1061 return {(this_ge.b * ge.c - ge.b * this_ge.c) / divisor, (ge.a * this_ge.c - this_ge.a * ge.c) / divisor};
1062}
1063
1082 Cm_Assert(si.line_type != this->Type(), "only different types of lines can calculate intersections"); // 类型相同触发断言
1083
1084 auto this_si = this->SICoefficients(); // 获取当前横线斜截式的系数,当前横线作为线1
1085 if (this_si.line_type == VLINE) {
1086 /*
1087 两个直线方程求交点 (k1=this_si.slope, b1=this_si.intercept, k2=si.slope, b2=si.intercept)
1088 x = k1y + b1
1089 y = k2x + b2
1090
1091 将 y 代入 => x = k1(k2x + b2) + b1 => x = k1k2x + k1b2 + b1 => (1 - k1k2)x = k1b2 + b1
1092
1093 则有如下公式
1094 x = (k1b2 + b1) / (1 - k1k2)
1095 y = k2 * x + b2
1096 */
1097
1098 double x = (this_si.slope * si.intercept + this_si.intercept) / (1.0 - this_si.slope * si.slope); // x
1099 double y = si.slope * x + si.intercept; // y
1100
1101 return {x, y};
1102 } else {
1103 /*
1104 两个直线方程求交点 (k1=this_si.slope, b1=this_si.intercept, k2=si.slope, b2=si.intercept)
1105 y = k1x + b1
1106 x = k2y + b2
1107
1108 将 y 代入 => x = k2(k1x + b1) + b2 => x = k2k1x + k2b1 + b2 => (1 - k2k1)x = k2b1 + b2
1109
1110 则有如下公式
1111 x = (k2b1 + b2) / (1 - k2k1)
1112 y = k1x + b1
1113 */
1114
1115 double x = (si.slope * this_si.intercept + si.intercept) / (1.0 - si.slope * this_si.slope); // x
1116 double y = this_si.slope * x + this_si.intercept; // y
1117
1118 return {x, y};
1119 }
1120}
1121
1131inline Point2d Line::Midpoint() const {
1132 return {(pt1.x + pt2.x) / 2.0, (pt1.y + pt2.y) / 2.0};
1133}
1134
1146inline double Line::X(Statistic type) const {
1147 switch (type) {
1148 case AVERAGE:
1149 return (pt1.x + pt2.x) / 2.0; // 线的平均x坐标
1150 case MAXIMUM:
1151 return std::max(pt1.x, pt2.x); // 线的最大x坐标
1152 case MINIMUM:
1153 return std::min(pt1.x, pt2.x); // 线的最小x坐标
1154 default:
1155 throw Exception("The type(Statistic) of param \"type\" is incorrect!");
1156 }
1157}
1158
1170inline double Line::Y(Statistic type) const {
1171 switch (type) {
1172 case AVERAGE:
1173 return (pt1.y + pt2.y) / 2.0; // 线的平均y坐标
1174 case MAXIMUM:
1175 return std::max(pt1.y, pt2.y); // 线的最大y坐标
1176 case MINIMUM:
1177 return std::min(pt1.y, pt2.y); // 线的最小y坐标
1178 default:
1179 throw Exception("The type(Statistic) of param \"type\" is incorrect!");
1180 }
1181}
1182
1194inline Line& Line::LimitX(const Interval& interval) {
1195 pt1.LimitX(interval.start, interval.end - 1);
1196 pt2.LimitX(interval.start, interval.end - 1);
1197
1198 return *this;
1199}
1200
1212inline Line& Line::LimitY(const Interval& interval) {
1213 pt1.LimitY(interval.start, interval.end - 1);
1214 pt2.LimitY(interval.start, interval.end - 1);
1215
1216 return *this;
1217}
1218
1235inline bool Line::InInterval(const Interval& x_interval, const Interval& y_interval) const {
1236 return (pt1.x >= x_interval.start && pt1.x < x_interval.end) &&
1237 (pt2.x >= x_interval.start && pt2.x < x_interval.end) &&
1238 (pt1.y >= y_interval.start && pt1.y < y_interval.end) &&
1239 (pt2.y >= y_interval.start && pt2.y < y_interval.end);
1240}
1241
1257inline bool Line::IsIntersect(const Line& line, IntersectType type, bool is_include_boundaries) const {
1258 if (is_include_boundaries)
1259 switch (type) {
1260 case INTERSECT_X: // 水平方向上相交
1261 return std::max(pt1.x, pt2.x) >= std::min(line.pt1.x, line.pt2.x) && std::max(line.pt1.x, line.pt2.x) >= std::min(pt1.x, pt2.x);
1262 case INTERSECT_Y: // 垂直方向上相交
1263 return std::max(pt1.y, pt2.y) >= std::min(line.pt1.y, line.pt2.y) && std::max(line.pt1.y, line.pt2.y) >= std::min(pt1.y, pt2.y);
1264 case INTERSECT_XY: { // 两个方向上都相交
1265 /*
1266 线段1:点a和点b构成
1267 线段2:点c和点d构成
1268
1269 a d
1270 \ /
1271 \ /
1272 \ /
1273 /\
1274 / \
1275 / \
1276 c b
1277
1278 跨立实验:(ad x cd) * (bd x cd) <= 0
1279 其中 x 表示叉乘,* 表示两数字乘积,ad表示ad向量(d - a),cd同理
1280 < 0 表示两线段相交(不含端点)
1281 = 0 表示两线段相交(仅端点相交)
1282
1283 */
1284 auto& a = pt1;
1285 auto& b = pt2;
1286 auto& c = line.pt1;
1287 auto& d = line.pt2;
1288
1289 // 执行快速排斥实验(即在x和y坐标方向上都不相交,即对 INTERSECT_X 和 INTERSECT_Y 情况取非)
1290 if (std::max(a.x, b.x) < std::min(c.x, d.x) || std::max(c.x, d.x) < std::min(a.x, b.x) || std::max(a.y, b.y) < std::min(c.y, d.y) || std::max(c.y, d.y) < std::min(a.y, b.y))
1291 return false;
1292
1293 auto cd_vec = d - c;
1294
1295 // 执行跨立实验
1296 return (d - a).Cross(cd_vec) * (d - b).Cross(cd_vec) <= 0;
1297 }
1298 default:
1299 throw Exception("The type(IntersectType) of param \"type\" is incorrect!");
1300 }
1301 else
1302 switch (type) {
1303 case INTERSECT_X: // 水平方向上相交
1304 return std::max(pt1.x, pt2.x) > std::min(line.pt1.x, line.pt2.x) && std::max(line.pt1.x, line.pt2.x) > std::min(pt1.x, pt2.x);
1305 case INTERSECT_Y: // 垂直方向上相交
1306 return std::max(pt1.y, pt2.y) > std::min(line.pt1.y, line.pt2.y) && std::max(line.pt1.y, line.pt2.y) > std::min(pt1.y, pt2.y);
1307 case INTERSECT_XY: { // 两个方向上都相交
1308 /*
1309 线段1:点a和点b构成
1310 线段2:点c和点d构成
1311
1312 a d
1313 \ /
1314 \ /
1315 \ /
1316 /\
1317 / \
1318 / \
1319 c b
1320
1321 跨立实验:(ad x cd) * (bd x cd) <= 0
1322 其中 x 表示叉乘,* 表示两数字乘积,ad表示ad向量(d - a),cd同理
1323 < 0 表示两线段相交(不含端点)
1324 = 0 表示两线段相交(仅端点相交)
1325
1326 */
1327 auto& a = pt1;
1328 auto& b = pt2;
1329 auto& c = line.pt1;
1330 auto& d = line.pt2;
1331
1332 // 执行快速排斥实验(即在x和y坐标方向上都不相交,即对 INTERSECT_X 和 INTERSECT_Y 情况取非)
1333 if (std::max(a.x, b.x) < std::min(c.x, d.x) || std::max(c.x, d.x) < std::min(a.x, b.x) || std::max(a.y, b.y) < std::min(c.y, d.y) || std::max(c.y, d.y) < std::min(a.y, b.y))
1334 return false;
1335
1336 auto cd_vec = d - c;
1337
1338 // 执行跨立实验
1339 return (d - a).Cross(cd_vec) * (d - b).Cross(cd_vec) < 0;
1340 }
1341 default:
1342 throw Exception("The type(IntersectType) of param \"type\" is incorrect!");
1343 }
1344}
1345
1357inline bool Line::IsPoint() const {
1358 return pt1.x == pt2.x && pt1.y == pt2.y; // 两点坐标相等即为点
1359}
1360
1382inline Line Line::SimpleConnect(const Line& line) const {
1383 Cm_Assert(this->Type() == line.Type(), "this function requires line type is same!");
1384
1385 Line new_line;
1386
1387 if (this->Type() == VLINE) {
1388 // 竖线
1389 // 设置新线的上端点(y坐标小的即为上端点)
1390 if (pt1.y < line.pt1.y) {
1391 new_line.pt1.x = pt1.x;
1392 new_line.pt1.y = pt1.y;
1393 } else {
1394 new_line.pt1.x = line.pt1.x;
1395 new_line.pt1.y = line.pt1.y;
1396 }
1397
1398 // 设置新线的下端点(y坐标大的即为下端点)
1399 if (pt2.y > line.pt2.y) {
1400 new_line.pt2.x = pt2.x;
1401 new_line.pt2.y = pt2.y;
1402 } else {
1403 new_line.pt2.x = line.pt2.x;
1404 new_line.pt2.y = line.pt2.y;
1405 }
1406 } else {
1407 // 横线
1408 // 设置新线的左端点(x坐标小的即为左端点)
1409 if (pt1.x < line.pt1.x) {
1410 new_line.pt1.x = pt1.x;
1411 new_line.pt1.y = pt1.y;
1412 } else {
1413 new_line.pt1.x = line.pt1.x;
1414 new_line.pt1.y = line.pt1.y;
1415 }
1416
1417 // 设置新线的右端点(x坐标大的即为右端点)
1418 if (pt2.x > line.pt2.x) {
1419 new_line.pt2.x = pt2.x;
1420 new_line.pt2.y = pt2.y;
1421 } else {
1422 new_line.pt2.x = line.pt2.x;
1423 new_line.pt2.y = line.pt2.y;
1424 }
1425 }
1426 return new_line;
1427}
1428
1446inline bool Line::IsAligned(const Line& line, Position align_direction, int threshold) const {
1447 Cm_Assert(this->Type() != line.Type(), "this function requires line type is different!");
1448
1449 Point2d pt = this->Intersect(line);
1450
1451 if (this->Type() == HLINE) {
1452 switch (align_direction) {
1453 case LEFT:
1454 // ╟
1455 return std::abs(pt.x - pt1.x) <= threshold;
1456 case RIGHT:
1457 // ╢
1458 return std::abs(pt.x - pt2.x) <= threshold;
1459 default:
1460 throw Exception("The horizontal line \"align direction\" only supports \"cm::LEFT\" or \"cm::RIGHT\" types!");
1461 }
1462 } else {
1463 switch (align_direction) {
1464 case TOP:
1465 // ╤
1466 return std::abs(pt.y - pt1.y) <= threshold;
1467 case BOTTOM:
1468 // ╧
1469 return std::abs(pt.y - pt2.y) <= threshold;
1470 default:
1471 throw Exception("The vertical line \"align direction\" only supports \"cm::TOP\" or \"cm::BOTTOM\" types!");
1472 }
1473 }
1474}
1475
1491inline bool Line::IsNear(const Line& line, double threshold, Statistic type) const {
1492 // 计算点到直线距离,如果统计(最大,最小,平均)距离都小于阈值,那么判断它们离得很近
1493 double dst1 = this->DistanceTo(line.pt1);
1494 double dst2 = this->DistanceTo(line.pt2);
1495 double dst3 = line.DistanceTo(pt1);
1496 double dst4 = line.DistanceTo(pt2);
1497
1498 switch (type) {
1499 case MAXIMUM:
1500 return std::max(dst1, std::max(dst2, std::max(dst3, dst4))) <= threshold;
1501 case MINIMUM:
1502 return std::min(dst1, std::min(dst2, std::min(dst3, dst4))) <= threshold;
1503 case AVERAGE:
1504 return (dst1 + dst2 + dst3 + dst4) <= threshold * 4;
1505 default:
1506 throw Exception("The type(Statistic) of param \"type\" is incorrect!");
1507 }
1508}
1509
1510} // namespace cm
1511
1512#endif
通用异常类
Definition type.h:39
区间类
Definition interval.hpp:29
int start
区间开始值(开区间)
Definition interval.hpp:32
int end
区间结束值(闭区间)
Definition interval.hpp:34
线类
Definition line.hpp:31
Line & Scale(double factor, bool round=true)
线的坐标缩放操作
Definition line.hpp:952
bool IsIntersect(const Line &line, IntersectType type=INTERSECT_XY, bool is_include_boundaries=false) const
判断两线是否相交
Definition line.hpp:1257
Point2d Midpoint() const
获取线段中点坐标
Definition line.hpp:1131
double DistanceTo(const Point< T > &pt) const
计算点到直线的距离
Definition line.hpp:870
LineType Type() const
获取线类型
Definition line.hpp:749
Point2i pt2
线的第二个点(默认:对于横线是右边的点,对于竖线是下面的点)
Definition line.hpp:203
Line & TidyLine()
整理线的端点顺序
Definition line.hpp:990
Line & LimitY(const Interval &interval)
限制线的 y 坐标在指定区间范围内
Definition line.hpp:1212
bool IsAligned(const Line &line, Position align_direction, int threshold=5) const
判断两根线是否对齐
Definition line.hpp:1446
bool IsNear(const Line &line, double threshold=5.0, Statistic type=MAXIMUM) const
判断两线是否接近
Definition line.hpp:1491
Line()
线类的默认构造函数
Definition line.hpp:469
Point2i pt1
线的第一个点(默认:对于横线是左边的点;对于竖线是上面的点)
Definition line.hpp:201
int & operator[](size_t index)
线类重载方括号运算符
Definition line.hpp:691
double Length(bool is_rough=false) const
获取线的长度
Definition line.hpp:778
GEEquation GECoefficients() const
计算线的一般方程系数
Definition line.hpp:803
friend bool operator!=(const Line &line1, const Line &line2)
线类重载不等于号
Definition line.hpp:656
Line & LimitX(const Interval &interval)
限制线的 x 坐标在指定区间范围内
Definition line.hpp:1194
~Line()
线类的析构函数
Definition line.hpp:596
friend std::ostream & operator<<(std::ostream &out, const Line &line)
线类重载输出流运算符
Definition line.hpp:672
bool IsPoint() const
判断线是否是一个点
Definition line.hpp:1357
Line SimpleConnect(const Line &line) const
简单合并线
Definition line.hpp:1382
double X(Statistic type) const
获取线的 X 坐标
Definition line.hpp:1146
double Y(Statistic type) const
获取线的 Y 坐标
Definition line.hpp:1170
Point2d Intersect(const Line &line) const
计算两线的交点
Definition line.hpp:1017
friend bool operator==(const Line &line1, const Line &line2)
线类重载等于号
Definition line.hpp:638
Line & Translate(int dx, int dy)
线的坐标平移操作
Definition line.hpp:933
bool InInterval(const Interval &x_interval, const Interval &y_interval) const
判断线是否在区间内
Definition line.hpp:1235
Line & Rotate(double angle, const Point< T > &center={0, 0})
线的坐标旋转操作
Definition line.hpp:974
SIEquation SICoefficients() const
计算线的斜截式系数
Definition line.hpp:840
T y
点的 y 坐标
Definition point.hpp:57
Point< T > & Scale(double factor, bool round=false)
点的坐标缩放操作
Definition point.hpp:447
Point< T > & LimitX(U start, U end)
限制点的 x 坐标在区间 [min_x, max_x] 范围内
Definition point.hpp:545
double Cross(const Point< U > &pt) const
计算叉积
Definition point.hpp:525
T x
点的 x 坐标
Definition point.hpp:55
double DistanceTo(const Point< U > &pt) const
计算点到点之间的距离
Definition point.hpp:410
Point< T > & Rotate(double angle, const Point< U > &center={0, 0})
点的坐标旋转操作
Definition point.hpp:475
Point< T > & Translate(T dx, T dy)
点的坐标平移操作
Definition point.hpp:427
Point< T > & LimitY(U start, U end)
限制点的 y 坐标在区间 [min_x, max_x] 范围内
Definition point.hpp:571
#define Cm_Assert(expr, message)
断言宏
Definition macro.h:109
bool operator!=(const Interval &interval1, const Interval &interval2)
区间类重载不等于号
Definition interval.hpp:232
Statistic
统计方式枚举
Definition enum.h:70
@ AVERAGE
平均值
Definition enum.h:72
@ MINIMUM
最小值
Definition enum.h:76
@ MAXIMUM
最大值
Definition enum.h:74
LineDistanceType
线与线的间距类型枚举
Definition enum.h:32
@ LDIS_MAX_X
线与线之间的最大x坐标距离
Definition enum.h:46
@ LDIS_MIN_Y
线与线之间的最小y坐标距离
Definition enum.h:44
@ LDIS_MIN_X
线与线之间的最小x坐标距离
Definition enum.h:42
@ LDIS_HORIZONTAL
线与线之间的水平距离(通常是竖线与竖线之间的水平距离)
Definition enum.h:36
@ LDIS_MAX_Y
线与线之间的最大y坐标距离
Definition enum.h:48
@ LDIS_VERTICAL
线与线之间的垂直距离(通常是横线与横线之间的垂直距离)
Definition enum.h:34
@ LDIS_VLINE_V
竖线与竖线之间的垂直距离(可能为负数,为负数时表示两竖线在y坐标上相交)
Definition enum.h:40
@ LDIS_HLINE_H
横线与横线之间的水平距离(可能为负数,为负数时表示两横线在x坐标上相交)
Definition enum.h:38
Position
位置枚举
Definition enum.h:84
@ RIGHT
Definition enum.h:90
@ LEFT
Definition enum.h:86
@ TOP
Definition enum.h:88
@ BOTTOM
Definition enum.h:92
LineType
线类型枚举
Definition enum.h:20
@ HLINE
横线 (horizontal line)
Definition enum.h:24
@ VLINE
竖线 (vertical line)
Definition enum.h:26
IntersectType
相交类型枚举
Definition enum.h:56
@ INTERSECT_Y
y坐标上相交
Definition enum.h:60
@ INTERSECT_X
x坐标上相交
Definition enum.h:58
@ INTERSECT_XY
xy坐标上相交
Definition enum.h:62
bool operator==(const Interval &interval1, const Interval &interval2)
区间类重载等于号
Definition interval.hpp:214
std::ostream & operator<<(std::ostream &out, const Interval &interval)
区间类重载输出流运算符
Definition interval.hpp:328
线的比较仿函数
Definition line.hpp:190
CompareByCoordinate(LineType type, int min_distance=10, bool use_pt1=true)
线的比较仿函数的带参构造函数(通过线的坐标比较)
Definition line.hpp:407
bool operator()(const Line &line1, const Line &line2) const
线的比较仿函数重载括号运算符
Definition line.hpp:458
线的比较仿函数
Definition line.hpp:159
bool operator()(const Line &line1, const Line &line2) const
线的比较仿函数重载括号运算符
Definition line.hpp:390
CompareByLength(bool is_rough=false)
线的比较仿函数的带参构造函数(通过线的长度比较)
Definition line.hpp:372
线的一般方程
Definition line.hpp:53
GEEquation()=default
线的一般方程结构体的默认构造函数
GEEquation(double a, double b, double c)
线的一般方程结构体的带参构造函数
Definition line.hpp:64
double GetX(double y) const
计算 x 坐标
Definition line.hpp:269
GEEquation(const GEEquation &ge)=default
线的一般方程结构体的拷贝构造函数
double GetY(double x) const
计算 y 坐标
Definition line.hpp:288
double a
一般方程(ax + by + c = 0)的系数 a
Definition line.hpp:55
double b
一般方程(ax + by + c = 0)的系数 b
Definition line.hpp:57
double c
一般方程(ax + by + c = 0)的系数 c
Definition line.hpp:59
线的哈希仿函数
Definition line.hpp:132
size_t operator()(const Line &line) const
线的哈希仿函数重载括号运算符
Definition line.hpp:359
线的斜截式方程
Definition line.hpp:96
double GetY(double x) const
计算 y 坐标
Definition line.hpp:333
double intercept
斜截式方程(横线:y = kx + b,竖线:x = ky + b)的截距 b
Definition line.hpp:100
double slope
斜截式方程(横线:y = kx + b,竖线:x = ky + b)的斜率 k
Definition line.hpp:98
SIEquation()=default
线的斜截式方程结构体的默认构造函数
SIEquation(double slope, double intercept, LineType line_type)
线的斜截式方程结构体的带参构造函数
Definition line.hpp:107
SIEquation(const SIEquation &ge)=default
线的斜截式方程结构体的拷贝构造函数
LineType line_type
斜截式方程(横线:y = kx + b,竖线:x = ky + b)的线类型
Definition line.hpp:102
double GetX(double y) const
计算 x 坐标
Definition line.hpp:307