Compare commits

..

No commits in common. "main" and "master" have entirely different histories.
main ... master

8 changed files with 88 additions and 194 deletions

View file

@ -1,21 +1,13 @@
.PHONY: default .PHONY: default
default: dashing dashing.omp default: dashing
.PHONY: bench .PHONY: bench
bench: dashing bench: dashing
perf stat --log-fd=2 ./dashing -b -s .1 data/HWOOD6E1.pat data/sf.seg perf stat --log-fd=2 ./dashing -b -s .1 data/HWOOD6E1.pat data/sf.seg
dashing: main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh dashing: main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh
+g++ -W -Wall -O2 -g -std=c++20 $(filter %.cc, $^) -o $@ -flto -DNDEBUG g++ -W -Wall -O2 -g -std=c++11 $(filter %.cc, $^) -o $@ -lboost_random
dashing-noopt: main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh dashing-noopt: main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh
+g++ -W -Wall -O0 -g -std=c++20 $(filter %.cc, $^) -o $@ g++ -W -Wall -O0 -g -std=c++11 $(filter %.cc, $^) -o $@ -lboost_random
dashing-clang: main.cc dashing.cc dashing.hh parse_numbers.hh dashing-clang: main.cc dashing.cc dashing.hh parse_numbers.hh
+clang++ -W -Wall -O2 -g -std=c++20 $(filter %.cc, $^) -o $@ -flto -DNDEBUG clang++ -W -Wall -O2 -g -std=c++11 $(filter %.cc, $^) -o $@ -lboost_random
dashing.pgo.0: main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh
+g++ -W -Wall -O2 -g -std=c++20 $(filter %.cc, $^) -o $@ -flto -DNDEBUG -fprofile-generate
dashing.pgo.1: dashing.pgo.0 main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh
./dashing.pgo.0 -b -s .002 data/HWOOD6E1.pat data/sf.seg
+g++ -W -Wall -O2 -g -std=c++20 $(filter %.cc, $^) -o $@ -flto -DNDEBUG -fprofile-use
dashing.omp: main.cc dashing.cc dashing.hh parse_numbers.hh contours_and_segments.hh
+g++ -W -Wall -O2 -g -std=c++20 $(filter %.cc, $^) -o $@ -flto -DNDEBUG -fopenmp -DDASHING_OMP

View file

@ -2,11 +2,11 @@
License: permissive (zlib); see source files for additional details. License: permissive (zlib); see source files for additional details.
On a i5-1235U in multithreaded benchmark mode, it runs at over 3.3 billion dashes per second: On a i5-3320M in benchmark mode, it runs at over 175 million dashes per second:
``` ```
$ time ./dashing.omp -b -s .002 data/HWOOD6E1.pat data/sf.seg $ time ./dashing -b -s .01 data/HWOOD6E1.pat data/sf.seg
2822685873 111430525
real 0m0.851s user 0m0.616s
``` ```
![Example image](data/sf.png) ![Example image](data/sf.png)
@ -31,7 +31,7 @@ I would be interested in passing this project to a new maintainer.
The winding rule `wr` defines which regions are in the interior of the contours. The winding rule `wr` defines which regions are in the interior of the contours.
For each dash or dot in the resulting hatch, `cb` is called with the output segment. For each dash or dot in the resulting hatch, `cb` is called with the output segment.
`xyhatch(const HatchPattern&, const C &segments, Cb cb, const char *wr)`: `xyhatch(const HatchPattern&, const C &segments, Cb cb, const char \*wr)`:
The container C holds segments which must define a set of closed The container C holds segments which must define a set of closed
contours. contours.
The winding rule `wr` defines which regions are in the interior of the contours. The winding rule `wr` defines which regions are in the interior of the contours.
@ -39,7 +39,7 @@ I would be interested in passing this project to a new maintainer.
`HatchPattern::FromFile`: Read a hatch pattern from a file. `HatchPattern::FromFile`: Read a hatch pattern from a file.
`parse_numbers(std::string line)`: Read a comma and/or space-separated `parse\_numbers(std::string line)`: Read a comma and/or space-separated
sequence of numbers into a vector sequence of numbers into a vector
`SegmentsFromFile`, `ContoursFromFile`, `ContourToSegments`, `ContoursToSegments`: Read and convert segments and contours `SegmentsFromFile`, `ContoursFromFile`, `ContourToSegments`, `ContoursToSegments`: Read and convert segments and contours
@ -51,22 +51,6 @@ Useful winding rules include:
but any predicate of a single integer may be used. but any predicate of a single integer may be used.
### Parallel API
These APIs are available if built with `-fopenmp -DDASHING_OMP`
`xyhatch_omp(const HatchPattern&, It start, It end, Cb cb, Wr wr)`:
Iterators `start`..`end` define a range of segments, which must define a set of closed contours.
The winding rule `wr` defines which regions are in the interior of the contours.
For each dash or dot in the resulting hatch, `cb` is called with the output segment and the thread ID.
`xyhatch_omp(const HatchPattern&, const C &segments, Cb cb, Wr wr)`:
The container C holds segments which must define a set of closed
contours.
The winding rule `wr` defines which regions are in the interior of the contours.
For each dash or dot in the resulting hatch, `cb` is called with the output segment and the thread ID.
### Other APIs
Other items in the header files are implementation details. Other items in the header files are implementation details.
## Demo program ## Demo program

