Implement stippled line styles from ISO 128.

Now it's possible to use a styled line to indicate e.g.
a centerline.
This commit is contained in:
EvilSpirit 2016-02-24 00:00:39 +06:00 committed by whitequark
parent 171f208cfb
commit 1170a91875
11 changed files with 292 additions and 46 deletions

View File

@ -23,15 +23,7 @@ void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat) {
ssglDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3);
// Narrow lines are drawn as lines, but fat lines must be drawn as
// filled polygons, to get the line join style right.
if(!maybeFat || dogd.lineWidth < 3) {
glBegin(GL_LINES);
ssglVertex3v(a);
ssglVertex3v(b);
glEnd();
} else {
ssglFatLine(a, b, dogd.lineWidth/SS.GW.scale);
}
ssglStippledLine(a, b, dogd.lineWidth, dogd.stippleType, dogd.stippleScale);
ssglDepthRangeOffset(0);
} else {
Point2d ap = SS.GW.ProjectPoint(a);
@ -114,6 +106,8 @@ void Entity::DrawAll(void) {
void Entity::Draw(void) {
hStyle hs = Style::ForEntity(h);
dogd.lineWidth = Style::Width(hs);
dogd.stippleType = Style::PatternType(hs);
dogd.stippleScale = Style::StippleScaleMm(hs);
ssglLineWidth((float)dogd.lineWidth);
ssglColorRGB(Style::Color(hs));

View File

@ -198,6 +198,8 @@ const SolveSpaceUI::SaveTable SolveSpaceUI::SAVED[] = {
{ 's', "Style.filled", 'b', &(SS.sv.s.filled) },
{ 's', "Style.visible", 'b', &(SS.sv.s.visible) },
{ 's', "Style.exportable", 'b', &(SS.sv.s.exportable) },
{ 's', "Style.stippleType", 'd', &(SS.sv.s.stippleType) },
{ 's', "Style.stippleScale", 'f', &(SS.sv.s.stippleScale) },
{ 0, NULL, 0, NULL }
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

View File

@ -200,6 +200,128 @@ static void FatLineEndcap(Vector p, Vector u, Vector v)
glEnd();
}
void ssglLine(const Vector &a, const Vector &b, double pixelWidth, bool maybeFat = true) {
if(!maybeFat || pixelWidth < 3.0) {
glBegin(GL_LINES);
ssglVertex3v(a);
ssglVertex3v(b);
glEnd();
} else {
ssglFatLine(a, b, pixelWidth / SS.GW.scale);
}
}
void ssglPoint(Vector p, double pixelSize)
{
if(/*!maybeFat || */pixelSize < 3.0) {
glBegin(GL_LINES);
Vector u = SS.GW.projRight.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
ssglVertex3v(p.Minus(u));
ssglVertex3v(p.Plus(u));
glEnd();
} else {
Vector u = SS.GW.projRight.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
Vector v = SS.GW.projUp.WithMagnitude(pixelSize / SS.GW.scale / 2.0);
FatLineEndcap(p, u, v);
FatLineEndcap(p, u.ScaledBy(-1.0), v);
}
}
void ssglStippledLine(Vector a, Vector b, double width,
int stippleType, double stippleScale)
{
const char *stipplePattern;
switch(stippleType) {
case Style::STIPPLE_CONTINUOUS: ssglLine(a, b, width); return;
case Style::STIPPLE_DASH: stipplePattern = "- "; break;
case Style::STIPPLE_LONG_DASH: stipplePattern = "_ "; break;
case Style::STIPPLE_DASH_DOT: stipplePattern = "-."; break;
case Style::STIPPLE_DASH_DOT_DOT: stipplePattern = "-.."; break;
case Style::STIPPLE_DOT: stipplePattern = "."; break;
case Style::STIPPLE_FREEHAND: stipplePattern = "~"; break;
case Style::STIPPLE_ZIGZAG: stipplePattern = "~__"; break;
default: oops();
}
ssglStippledLine(a, b, width, stipplePattern, stippleScale);
}
void ssglStippledLine(Vector a, Vector b, double width,
const char *stipplePattern, double stippleScale)
{
if(stipplePattern == NULL || *stipplePattern == 0) oops();
Vector dir = b.Minus(a);
double len = dir.Magnitude();
dir = dir.WithMagnitude(1.0);
const char *si = stipplePattern;
double end = len;
double ss = stippleScale / 2.0;
do {
double start = end;
switch(*si) {
case ' ':
end -= 1.0 * ss;
break;
case '-':
start = max(start - 0.5 * ss, 0.0);
end = max(start - 2.0 * ss, 0.0);
if(start == end) break;
ssglLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), width);
end = max(end - 0.5 * ss, 0.0);
break;
case '_':
end = max(end - 4.0 * ss, 0.0);
ssglLine(a.Plus(dir.ScaledBy(start)), a.Plus(dir.ScaledBy(end)), width);
break;
case '.':
end = max(end - 0.5 * ss, 0.0);
if(end == 0.0) break;
ssglPoint(a.Plus(dir.ScaledBy(end)), width);
end = max(end - 0.5 * ss, 0.0);
break;
case '~': {
Vector ab = b.Minus(a);
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
Vector abn = (ab.Cross(gn)).WithMagnitude(1);
abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
double pws = 2.0 * width / SS.GW.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)));
ssglLine(aa, bb, width);
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));
ssglLine(bb, aa, width);
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)));
ssglLine(aa, bb, width);
break;
}
default: oops();
}
if(*(++si) == 0) si = stipplePattern;
} while(end > 0.0);
}
void ssglFatLine(Vector a, Vector b, double width)
{
// The half-width of the line we're drawing.

View File

@ -481,6 +481,8 @@ public:
double dmin;
Vector refp;
double lineWidth;
double stippleScale;
int stippleType;
} dogd; // state for drawing or getting distance (for hit testing)
void LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat=false);
void DrawOrGetDistance(void);
@ -711,6 +713,19 @@ public:
int tag;
hStyle h;
enum {
STIPPLE_CONTINUOUS = 0,
STIPPLE_DASH = 1,
STIPPLE_LONG_DASH = 2,
STIPPLE_DASH_DOT = 3,
STIPPLE_DASH_DOT_DOT = 4,
STIPPLE_DOT = 5,
STIPPLE_FREEHAND = 6,
STIPPLE_ZIGZAG = 7,
LAST_STIPPLE = STIPPLE_ZIGZAG
};
enum {
// If an entity has no style, then it will be colored according to
// whether the group that it's in is active or not, whether it's
@ -757,6 +772,8 @@ public:
RgbaColor fillColor;
bool visible;
bool exportable;
int stippleType;
double stippleScale;
// The default styles, for entities that don't have a style assigned yet,
// and for datums and such.
@ -793,6 +810,8 @@ public:
static double TextHeight(hStyle hs);
static bool Exportable(int hs);
static hStyle ForEntity(hEntity he);
static int PatternType(hStyle hs);
static double StippleScaleMm(hStyle hs);
std::string DescriptionString(void);

View File

@ -357,6 +357,10 @@ void ssglWriteTextRefCenter(const std::string &str, double h, Vector t, Vector u
double ssglStrWidth(const std::string &str, double h);
double ssglStrHeight(double h);
void ssglLockColorTo(RgbaColor rgb);
void ssglStippledLine(Vector a, Vector b, double width,
int stippleType, double stippleScale);
void ssglStippledLine(Vector a, Vector b, double width,
const char *stipplePattern, double stippleScale);
void ssglFatLine(Vector a, Vector b, double width);
void ssglUnlockColor(void);
void ssglColorRGB(RgbaColor rgb);

View File

@ -89,6 +89,8 @@ void Style::FillDefaultStyle(Style *s, const Default *d) {
s->exportable = true;
s->filled = false;
s->fillColor = RGBf(0.3, 0.3, 0.3);
s->stippleType = Style::STIPPLE_CONTINUOUS;
s->stippleScale = 15.0;
}
void Style::LoadFactoryDefaults(void) {
@ -107,6 +109,8 @@ void Style::LoadFactoryDefaults(void) {
s->exportable = true;
s->filled = false;
s->fillColor = RGBf(0.3, 0.3, 0.3);
s->stippleType = Style::STIPPLE_CONTINUOUS;
s->stippleScale = 15.0;
s->name = CnfPrefixToName(d->cnfPrefix);
}
SS.backgroundColor = RGBi(0, 0, 0);
@ -316,6 +320,21 @@ hStyle Style::ForEntity(hEntity he) {
return hs;
}
int Style::PatternType(hStyle hs) {
Style *s = Get(hs);
return s->stippleType;
}
double Style::StippleScaleMm(hStyle hs) {
Style *s = Get(hs);
if(s->widthAs == UNITS_AS_MM) {
return s->stippleScale;
} else if(s->widthAs == UNITS_AS_PIXELS) {
return s->stippleScale / SS.GW.scale;
}
return 1.0;
}
std::string Style::DescriptionString(void) {
if(name.empty()) {
return ssprintf("s%03x-(unnamed)", h.v);
@ -499,11 +518,42 @@ void TextWindow::ScreenDeleteStyle(int link, uint32_t v) {
InvalidateGraphics();
}
void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, uint32_t v) {
void TextWindow::ScreenChangeStylePatternType(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
double val = (link == 't') ? s->textHeight : s->width;
int units = (link == 't') ? s->textHeightAs : s->widthAs;
s->stippleType = link - 1;
}
void TextWindow::ScreenChangeStyleMetric(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
double val;
int units, meaning, col;
switch(link) {
case 't':
val = s->textHeight;
units = s->textHeightAs;
col = 10;
meaning = EDIT_STYLE_TEXT_HEIGHT;
break;
case 's':
val = s->stippleScale;
units = s->widthAs;
col = 17;
meaning = EDIT_STYLE_STIPPLE_PERIOD;
break;
case 'w':
case 'W':
val = s->width;
units = s->widthAs;
col = 9;
meaning = EDIT_STYLE_WIDTH;
break;
default: oops();
}
std::string edit_value;
if(units == Style::UNITS_AS_PIXELS) {
@ -511,12 +561,9 @@ void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, uint32_t v) {
} else {
edit_value = SS.MmToString(val);
}
int col = 9;
if(link == 't') col++;
SS.TW.ShowEditControl(col, edit_value);
SS.TW.edit.style = hs;
SS.TW.edit.meaning = (link == 't') ? EDIT_STYLE_TEXT_HEIGHT :
EDIT_STYLE_WIDTH;
SS.TW.edit.meaning = meaning;
}
void TextWindow::ScreenChangeStyleTextAngle(int link, uint32_t v) {
@ -557,12 +604,14 @@ void TextWindow::ScreenChangeStyleYesNo(int link, uint32_t v) {
if(s->widthAs != Style::UNITS_AS_MM) {
s->widthAs = Style::UNITS_AS_MM;
s->width /= SS.GW.scale;
s->stippleScale /= SS.GW.scale;
}
break;
case 'W':
if(s->widthAs != Style::UNITS_AS_PIXELS) {
s->widthAs = Style::UNITS_AS_PIXELS;
s->width *= SS.GW.scale;
s->stippleScale *= SS.GW.scale;
}
break;
@ -627,6 +676,7 @@ void TextWindow::ScreenChangeStyleYesNo(int link, uint32_t v) {
bool TextWindow::EditControlDoneForStyles(const char *str) {
Style *s;
switch(edit.meaning) {
case EDIT_STYLE_STIPPLE_PERIOD:
case EDIT_STYLE_TEXT_HEIGHT:
case EDIT_STYLE_WIDTH: {
SS.UndoRemember();
@ -643,6 +693,8 @@ bool TextWindow::EditControlDoneForStyles(const char *str) {
v = max(0.0, v);
if(edit.meaning == EDIT_STYLE_TEXT_HEIGHT) {
s->textHeight = v;
} else if(edit.meaning == EDIT_STYLE_STIPPLE_PERIOD) {
s->stippleScale = v;
} else {
s->width = v;
}
@ -728,20 +780,32 @@ void TextWindow::ShowStyleInfo(void) {
if(s->widthAs == Style::UNITS_AS_PIXELS) {
Printf(false, " %Ftwidth%E %@ %D%f%Lp%Fl[change]%E",
s->width,
s->h.v, &ScreenChangeStyleWidthOrTextHeight,
s->h.v, &ScreenChangeStyleMetric,
(s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
} else {
Printf(false, " %Ftwidth%E %s %D%f%Lp%Fl[change]%E",
SS.MmToString(s->width).c_str(),
s->h.v, &ScreenChangeStyleWidthOrTextHeight,
s->h.v, &ScreenChangeStyleMetric,
(s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
}
if(s->h.v >= Style::FIRST_CUSTOM) {
if(s->widthAs == Style::UNITS_AS_PIXELS) {
Printf(false, "%Ba %Ftstipple width%E %@ %D%f%Lp%Fl[change]%E",
s->stippleScale,
s->h.v, &ScreenChangeStyleMetric, 's');
} else {
Printf(false, "%Ba %Ftstipple width%E %s %D%f%Lp%Fl[change]%E",
SS.MmToString(s->stippleScale).c_str(),
s->h.v, &ScreenChangeStyleMetric, 's');
}
}
bool widthpx = (s->widthAs == Style::UNITS_AS_PIXELS);
if(s->h.v < Style::FIRST_CUSTOM) {
Printf(false,"%Ba %Ftin units of %Fdpixels%E");
Printf(false," %Ftin units of %Fdpixels%E");
} else {
Printf(false,"%Ba %Ftin units of %Fd"
Printf(false," %Ftin units of %Fd"
"%D%f%LW%s pixels%E "
"%D%f%Lw%s %s",
s->h.v, &ScreenChangeStyleYesNo,
@ -751,6 +815,45 @@ void TextWindow::ShowStyleInfo(void) {
SS.UnitName());
}
if(s->h.v >= Style::FIRST_CUSTOM) {
Printf(false,"%Ba %Ftstipple type:%E");
const size_t patternCount = Style::LAST_STIPPLE + 1;
const char *patternsSource[patternCount] = {
"___________",
"- - - - - -",
"__ __ __ __",
"-.-.-.-.-.-",
"..-..-..-..",
"...........",
"~~~~~~~~~~~",
"__~__~__~__"
};
std::string patterns[patternCount];
for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
const char *str = patternsSource[i];
do {
switch(*str) {
case ' ': patterns[i] += " "; break;
case '.': patterns[i] += "\xEE\x80\x84"; break;
case '_': patterns[i] += "\xEE\x80\x85"; break;
case '-': patterns[i] += "\xEE\x80\x86"; break;
case '~': patterns[i] += "\xEE\x80\x87"; break;
default: oops();
}
} while(*(++str));
}
for(int i = 0; i <= Style::LAST_STIPPLE; i++) {
const char *radio = s->stippleType == i ? RADIO_TRUE : RADIO_FALSE;
Printf(false, "%Bp %D%f%Lp%s %s%E",
(i % 2 == 0) ? 'd' : 'a',
s->h.v, &ScreenChangeStylePatternType,
i + 1, radio, patterns[i].c_str());
}
}
if(s->h.v >= Style::FIRST_CUSTOM) {
// The fill color, and whether contours are filled
@ -775,12 +878,12 @@ void TextWindow::ShowStyleInfo(void) {
if(s->textHeightAs == Style::UNITS_AS_PIXELS) {
Printf(false, "%Ba %Ftheight %E%@ %D%f%Lt%Fl%s%E",
s->textHeight,
s->h.v, &ScreenChangeStyleWidthOrTextHeight,
s->h.v, &ScreenChangeStyleMetric,
chng);
} else {
Printf(false, "%Ba %Ftheight %E%s %D%f%Lt%Fl%s%E",
SS.MmToString(s->textHeight).c_str(),
s->h.v, &ScreenChangeStyleWidthOrTextHeight,
s->h.v, &ScreenChangeStyleMetric,
chng);
}

View File

@ -171,6 +171,7 @@ public:
EDIT_STYLE_NAME = 505,
EDIT_BACKGROUND_COLOR = 506,
EDIT_BACKGROUND_IMG_SCALE = 507,
EDIT_STYLE_STIPPLE_PERIOD = 508,
// For paste transforming
EDIT_PASTE_TIMES_REPEATED = 600,
EDIT_PASTE_ANGLE = 601,
@ -260,6 +261,7 @@ public:
static void ScreenShowListOfStyles(int link, uint32_t v);
static void ScreenShowStyleInfo(int link, uint32_t v);
static void ScreenDeleteStyle(int link, uint32_t v);
static void ScreenChangeStylePatternType(int link, uint32_t v);
static void ScreenChangeStyleYesNo(int link, uint32_t v);
static void ScreenCreateCustomStyle(int link, uint32_t v);
static void ScreenLoadFactoryDefaultStyles(int link, uint32_t v);
@ -309,7 +311,7 @@ public:
static void ScreenChangeGCodeParameter(int link, uint32_t v);
static void ScreenChangeAutosaveInterval(int link, uint32_t v);
static void ScreenChangeStyleName(int link, uint32_t v);
static void ScreenChangeStyleWidthOrTextHeight(int link, uint32_t v);
static void ScreenChangeStyleMetric(int link, uint32_t v);
static void ScreenChangeStyleTextAngle(int link, uint32_t v);
static void ScreenChangeStyleColor(int link, uint32_t v);
static void ScreenChangeBackgroundColor(int link, uint32_t v);