Preserve stipple phase across separate piecewise linear segments.
This significantly increases visual clarity, especially for curves with a low chord tolerance value.
This commit is contained in:
parent
47288e9a4c
commit
b37aba00e2
|
@ -377,7 +377,7 @@ public:
|
||||||
void DoFatLine(const Vector &a, const Vector &b, double width);
|
void DoFatLine(const Vector &a, const Vector &b, double width);
|
||||||
void DoLine(const Vector &a, const Vector &b, hStroke hcs);
|
void DoLine(const Vector &a, const Vector &b, hStroke hcs);
|
||||||
void DoPoint(Vector p, double radius);
|
void DoPoint(Vector p, double radius);
|
||||||
void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs);
|
void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs, double phase = 0.0);
|
||||||
|
|
||||||
void UpdateProjection(bool flip = FLIP_FRAMEBUFFER);
|
void UpdateProjection(bool flip = FLIP_FRAMEBUFFER);
|
||||||
void BeginFrame();
|
void BeginFrame();
|
||||||
|
|
|
@ -42,9 +42,10 @@ void CairoRenderer::SelectStroke(hStroke hcs) {
|
||||||
current.hcs = hcs;
|
current.hcs = hcs;
|
||||||
|
|
||||||
RgbaColor color = stroke->color;
|
RgbaColor color = stroke->color;
|
||||||
std::vector<double> dashes =
|
std::vector<double> dashes = StipplePatternDashes(stroke->stipplePattern);
|
||||||
StipplePatternDashes(stroke->stipplePattern,
|
for(double &dash : dashes) {
|
||||||
stroke->StippleScalePx(camera));
|
dash *= stroke->StippleScalePx(camera);
|
||||||
|
}
|
||||||
cairo_set_line_width(context, stroke->WidthPx(camera));
|
cairo_set_line_width(context, stroke->WidthPx(camera));
|
||||||
cairo_set_dash(context, dashes.data(), dashes.size(), 0);
|
cairo_set_dash(context, dashes.data(), dashes.size(), 0);
|
||||||
cairo_set_source_rgba(context, color.redF(), color.greenF(), color.blueF(),
|
cairo_set_source_rgba(context, color.redF(), color.greenF(), color.blueF(),
|
||||||
|
|
|
@ -354,91 +354,51 @@ void OpenGl1Renderer::DoPoint(Vector p, double d) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGl1Renderer::DoStippledLine(const Vector &a, const Vector &b, hStroke hcs) {
|
void OpenGl1Renderer::DoStippledLine(const Vector &a, const Vector &b, hStroke hcs, double phase) {
|
||||||
Stroke *stroke = SelectStroke(hcs);
|
Stroke *stroke = SelectStroke(hcs);
|
||||||
|
|
||||||
const char *patternSeq;
|
if(stroke->stipplePattern == StipplePattern::CONTINUOUS) {
|
||||||
switch(stroke->stipplePattern) {
|
DoLine(a, b, hcs);
|
||||||
case StipplePattern::CONTINUOUS: DoLine(a, b, hcs); return;
|
return;
|
||||||
case StipplePattern::SHORT_DASH: patternSeq = "- "; break;
|
}
|
||||||
case StipplePattern::DASH: patternSeq = "- "; break;
|
|
||||||
case StipplePattern::LONG_DASH: patternSeq = "_ "; break;
|
double scale = stroke->StippleScaleMm(camera);
|
||||||
case StipplePattern::DASH_DOT: patternSeq = "-."; break;
|
const std::vector<double> &dashes = StipplePatternDashes(stroke->stipplePattern);
|
||||||
case StipplePattern::DASH_DOT_DOT: patternSeq = "-.."; break;
|
double length = StipplePatternLength(stroke->stipplePattern) * scale;
|
||||||
case StipplePattern::DOT: patternSeq = "."; break;
|
|
||||||
case StipplePattern::FREEHAND: patternSeq = "~"; break;
|
phase -= floor(phase / length) * length;
|
||||||
case StipplePattern::ZIGZAG: patternSeq = "~__"; break;
|
|
||||||
|
double curPhase = 0.0;
|
||||||
|
size_t curDash;
|
||||||
|
for(curDash = 0; curDash < dashes.size(); curDash++) {
|
||||||
|
curPhase += dashes[curDash] * scale;
|
||||||
|
if(phase < curPhase) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector dir = b.Minus(a);
|
Vector dir = b.Minus(a);
|
||||||
double len = dir.Magnitude();
|
double len = dir.Magnitude();
|
||||||
dir = dir.WithMagnitude(1.0);
|
dir = dir.WithMagnitude(1.0);
|
||||||
|
|
||||||
const char *si = patternSeq;
|
double cur = 0.0;
|
||||||
double end = len;
|
Vector curPos = a;
|
||||||
double ss = stroke->StippleScaleMm(camera) / 2.0;
|
double width = stroke->WidthMm(camera);
|
||||||
do {
|
|
||||||
double start = end;
|
|
||||||
switch(*si) {
|
|
||||||
case ' ':
|
|
||||||
end -= 1.0 * ss;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '-':
|
double curDashLen = (curPhase - phase) / scale;
|
||||||
start = max(start - 0.5 * ss, 0.0);
|
while(cur < len) {
|
||||||
end = max(start - 2.0 * ss, 0.0);
|
double next = std::min(len, cur + curDashLen * scale);
|
||||||
if(start == end) break;
|
Vector nextPos = curPos.Plus(dir.ScaledBy(next - cur));
|
||||||
DoLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), hcs);
|
if(curDash % 2 == 0) {
|
||||||
end = max(end - 0.5 * ss, 0.0);
|
if(curDashLen <= LENGTH_EPS) {
|
||||||
break;
|
DoPoint(curPos, width);
|
||||||
|
} else {
|
||||||
case '_':
|
DoLine(curPos, nextPos, hcs);
|
||||||
end = max(end - 4.0 * ss, 0.0);
|
|
||||||
DoLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), hcs);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '.':
|
|
||||||
end = max(end - 0.5 * ss, 0.0);
|
|
||||||
if(end == 0.0) break;
|
|
||||||
DoPoint(a.Plus(dir.ScaledBy(end)), stroke->WidthPx(camera));
|
|
||||||
end = max(end - 0.5 * ss, 0.0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '~': {
|
|
||||||
Vector ab = b.Minus(a);
|
|
||||||
Vector gn = (camera.projRight).Cross(camera.projUp);
|
|
||||||
Vector abn = (ab.Cross(gn)).WithMagnitude(1);
|
|
||||||
abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
|
|
||||||
double pws = 2.0 * stroke->width / camera.scale;
|
|
||||||
|
|
||||||
end = max(end - 0.5 * ss, 0.0);
|
|
||||||
Vector aa = a.Plus(dir.ScaledBy(start));
|
|
||||||
Vector bb = a.Plus(dir.ScaledBy(end))
|
|
||||||
.Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
|
|
||||||
DoLine(aa, bb, hcs);
|
|
||||||
if(end == 0.0) break;
|
|
||||||
|
|
||||||
start = end;
|
|
||||||
end = max(end - 1.0 * ss, 0.0);
|
|
||||||
aa = a.Plus(dir.ScaledBy(end))
|
|
||||||
.Plus(abn.ScaledBy(pws))
|
|
||||||
.Minus(abn.ScaledBy(2.0 * pws * (start - end) / ss));
|
|
||||||
DoLine(bb, aa, hcs);
|
|
||||||
if(end == 0.0) break;
|
|
||||||
|
|
||||||
start = end;
|
|
||||||
end = max(end - 0.5 * ss, 0.0);
|
|
||||||
bb = a.Plus(dir.ScaledBy(end))
|
|
||||||
.Minus(abn.ScaledBy(pws))
|
|
||||||
.Plus(abn.ScaledBy(pws * (start - end) / (0.5 * ss)));
|
|
||||||
DoLine(aa, bb, hcs);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default: ssassert(false, "Unexpected stipple pattern element");
|
|
||||||
}
|
}
|
||||||
if(*(++si) == 0) si = patternSeq;
|
cur = next;
|
||||||
} while(end > 0.0);
|
curPos = nextPos;
|
||||||
|
curDash++;
|
||||||
|
curDashLen = dashes[curDash % dashes.size()];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -450,35 +410,41 @@ void OpenGl1Renderer::DrawLine(const Vector &a, const Vector &b, hStroke hcs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGl1Renderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
|
void OpenGl1Renderer::DrawEdges(const SEdgeList &el, hStroke hcs) {
|
||||||
|
double phase = 0.0;
|
||||||
for(const SEdge *e = el.l.First(); e; e = el.l.NextAfter(e)) {
|
for(const SEdge *e = el.l.First(); e; e = el.l.NextAfter(e)) {
|
||||||
DoStippledLine(e->a, e->b, hcs);
|
DoStippledLine(e->a, e->b, hcs, phase);
|
||||||
|
phase += e->a.Minus(e->b).Magnitude();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGl1Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) {
|
void OpenGl1Renderer::DrawOutlines(const SOutlineList &ol, hStroke hcs, DrawOutlinesAs drawAs) {
|
||||||
Vector projDir = camera.projRight.Cross(camera.projUp);
|
Vector projDir = camera.projRight.Cross(camera.projUp);
|
||||||
|
double phase = 0.0;
|
||||||
switch(drawAs) {
|
switch(drawAs) {
|
||||||
case DrawOutlinesAs::EMPHASIZED_AND_CONTOUR:
|
case DrawOutlinesAs::EMPHASIZED_AND_CONTOUR:
|
||||||
for(const SOutline &o : ol.l) {
|
for(const SOutline &o : ol.l) {
|
||||||
if(o.IsVisible(projDir) || o.tag != 0) {
|
if(o.IsVisible(projDir) || o.tag != 0) {
|
||||||
DoStippledLine(o.a, o.b, hcs);
|
DoStippledLine(o.a, o.b, hcs, phase);
|
||||||
}
|
}
|
||||||
|
phase += o.a.Minus(o.b).Magnitude();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawOutlinesAs::EMPHASIZED_WITHOUT_CONTOUR:
|
case DrawOutlinesAs::EMPHASIZED_WITHOUT_CONTOUR:
|
||||||
for(const SOutline &o : ol.l) {
|
for(const SOutline &o : ol.l) {
|
||||||
if(!o.IsVisible(projDir) && o.tag != 0) {
|
if(!o.IsVisible(projDir) && o.tag != 0) {
|
||||||
DoStippledLine(o.a, o.b, hcs);
|
DoStippledLine(o.a, o.b, hcs, phase);
|
||||||
}
|
}
|
||||||
|
phase += o.a.Minus(o.b).Magnitude();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawOutlinesAs::CONTOUR_ONLY:
|
case DrawOutlinesAs::CONTOUR_ONLY:
|
||||||
for(const SOutline &o : ol.l) {
|
for(const SOutline &o : ol.l) {
|
||||||
if(o.IsVisible(projDir)) {
|
if(o.IsVisible(projDir)) {
|
||||||
DoStippledLine(o.a, o.b, hcs);
|
DoStippledLine(o.a, o.b, hcs, phase);
|
||||||
}
|
}
|
||||||
|
phase += o.a.Minus(o.b).Magnitude();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,8 @@ enum class StipplePattern : uint32_t {
|
||||||
LAST = ZIGZAG
|
LAST = ZIGZAG
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<double> StipplePatternDashes(StipplePattern pattern, double scale);
|
const std::vector<double> &StipplePatternDashes(StipplePattern pattern);
|
||||||
|
double StipplePatternLength(StipplePattern pattern);
|
||||||
|
|
||||||
enum class Command : uint32_t;
|
enum class Command : uint32_t;
|
||||||
|
|
||||||
|
|
74
src/util.cpp
74
src/util.cpp
|
@ -1133,38 +1133,48 @@ bool BBox::Contains(const Point2d &p, double r) const {
|
||||||
p.y <= (maxp.y + r);
|
p.y <= (maxp.y + r);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<double> SolveSpace::StipplePatternDashes(StipplePattern pattern, double scale) {
|
const std::vector<double>& SolveSpace::StipplePatternDashes(StipplePattern pattern) {
|
||||||
// Inkscape ignores all elements that are exactly zero instead of drawing
|
static bool initialized;
|
||||||
// them as dots.
|
static std::vector<double> dashes[(size_t)StipplePattern::LAST + 1];
|
||||||
double zero = 1e-6;
|
if(!initialized) {
|
||||||
|
// Inkscape ignores all elements that are exactly zero instead of drawing
|
||||||
std::vector<double> result;
|
// them as dots, so set those to 1e-6.
|
||||||
switch(pattern) {
|
dashes[(size_t)StipplePattern::CONTINUOUS] =
|
||||||
case StipplePattern::CONTINUOUS:
|
{};
|
||||||
break;
|
dashes[(size_t)StipplePattern::SHORT_DASH] =
|
||||||
case StipplePattern::SHORT_DASH:
|
{ 1.0, 2.0 };
|
||||||
result = { scale, scale * 2.0 };
|
dashes[(size_t)StipplePattern::DASH] =
|
||||||
break;
|
{ 1.0, 1.0 };
|
||||||
case StipplePattern::DASH:
|
dashes[(size_t)StipplePattern::DASH_DOT] =
|
||||||
result = { scale, scale };
|
{ 1.0, 0.5, 1e-6, 0.5 };
|
||||||
break;
|
dashes[(size_t)StipplePattern::DASH_DOT_DOT] =
|
||||||
case StipplePattern::DASH_DOT:
|
{ 1.0, 0.5, 1e-6, 0.5, 0.5, 1e-6 };
|
||||||
result = { scale, scale * 0.5, zero, scale * 0.5 };
|
dashes[(size_t)StipplePattern::DOT] =
|
||||||
break;
|
{ 1e-6, 0.5 };
|
||||||
case StipplePattern::DASH_DOT_DOT:
|
dashes[(size_t)StipplePattern::LONG_DASH] =
|
||||||
result = { scale, scale * 0.5, zero, scale * 0.5, scale * 0.5, zero };
|
{ 2.0, 0.5 };
|
||||||
break;
|
dashes[(size_t)StipplePattern::FREEHAND] =
|
||||||
case StipplePattern::DOT:
|
{ 1.0, 2.0 };
|
||||||
result = { zero, scale * 0.5 };
|
dashes[(size_t)StipplePattern::ZIGZAG] =
|
||||||
break;
|
{ 1.0, 2.0 };
|
||||||
case StipplePattern::LONG_DASH:
|
|
||||||
result = { scale * 2.0, scale * 0.5 };
|
|
||||||
break;
|
|
||||||
|
|
||||||
case StipplePattern::FREEHAND:
|
|
||||||
case StipplePattern::ZIGZAG:
|
|
||||||
ssassert(false, "Freehand and zigzag export not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return dashes[(size_t)pattern];
|
||||||
|
}
|
||||||
|
|
||||||
|
double SolveSpace::StipplePatternLength(StipplePattern pattern) {
|
||||||
|
static bool initialized;
|
||||||
|
static double lengths[(size_t)StipplePattern::LAST + 1];
|
||||||
|
if(!initialized) {
|
||||||
|
for(size_t i = 0; i < (size_t)StipplePattern::LAST; i++) {
|
||||||
|
const std::vector<double> &dashes = StipplePatternDashes((StipplePattern)i);
|
||||||
|
double length = 0.0;
|
||||||
|
for(double dash : dashes) {
|
||||||
|
length += dash;
|
||||||
|
}
|
||||||
|
lengths[i] = length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lengths[(size_t)pattern];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user