View file

@ -12,7 +12,7 @@ typedef std::vector<Contour> Contours;
void ContourToSegments(Segments &dest, void ContourToSegments(Segments &dest,
/* EXPLICIT COPY */ Contour src, /* EXPLICIT COPY */ Contour src,
F jitter = 0) double jitter = 0)
{ {
if(jitter) if(jitter)
{ {
@ -32,7 +32,7 @@ void ContourToSegments(Segments &dest,
dest.emplace_back(Segment{src[i-1], src[0], false}); dest.emplace_back(Segment{src[i-1], src[0], false});
} }
void ContoursToSegments(Segments &dest, Contours const &src, F jitter=0) void ContoursToSegments(Segments &dest, Contours const &src, double jitter=0)
{ {
dest.clear(); dest.clear();
@ -62,14 +62,14 @@ Contours ContoursFromFile(std::istream &fi)
return result; return result;
} }
Segments SegmentsFromFile(std::istream &fi, F jitter) { Segments SegmentsFromFile(std::istream &fi, double jitter) {
auto && contours = ContoursFromFile(fi); auto && contours = ContoursFromFile(fi);
auto result = Segments{}; auto result = Segments{};
ContoursToSegments(result, contours, jitter); ContoursToSegments(result, contours, jitter);
return result; return result;
} }
std::vector<Segment> SegmentsFromFile(const char *filename, F jitter) { std::vector<Segment> SegmentsFromFile(const char *filename, double jitter) {
std::fstream fi(filename); std::fstream fi(filename);
return SegmentsFromFile(fi, jitter); return SegmentsFromFile(fi, jitter);
} }

View file

@ -2,41 +2,37 @@
#include "dashing.hh" #include "dashing.hh"
namespace dashing namespace dashing
{ {
std::vector<F> parse_numbers(std::string line) { std::vector<double> parse_numbers(std::string line) {
boost::algorithm::replace_all(line, ",", " ");
std::istringstream fi(line); std::istringstream fi(line);
std::vector<F> result; std::vector<double> result;
F d; double d;
while((fi >> d)) { while((fi >> d)) result.push_back(d);
result.push_back(d);
for (auto p = fi.peek(); p == ',' || isspace(p); p = fi.peek()) {
fi.get();
}
}
return result; return result;
} }
PSMatrix PSMatrix::inverse() const { PSMatrix PSMatrix::inverse() const {
auto i = F(1.) / determinant(); auto i = 1. / determinant();
return PSMatrix{d*i, -b*i, -c*i, a*i, i*(c*f-e*d), i*(b*e-a*f)}; return PSMatrix{d*i, -b*i, -c*i, a*i, i*(c*f-e*d), i*(b*e-a*f)};
} }
PSMatrix Translation(F x, F y) { PSMatrix Translation(double x, double y) {
PSMatrix r{1.,0.,0.,1.,x,y}; PSMatrix r{1.,0.,0.,1.,x,y};
return r; return r;
} }
PSMatrix Rotation(F theta) { PSMatrix Rotation(double theta) {
F c = cos(theta), s = sin(theta); double c = cos(theta), s = sin(theta);
PSMatrix r{c,s,-s,c,0.,0.}; PSMatrix r{c,s,-s,c,0.,0.};
return r; return r;
} }
PSMatrix XSkew(F xk) { PSMatrix XSkew(double xk) {
PSMatrix r{1.,0.,xk,1.,0.,0.}; PSMatrix r{1.,0.,xk,1.,0.,0.};
return r; return r;
} }
PSMatrix YScale(F ys) { PSMatrix YScale(double ys) {
PSMatrix r{1.,0.,0.,ys,0.,0.}; PSMatrix r{1.,0.,0.,ys,0.,0.};
return r; return r;
} }
@ -52,31 +48,21 @@ PSMatrix operator*(const PSMatrix &m1, const PSMatrix m2) {
return r; return r;
} }
static F radians(F degrees) { return degrees * acos(0) / 90.; } static double radians(double degrees) { return degrees * acos(0) / 90.; }
Dash::Dash(F th, F x0, F y0, F dx, F dy, Dash::Dash(double th, double x0, double y0, double dx, double dy,
const std::vector<F>::const_iterator dbegin, const std::vector<double>::const_iterator dbegin,
const std::vector<F>::const_iterator dend) const std::vector<double>::const_iterator dend) : dash(dbegin, dend) {
: dash{dbegin, dend} {
auto s = 0.; auto s = 0.;
for(size_t i = 0; i < dash.size(); i++) { for(auto d : dash) { sum.push_back(s); s += fabs(d); }
bool is_negative = std::signbit(dash[i]);
bool index_is_odd = (i % 2) != 0;
if(is_negative != index_is_odd) {
throw std::invalid_argument("not a supported dash specification (but probably valid)");
}
dash[i] = std::abs(dash[i]);
}
if (dash.size() % 2) dash.push_back(0);
for(auto d : dash) { sum.push_back(s); s += d; }
sum.push_back(s); sum.push_back(s);
tr = Translation(x0, y0) * Rotation(th) * XSkew(dx / dy) * YScale(dy); tr = Translation(x0, y0) * Rotation(th) * XSkew(dx / dy) * YScale(dy);
tf = tr.inverse(); tf = tr.inverse();
} }
Dash Dash::FromString(const std::string &line, F scale) { Dash Dash::FromString(const std::string &line, double scale) {
std::vector<F> words = parse_numbers(line); std::vector<double> words = parse_numbers(line);
if(words.size() < 5) if(words.size() < 5)
throw std::invalid_argument("not a valid dash specification"); throw std::invalid_argument("not a valid dash specification");
for(auto i = words.begin() + 1; i != words.end(); i++) *i *= scale; for(auto i = words.begin() + 1; i != words.end(); i++) *i *= scale;

View file

@ -18,47 +18,43 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
#pragma once #ifndef DASHING_H
#define DASHING_H
#include <algorithm>
#include <cmath> #include <cmath>
#include <cassert> #include <cassert>
#include <vector> #include <vector>
#include <string> #include <string>
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
#include <span> #include <boost/algorithm/string/replace.hpp>
#if defined(DASHING_OMP) #include <boost/algorithm/string/trim.hpp>
#include <omp.h>
#endif
#include "dashing_F.hh"
namespace dashing namespace dashing
{ {
struct PSMatrix { struct PSMatrix {
F a, b, c, d, e, f; double a, b, c, d, e, f;
PSMatrix inverse() const; PSMatrix inverse() const;
F determinant() const { return a * d - b * c; } double determinant() const { return a * d - b * c; }
}; };
PSMatrix Translation(F x, F y); PSMatrix Translation(double x, double y);
PSMatrix Rotation(F theta); PSMatrix Rotation(double theta);
PSMatrix XSkew(F xk); PSMatrix XSkew(double xk);
PSMatrix YScale(F ys); PSMatrix YScale(double ys);
struct Point { F x, y; }; struct Point { double x, y; };
inline Point operator*(const Point &p, const PSMatrix &m) { inline Point operator*(const Point &p, const PSMatrix &m) {
return Point{ p.x*m.a + p.y*m.c + m.e, return Point{ p.x*m.a + p.y*m.c + m.e,
p.x*m.b + p.y*m.d + m.f }; p.x*m.b + p.y*m.d + m.f };
} }
inline Point operator*(const Point &p, F d) inline Point operator*(const Point &p, double d)
{ {
return Point{ p.x * d, p.y * d }; return Point{ p.x * d, p.y * d };
} }
inline Point operator*(F d, const Point &p) inline Point operator*(double d, const Point &p)
{ {
return Point{ p.x * d, p.y * d }; return Point{ p.x * d, p.y * d };
} }
@ -71,17 +67,17 @@ PSMatrix operator*(const PSMatrix &m1, const PSMatrix m2);
struct Dash { struct Dash {
PSMatrix tr, tf; PSMatrix tr, tf;
std::vector<F> dash, sum; std::vector<double> dash, sum;
Dash(F th, F x0, F y0, F dx, F dy, Dash(double th, double x0, double y0, double dx, double dy,
const std::vector<F>::const_iterator dbegin, const std::vector<double>::const_iterator dbegin,
const std::vector<F>::const_iterator dend); const std::vector<double>::const_iterator dend);
static Dash FromString(const std::string &line, F scale); static Dash FromString(const std::string &line, double scale);
}; };
struct Segment { Point p, q; bool swapped; }; struct Segment { Point p, q; bool swapped; };
struct Intersection { F u; bool positive; }; struct Intersection { double u; bool positive; };
inline bool operator<(const Intersection &a, const Intersection &b) inline bool operator<(const Intersection &a, const Intersection &b)
{ {
return a.u < b.u; return a.u < b.u;
@ -94,16 +90,16 @@ inline void ysort(Segment &s) {
std::swap(s.p, s.q); std::swap(s.p, s.q);
} }
inline int intceil(F x) { return int(ceil(x)); } inline double intceil(double x) { return int(ceil(x)); }
inline int intfloor(F x) { return int(floor(x)); } inline double intfloor(double x) { return int(floor(x)); }
inline F pythonmod(F a, F b) { inline double pythonmod(double a, double b) {
auto r = a - floor(a / b) * b; auto r = a - floor(a / b) * b;
if(r == b) return 0; if(r == b) return 0;
return r; return r;
} }
inline size_t utoidx(const Dash &d, F u, F &o) { inline size_t utoidx(const Dash &d, double u, double &o) {
u = pythonmod(u, d.sum.back()); u = pythonmod(u, d.sum.back());
for(size_t i = 1; i != d.sum.size(); i++) { for(size_t i = 1; i != d.sum.size(); i++) {
if(u < d.sum[i]) { o = u - d.sum[i-1]; return i-1; } if(u < d.sum[i]) { o = u - d.sum[i-1]; return i-1; }
@ -112,30 +108,26 @@ inline size_t utoidx(const Dash &d, F u, F &o) {
} }
template<class Cb> template<class Cb>
void uvdraw(const Dash &pattern, F v, F u1, F u2, Cb cb) { void uvdraw(const Dash &pattern, double v, double u1, double u2, Cb cb) {
if(pattern.dash.empty()) { cb(v, u1, u2); return; } if(pattern.dash.empty()) { cb(v, u1, u2); return; }
F o; double o;
auto i = utoidx(pattern, u1, o); auto i = utoidx(pattern, u1, o);
const auto pi = pattern.dash[i]; const auto &pi = pattern.dash[i];
if(i % 2 == 0) { cb(v, u1, std::min(u2, u1+pi-o)); u1 += pi-o; } if(pi >= 0) { cb(v, u1, std::min(u2, u1+pi-o)); u1 += pi-o; }
else { u1 -= pi+o; } else { u1 -= pi+o; }
i++; i = i + 1;
if(i % 2) { if(i == pattern.dash.size()) i = 0;
u1 += pattern.dash[i];
i++;
}
for(auto u = u1; u < u2;) { for(auto u = u1; u < u2;) {
if(i >= pattern.dash.size()) i = 0; const auto &pi = pattern.dash[i];
const auto pi = pattern.dash[i]; if(pi >= 0) { cb(v, u, std::min(u2, u+pi)); u += pi; }
cb(v, u, std::min(u2, u+pi)); else { u -= pi; }
u += pi; i = i + 1;
u += pattern.dash[i+1]; if(i == pattern.dash.size()) i = 0;
i+=2;
} }
} }
template<class Cb, class Wr> template<class Cb, class Wr>
void uvspans(const Dash &pattern, std::vector<Segment> & segments, Cb cb, std::vector<Intersection> &uu, Wr wr) { void uvspans(const Dash &pattern, std::vector<Segment> && segments, Cb cb, std::vector<Intersection> &uu, Wr wr) {
if(segments.empty()) return; // no segments if(segments.empty()) return; // no segments
for(auto &s : segments) ysort(s); for(auto &s : segments) ysort(s);
@ -181,16 +173,16 @@ void uvspans(const Dash &pattern, std::vector<Segment> & segments, Cb cb, std::v
segments_begin ++; segments_begin ++;
} }
for(const auto &s : std::span(heap_begin, heap_end)) { for(const auto &s : boost::make_iterator_range(heap_begin, heap_end)) {
auto du = s.q.x - s.p.x; auto du = s.q.x - s.p.x;
auto dv = s.q.y - s.p.y; auto dv = s.q.y - s.p.y;
assert(dv); assert(dv);
uu.push_back( if(dv) uu.push_back(
Intersection{s.p.x + du * (v - s.p.y) / dv,s.swapped}); Intersection{s.p.x + du * (v - s.p.y) / dv,s.swapped});
} }
std::sort(uu.begin(), uu.end()); std::sort(uu.begin(), uu.end());
int winding = 0; int winding = 0;
F old_u = -std::numeric_limits<F>::infinity(); double old_u = -std::numeric_limits<double>::infinity();
for(const auto &isect : uu) { for(const auto &isect : uu) {
if(wr(winding)) uvdraw(pattern, v, old_u, isect.u, cb); if(wr(winding)) uvdraw(pattern, v, old_u, isect.u, cb);
winding += 2*isect.positive - 1; winding += 2*isect.positive - 1;
@ -201,16 +193,14 @@ void uvspans(const Dash &pattern, std::vector<Segment> & segments, Cb cb, std::v
struct HatchPattern { struct HatchPattern {
std::vector<Dash> d; std::vector<Dash> d;
static HatchPattern FromFile(std::istream &fi, F scale) { static HatchPattern FromFile(std::istream &fi, double scale) {
HatchPattern result; HatchPattern result;
std::string line; std::string line;
while(getline(fi, line)) { while(getline(fi, line)) {
auto i = line.find(";"); auto i = line.find(";");
if(i != line.npos) line.erase(i, line.npos); if(i != line.npos) line.erase(i, line.npos);
while(!line.empty() && isspace(line.back())) { boost::algorithm::trim(line);
line.pop_back();
}
if(line.empty()) continue; if(line.empty()) continue;
if(line[0] == '*') continue; if(line[0] == '*') continue;
result.d.push_back(Dash::FromString(line, scale)); result.d.push_back(Dash::FromString(line, scale));
@ -218,34 +208,34 @@ struct HatchPattern {
return result; return result;
} }
static HatchPattern FromFile(const char *filename, F scale) { static HatchPattern FromFile(const char *filename, double scale) {
std::ifstream fi(filename); std::ifstream fi(filename);
return FromFile(fi, scale); return FromFile(fi, scale);
} }
}; };
template<class It, class Cb, class Wr> template<class It, class Cb, class Wr>
void xyhatch(const Dash &pattern, It start, It end, Cb cb, Wr wr) { void xyhatch(const Dash &pattern, It start, It end, Cb cb, std::vector<Segment> &uvsegments, std::vector<Intersection> &uu, Wr wr) {
std::vector<Segment> uvsegments; uvsegments.clear();
uvsegments.reserve(end-start);
std::vector<Intersection> uu;
uu.reserve(8);
bool swapped = pattern.tf.determinant() < 0; bool swapped = pattern.tf.determinant() < 0;
std::transform(start, end, std::back_inserter(uvsegments), std::transform(start, end, std::back_inserter(uvsegments),
[&](const Segment &s) [&](const Segment &s)
{ return Segment{s.p * pattern.tf, s.q * pattern.tf, swapped != s.swapped }; { return Segment{s.p * pattern.tf, s.q * pattern.tf, swapped != s.swapped };
}); });
uvspans(pattern, uvsegments, [&](F v, F u1, F u2) { uvspans(pattern, std::move(uvsegments), [&](double v, double u1, double u2) {
Point p{u1, v}, q{u2, v}; Point p{u1, v}, q{u2, v};
cb(Segment{ p * pattern.tr, q * pattern.tr, false }); Segment xy{ p * pattern.tr, q * pattern.tr, false };
cb(xy);
}, uu, wr); }, uu, wr);
} }
template<class It, class Cb, class Wr> template<class It, class Cb, class Wr>
void xyhatch(const HatchPattern &pattern, It start, It end, Cb cb, Wr wr) { void xyhatch(const HatchPattern &pattern, It start, It end, Cb cb, Wr wr) {
for(const auto &i : pattern.d) xyhatch(i, start, end, cb, wr); std::vector<Segment> uvsegments;
uvsegments.reserve(end-start);
std::vector<Intersection> uu;
uu.reserve(8);
for(const auto &i : pattern.d) xyhatch(i, start, end, cb, uvsegments, uu, wr);
} }
template<class C, class Cb, class Wr> template<class C, class Cb, class Wr>
@ -253,20 +243,5 @@ void xyhatch(const HatchPattern &pattern, const C &c, Cb cb, Wr wr) {
xyhatch(pattern, c.begin(), c.end(), cb, wr); xyhatch(pattern, c.begin(), c.end(), cb, wr);
} }
#if defined(DASHING_OMP)
template<class It, class Cb, class Wr>
void xyhatch_omp(const HatchPattern &pattern, It start, It end, Cb cb, Wr wr) {
#pragma omp parallel for
for(const auto &i : pattern.d) {
int iam = omp_get_thread_num();
xyhatch(i, start, end, [iam, &cb](Segment s) { cb(s, iam); }, wr);
}
} }
template<class C, class Cb, class Wr>
void xyhatch_omp(const HatchPattern &pattern, const C &c, Cb cb, Wr wr) {
xyhatch_omp(pattern, c.begin(), c.end(), cb, wr);
}
#endif #endif
}

View file

@ -1,26 +0,0 @@
/*
Copyright (c) 2015 Jeff Epler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgement in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
namespace dashing
{
typedef float F;
}

23
main.cc
View file

@ -20,12 +20,6 @@ freely, subject to the following restrictions:
// demo and benchmark program for dashing // demo and benchmark program for dashing
#include <new>
#include <atomic>
#include <cstring>
#if defined(DASHING_OMP)
#include <omp.h>
#endif
#include "dashing.hh" #include "dashing.hh"
#include "contours_and_segments.hh" #include "contours_and_segments.hh"
@ -91,20 +85,9 @@ int main(int argc, char **argv) {
if(xit) return 0; if(xit) return 0;
if(bench) { if(bench) {
#if defined(DASHING_OMP) int nseg = 0;
struct per_thread_count_type { auto print_seg = [&nseg](const Segment &s) { (void)s; nseg ++; };
alignas(std::hardware_destructive_interference_size) size_t n; xyhatch(h, s, print_seg, [](int i) { return i != 0; } );
};
auto max_threads = omp_get_max_threads();
std::vector<per_thread_count_type> thread_nseg(max_threads, per_thread_count_type{0});
auto count_seg = [&thread_nseg](const Segment &s, int iam) { (void)s; thread_nseg[iam].n++; };
xyhatch_omp(h, s, count_seg, [](int i) { return i != 0; } );
size_t nseg = std::accumulate(thread_nseg.begin(), thread_nseg.end(), size_t{}, [](size_t a, const per_thread_count_type &b) { return a + b.n; });
#else
size_t nseg{};
auto count_seg = [&nseg](const Segment &s) { (void)s; nseg ++; };
xyhatch(h, s, count_seg, [](int i) { return i != 0; } );
#endif
std::cout << nseg << "\n"; std::cout << nseg << "\n";
return 0; return 0;
} }

View file

@ -3,9 +3,9 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include "dashing_F.hh" #include <boost/algorithm/string/replace.hpp>
namespace dashing namespace dashing
{ {
std::vector<F> parse_numbers(std::string line); std::vector<double> parse_numbers(std::string line);
} }
#endif #endif