• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pcb2gcode / pcb2gcode / 19811714133

24 Nov 2025 02:34PM UTC coverage: 59.007% (-15.0%) from 74.006%
19811714133

push

github

web-flow
Merge pull request #730 from mar0x/master

Enable windows build in CI

2199 of 4409 branches covered (49.88%)

Branch coverage included in aggregate %.

1902 of 2541 relevant lines covered (74.85%)

117318.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.26
/segmentize.cpp
1
#include <vector>
2
#include <map>
3

4
#include "geometry_int.hpp"
5
#include "geometry.hpp"
6
#include "bg_operators.hpp"
7
#include "merge_near_points.hpp"
8
#include <boost/polygon/isotropy.hpp>
9
#include <boost/polygon/segment_concept.hpp>
10
#include <boost/polygon/segment_utils.hpp>
11

12
namespace segmentize {
13

14
using std::vector;
15
using std::pair;
16
using std::make_pair;
17
using std::sort;
18
using std::unique;
19

20
// For use when we have to convert from float to long and back.
21
const double SCALE = 1000000.0;
22

23
// Returns the sign of the input as -1,0,1 for negative/zero/positive.
24
template <typename T>
25
int sgn(T val) {
26
  return (T(0) < val) - (val < T(0));
10✔
27
}
28

29
vector<pair<linestring_type_fp, bool>> unique(const vector<pair<linestring_type_fp, bool>>& lss) {
35✔
30
  std::set<pair<linestring_type_fp, bool>> ret;
31
  for (const auto& ls : lss) {
8,187✔
32
    // Should we add this to the output?  Is it unique?
33
    if (ls.second) {
8,152!
34
      // This is reversible, check if it exists in any form in ret.
35
      if (ret.count({{ls.first.front(), ls.first.back()}, true}) > 0 ||
16,304!
36
          ret.count({{ls.first.back(), ls.first.front()}, true}) > 0) {
32,596!
37
        // Found it so don't add it.
38
        continue;
4✔
39
      }
40
      // Erase any directional ones if they exist.
41
      ret.erase({{ls.first.front(), ls.first.back()}, false});
16,296!
42
      ret.erase({{ls.first.back(), ls.first.front()}, false});
16,296!
43
    } else {
44
      // Not reversible.
45
      if (ret.count({{ls.first.front(), ls.first.back()}, true}) ||
×
46
          ret.count({{ls.first.back(), ls.first.front()}, true}) ||
×
47
          ret.count({{ls.first.front(), ls.first.back()}, false})) {
×
48
        // Found it so don't add it.
49
        continue;
×
50
      }
51
    }
52
    ret.insert(ls);
8,148!
53
  }
54
  return {ret.cbegin(), ret.cend()};
35!
55
}
56

57
/* Given a multi_linestring, return a new multiline_string where there
58
 * are no segments that cross any other segments.  Nor are there any T
59
 * shapes where the end of a linestring butts up against the center of
60
 * another.
61
 *
62
 * For each segment there is a boolean that is true if the segment is
63
 * reversible.  Non-reversible segments are re-oriented if needed.
64
 */
65
static inline vector<pair<segment_type_p, bool>> segmentize(
78!
66
    const vector<segment_type_p>& all_segments,
67
    const vector<bool>& allow_reversals) {
68
  vector<pair<size_t, segment_type_p>> intersected_segment_pairs;
69
  boost::polygon::intersect_segments(intersected_segment_pairs, all_segments.cbegin(), all_segments.cend());
78!
70
  vector<pair<segment_type_p, bool>> intersected_segments;
71
  for (const auto& p : intersected_segment_pairs) {
20,896✔
72
    const auto index_in_input = p.first;
20,818✔
73
    const auto& allow_reversal = allow_reversals[index_in_input];
74
    intersected_segments.push_back(make_pair(p.second, allow_reversal));
20,818✔
75
    if (!allow_reversal) {
20,818✔
76
      // Reverse segments that are now pointing in the wrong direction.
77
      const auto& input_segment = all_segments[index_in_input];
78
      auto& new_segment = intersected_segments.back().first;
79
      auto input_delta_x = input_segment.high().x() - input_segment.low().x();
7✔
80
      auto input_delta_y = input_segment.high().y() - input_segment.low().y();
7✔
81
      auto new_delta_x = new_segment.high().x() - new_segment.low().x();
7✔
82
      auto new_delta_y = new_segment.high().y() - new_segment.low().y();
7✔
83
      if (sgn(input_delta_x) != sgn(new_delta_x) ||
7!
84
          sgn(input_delta_y) != sgn(new_delta_y)) {
85
        // Swap low and high.
86
        auto low = new_segment.low();
87
        new_segment.low(new_segment.high());
88
        new_segment.high(low);
89
      }
90
    }
91
  }
92

93
  return intersected_segments;
78✔
94
}
78✔
95

96
// Convert each linestring, which might have multiple points in it,
97
// into a linestrings that have just two points, the start and the
98
// end.  Directionality is maintained on each one along with whether
99
// or not it is reversible.
100
vector<pair<linestring_type_fp, bool>> segmentize_paths(const vector<pair<linestring_type_fp, bool>>& toolpaths) {
78✔
101
  // Merge points that are very close to each other because it makes
102
  // us more likely to find intersections that was can use.
103
  auto merged_toolpaths = toolpaths;
78✔
104
  merge_near_points(merged_toolpaths, 0.00001);
78!
105

106
  // First we need to split all paths so that they don't cross.  We need to
107
  // scale them up because the input is not floating point.
108
  vector<segment_type_p> all_segments;
109
  vector<bool> allow_reversals;
110
  for (const auto& toolpath_and_allow_reversal : merged_toolpaths) {
4,279✔
111
    const auto& toolpath = toolpath_and_allow_reversal.first;
112
    for (size_t i = 1; i < toolpath.size(); i++) {
25,951✔
113
      all_segments.push_back(
114
          segment_type_p(
43,500✔
115
              point_type_p(toolpath[i-1].x() * SCALE, toolpath[i-1].y() * SCALE),
21,750✔
116
              point_type_p(toolpath[i  ].x() * SCALE, toolpath[i  ].y() * SCALE)));
21,750✔
117
      allow_reversals.push_back(toolpath_and_allow_reversal.second);
21,750!
118
    }
119
  }
120
  vector<pair<segment_type_p, bool>> split_segments = segmentize(all_segments, allow_reversals);
78!
121

122
  // Only allow reversing the direction of travel if mill_feed_direction is
123
  // ANY.  We need to scale them back down.
124
  vector<pair<linestring_type_fp, bool>> segments_as_linestrings;
125
  segments_as_linestrings.reserve(split_segments.size());
78!
126
  for (const auto& segment_and_allow_reversal : split_segments) {
20,896✔
127
    // Make a little 1-edge linestrings.
128
    linestring_type_fp ls;
129
    const auto& segment = segment_and_allow_reversal.first;
130
    const auto& allow_reversal = segment_and_allow_reversal.second;
131
    ls.push_back(point_type_fp(segment.low().x() / SCALE, segment.low().y() / SCALE));
20,818!
132
    ls.push_back(point_type_fp(segment.high().x() / SCALE, segment.high().y() / SCALE));
20,818!
133
    segments_as_linestrings.push_back(make_pair(ls, allow_reversal));
20,818✔
134
  }
135
  return segments_as_linestrings;
78✔
136
}
156✔
137

138
} //namespace segmentize
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc