Merge branch 'master' into python

pull/493/head
KmolYuan 2021-03-19 10:45:59 +08:00
commit 3136d6e82a
61 changed files with 177 additions and 104 deletions

View File

@ -29,6 +29,8 @@ New sketch features:
* Property browser now shows amount of degrees of freedom in group list. * Property browser now shows amount of degrees of freedom in group list.
It also shows a yellow "err" if the sketch has problems (e.g. self It also shows a yellow "err" if the sketch has problems (e.g. self
intersecting) that would propagate in subsequent groups. intersecting) that would propagate in subsequent groups.
* It is now possible to press "g" to toggle construction on new objects while
they are still being drawn.
New constraint features: New constraint features:
* When dragging an arc or rectangle point, it will be automatically * When dragging an arc or rectangle point, it will be automatically
@ -47,6 +49,10 @@ New constraint features:
would have been redundant with other ones. would have been redundant with other ones.
* New option to open the constraint editor for newly created constraints * New option to open the constraint editor for newly created constraints
with a value. with a value.
* New "redundant constraint timeout (in ms)" option to prevent UI freeze
when looking for redundant constraints.
* Swap vertical and horizontal constraints when pasting rotated by 90/270
degrees.
New export/import features: New export/import features:
* Link IDF circuit boards in an assembly (.emn files) * Link IDF circuit boards in an assembly (.emn files)
@ -63,12 +69,17 @@ New export/import features:
* VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org). * VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org).
* Export 2d section: custom styled entities that lie in the same * Export 2d section: custom styled entities that lie in the same
plane as the exported section are included. plane as the exported section are included.
* Added ExportBackgroundColor in configuration for EPS, PDF, and SVG files.
* STEP export includes object colors and transparency.
New rendering features: New rendering features:
* The "Show/hide hidden lines" button is now a tri-state button that allows * The "Show/hide hidden lines" button is now a tri-state button that allows
showing all lines (on top of shaded mesh), stippling occluded lines showing all lines (on top of shaded mesh), stippling occluded lines
or not drawing them at all. or not drawing them at all.
* The "Show/hide outlines" button is now independent from "Show/hide edges". * The "Show/hide outlines" button is now independent from "Show/hide edges".
* "View | Darken Inactive Solids" added. When turned off and a "sketch in plane"
group is active solids form previous groups will not be "darkened" (have the
s000d-#def-dim-solid style applied to them).
New measurement/analysis features: New measurement/analysis features:
* New choice for base unit, meters. * New choice for base unit, meters.
@ -85,7 +96,6 @@ New measurement/analysis features:
workplane is displayed. workplane is displayed.
Other new features: Other new features:
* Added ExportBackgroundColor in configuration for EPS, PDF, and SVG files.
* Improvements to the text window for selected entities and constraints. * Improvements to the text window for selected entities and constraints.
* Ambient light source added in text window to allow flat shaded renderings. * Ambient light source added in text window to allow flat shaded renderings.
* New command-line interface, for batch exporting and more. * New command-line interface, for batch exporting and more.
@ -114,6 +124,7 @@ Other new features:
* New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes * New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes
to enable support for multi-threading and link-time optimization. to enable support for multi-threading and link-time optimization.
* "Shift+Scroll" for ten times finer zoom. * "Shift+Scroll" for ten times finer zoom.
* Chinese translation
Bugs fixed: Bugs fixed:
* Fixed broken --view options for command line thumbnail image creation. * Fixed broken --view options for command line thumbnail image creation.

View File

@ -17,8 +17,8 @@ so any savefiles should first be archived.
Licensing Licensing
--------------- ---------------
SolveSpace is licensed under the GPLv3 and any contributions must be made available SolveSpace is licensed under the GPLv3 or later and any contributions
under the terms of that license. must be made available under the terms of that license.
Contributing translations Contributing translations
------------------------- -------------------------

View File

@ -259,4 +259,4 @@ and debug SolveSpace.
License License
------- -------
SolveSpace is distributed under the terms of the [GPL v3 license](COPYING.txt). SolveSpace is distributed under the terms of the [GPL v3](COPYING.txt) or later.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 819 B

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 686 B

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 B

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 920 B

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 620 B

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 B

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 B

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 B

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 B

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 B

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 871 B

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 B

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 515 B

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 B

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 515 B

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 B

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 703 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 639 B

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 B

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 733 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 193 B

View File

@ -127,7 +127,7 @@ hConstraint Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) {
Entity::NO_ENTITY, Entity::NO_ENTITY, /*other=*/false, /*other2=*/false); Entity::NO_ENTITY, Entity::NO_ENTITY, /*other=*/false, /*other2=*/false);
} }
void Constraint::ConstrainArcLineTangent(Constraint *c, Entity *line, Entity *arc) { bool Constraint::ConstrainArcLineTangent(Constraint *c, Entity *line, Entity *arc) {
Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(), Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
l1 = SK.GetEntity(line->point[1])->PointGetNum(); l1 = SK.GetEntity(line->point[1])->PointGetNum();
Vector a1 = SK.GetEntity(arc->point[1])->PointGetNum(), Vector a1 = SK.GetEntity(arc->point[1])->PointGetNum(),
@ -140,11 +140,12 @@ void Constraint::ConstrainArcLineTangent(Constraint *c, Entity *line, Entity *ar
Error(_("The tangent arc and line segment must share an " Error(_("The tangent arc and line segment must share an "
"endpoint. Constrain them with Constrain -> " "endpoint. Constrain them with Constrain -> "
"On Point before constraining tangent.")); "On Point before constraining tangent."));
return; return false;
} }
return true;
} }
void Constraint::ConstrainCubicLineTangent(Constraint *c, Entity *line, Entity *cubic) { bool Constraint::ConstrainCubicLineTangent(Constraint *c, Entity *line, Entity *cubic) {
Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(), Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
l1 = SK.GetEntity(line->point[1])->PointGetNum(); l1 = SK.GetEntity(line->point[1])->PointGetNum();
Vector as = cubic->CubicGetStartNum(), Vector as = cubic->CubicGetStartNum(),
@ -158,11 +159,12 @@ void Constraint::ConstrainCubicLineTangent(Constraint *c, Entity *line, Entity *
Error(_("The tangent cubic and line segment must share an " Error(_("The tangent cubic and line segment must share an "
"endpoint. Constrain them with Constrain -> " "endpoint. Constrain them with Constrain -> "
"On Point before constraining tangent.")); "On Point before constraining tangent."));
return; return false;
} }
return true;
} }
void Constraint::ConstrainCurveCurveTangent(Constraint *c, Entity *eA, Entity *eB) { bool Constraint::ConstrainCurveCurveTangent(Constraint *c, Entity *eA, Entity *eB) {
Vector as = eA->EndpointStart(), Vector as = eA->EndpointStart(),
af = eA->EndpointFinish(), af = eA->EndpointFinish(),
bs = eB->EndpointStart(), bs = eB->EndpointStart(),
@ -183,8 +185,9 @@ void Constraint::ConstrainCurveCurveTangent(Constraint *c, Entity *eA, Entity *e
Error(_("The curves must share an endpoint. Constrain them " Error(_("The curves must share an endpoint. Constrain them "
"with Constrain -> On Point before constraining " "with Constrain -> On Point before constraining "
"tangent.")); "tangent."));
return; return false;
} }
return true;
} }
void Constraint::MenuConstrain(Command id) { void Constraint::MenuConstrain(Command id) {
@ -417,8 +420,11 @@ void Constraint::MenuConstrain(Command id) {
c.ptA = gs.point[0]; c.ptA = gs.point[0];
// If a point is at-midpoint, then no reason to also constrain // If a point is at-midpoint, then no reason to also constrain
// it on-line; so auto-remove that. // it on-line; so auto-remove that. Handle as one undo group.
SS.UndoRemember();
DeleteAllConstraintsFor(Type::PT_ON_LINE, c.entityA, c.ptA); DeleteAllConstraintsFor(Type::PT_ON_LINE, c.entityA, c.ptA);
AddConstraint(&c, /*rememberForUndo=*/false);
break;
} else if(gs.lineSegments == 1 && gs.workplanes == 1 && gs.n == 2) { } else if(gs.lineSegments == 1 && gs.workplanes == 1 && gs.n == 2) {
c.type = Type::AT_MIDPOINT; c.type = Type::AT_MIDPOINT;
int i = SK.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0; int i = SK.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
@ -493,6 +499,7 @@ void Constraint::MenuConstrain(Command id) {
"(symmetric about workplane)\n")); "(symmetric about workplane)\n"));
return; return;
} }
// We may remove constraints so remember manually
if(c.entityA == Entity::NO_ENTITY) { if(c.entityA == Entity::NO_ENTITY) {
// Horizontal / vertical symmetry, implicit symmetry plane // Horizontal / vertical symmetry, implicit symmetry plane
// normal to the workplane // normal to the workplane
@ -514,10 +521,14 @@ void Constraint::MenuConstrain(Command id) {
if(gs.lineSegments == 1) { if(gs.lineSegments == 1) {
// If this line segment is already constrained horiz or // If this line segment is already constrained horiz or
// vert, then auto-remove that redundant constraint. // vert, then auto-remove that redundant constraint.
// Handle as one undo group.
SS.UndoRemember();
DeleteAllConstraintsFor(Type::HORIZONTAL, (gs.entity[0]), DeleteAllConstraintsFor(Type::HORIZONTAL, (gs.entity[0]),
Entity::NO_ENTITY); Entity::NO_ENTITY);
DeleteAllConstraintsFor(Type::VERTICAL, (gs.entity[0]), DeleteAllConstraintsFor(Type::VERTICAL, (gs.entity[0]),
Entity::NO_ENTITY); Entity::NO_ENTITY);
AddConstraint(&c, /*rememberForUndo=*/false);
break;
} }
} }
AddConstraint(&c); AddConstraint(&c);
@ -682,7 +693,9 @@ void Constraint::MenuConstrain(Command id) {
if(line->type == Entity::Type::ARC_OF_CIRCLE) { if(line->type == Entity::Type::ARC_OF_CIRCLE) {
swap(line, arc); swap(line, arc);
} }
ConstrainArcLineTangent(&c, line, arc); if(!ConstrainArcLineTangent(&c, line, arc)) {
return;
}
c.type = Type::ARC_LINE_TANGENT; c.type = Type::ARC_LINE_TANGENT;
c.entityA = arc->h; c.entityA = arc->h;
c.entityB = line->h; c.entityB = line->h;
@ -692,7 +705,9 @@ void Constraint::MenuConstrain(Command id) {
if(line->type == Entity::Type::CUBIC) { if(line->type == Entity::Type::CUBIC) {
swap(line, cubic); swap(line, cubic);
} }
ConstrainCubicLineTangent(&c, line, cubic); if(!ConstrainCubicLineTangent(&c, line, cubic)) {
return;
}
c.type = Type::CUBIC_LINE_TANGENT; c.type = Type::CUBIC_LINE_TANGENT;
c.entityA = cubic->h; c.entityA = cubic->h;
c.entityB = line->h; c.entityB = line->h;
@ -703,7 +718,9 @@ void Constraint::MenuConstrain(Command id) {
} }
Entity *eA = SK.GetEntity(gs.entity[0]), Entity *eA = SK.GetEntity(gs.entity[0]),
*eB = SK.GetEntity(gs.entity[1]); *eB = SK.GetEntity(gs.entity[1]);
ConstrainCurveCurveTangent(&c, eA, eB); if(!ConstrainCurveCurveTangent(&c, eA, eB)) {
return;
}
c.type = Type::CURVE_CURVE_TANGENT; c.type = Type::CURVE_CURVE_TANGENT;
c.entityA = eA->h; c.entityA = eA->h;
c.entityB = eB->h; c.entityB = eB->h;

View File

@ -318,8 +318,9 @@ void TextWindow::DescribeSelection() {
Printf(true, " at " PT_AS_STR, COSTR(p0)); Printf(true, " at " PT_AS_STR, COSTR(p0));
Vector p1 = SK.GetEntity(gs.point[1])->PointGetNum(); Vector p1 = SK.GetEntity(gs.point[1])->PointGetNum();
Printf(false, " " PT_AS_STR, COSTR(p1)); Printf(false, " " PT_AS_STR, COSTR(p1));
double d = (p1.Minus(p0)).Magnitude(); Vector dv = p1.Minus(p0);
Printf(true, " d = %Fi%s", SS.MmToString(d).c_str()); Printf(true, " d = %Fi%s", SS.MmToString(dv.Magnitude()).c_str());
Printf(false, " d(x, y, z) = " PT_AS_STR, COSTR(dv));
} else if(gs.n == 2 && gs.points == 1 && gs.circlesOrArcs == 1) { } else if(gs.n == 2 && gs.points == 1 && gs.circlesOrArcs == 1) {
Entity *ec = SK.GetEntity(gs.entity[0]); Entity *ec = SK.GetEntity(gs.entity[0]);
if(ec->type == Entity::Type::CIRCLE) { if(ec->type == Entity::Type::CIRCLE) {

View File

@ -462,7 +462,7 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const {
// Record our style for all of the Beziers that we just created. // Record our style for all of the Beziers that we just created.
for(; i < sbl->l.n; i++) { for(; i < sbl->l.n; i++) {
sbl->l[i].auxA = style.v; sbl->l[i].auxA = Style::ForEntity(h).v;
} }
} }

View File

@ -207,7 +207,6 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const Platform::Path &filename, bool
for(auto &entity : SK.entity) { for(auto &entity : SK.entity) {
Entity *e = &entity; Entity *e = &entity;
if(!e->IsVisible()) continue; if(!e->IsVisible()) continue;
if(e->construction) continue;
if(SS.exportPwlCurves || sm || fabs(SS.exportOffset) > LENGTH_EPS) if(SS.exportPwlCurves || sm || fabs(SS.exportOffset) > LENGTH_EPS)
{ {
@ -735,25 +734,22 @@ void VectorFileWriter::OutputLinesAndMesh(SBezierLoopSetSet *sblss, SMesh *sm) {
if(sblss) { if(sblss) {
SBezierLoopSet *sbls; SBezierLoopSet *sbls;
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) { for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
SBezierLoop *sbl; for(SBezierLoop *sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
sbl = sbls->l.First(); b = sbl->l.First();
if(!sbl) continue; if(!b || !Style::Exportable(b->auxA)) continue;
b = sbl->l.First();
if(!b || !Style::Exportable(b->auxA)) continue;
hStyle hs = { (uint32_t)b->auxA }; hStyle hs = { (uint32_t)b->auxA };
Style *stl = Style::Get(hs); Style *stl = Style::Get(hs);
double lineWidth = Style::WidthMm(b->auxA)*s; double lineWidth = Style::WidthMm(b->auxA)*s;
RgbaColor strokeRgb = Style::Color(hs, /*forExport=*/true); RgbaColor strokeRgb = Style::Color(hs, /*forExport=*/true);
RgbaColor fillRgb = Style::FillColor(hs, /*forExport=*/true); RgbaColor fillRgb = Style::FillColor(hs, /*forExport=*/true);
StartPath(strokeRgb, lineWidth, stl->filled, fillRgb, hs); StartPath(strokeRgb, lineWidth, stl->filled, fillRgb, hs);
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) { for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
Bezier(b); Bezier(b);
} }
FinishPath(strokeRgb, lineWidth, stl->filled, fillRgb, hs);
} }
FinishPath(strokeRgb, lineWidth, stl->filled, fillRgb, hs);
} }
} }
FinishAndCloseFile(); FinishAndCloseFile();

View File

@ -1083,17 +1083,18 @@ void SvgFileWriter::StartFile() {
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000; double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
fprintf(f, "stroke-width:%f;\r\n", sw); fprintf(f, "stroke-width:%f;\r\n", sw);
fprintf(f, "}\r\n"); fprintf(f, "}\r\n");
for(auto &style : SK.style) {
Style *s = &style;
RgbaColor strokeRgb = Style::Color(s->h, /*forExport=*/true); auto export_style = [&](hStyle hs) {
StipplePattern pattern = Style::PatternType(s->h); Style *s = Style::Get(hs);
double stippleScale = Style::StippleScaleMm(s->h); RgbaColor strokeRgb = Style::Color(hs, /*forExport=*/true);
RgbaColor fillRgb = Style::FillColor(hs, /*forExport=*/true);
StipplePattern pattern = Style::PatternType(hs);
double stippleScale = Style::StippleScaleMm(hs);
fprintf(f, ".s%x {\r\n", s->h.v); fprintf(f, ".s%x {\r\n", hs.v);
fprintf(f, "stroke:#%02x%02x%02x;\r\n", strokeRgb.red, strokeRgb.green, strokeRgb.blue); fprintf(f, "stroke:#%02x%02x%02x;\r\n", strokeRgb.red, strokeRgb.green, strokeRgb.blue);
// don't know why we have to take a half of the width // don't know why we have to take a half of the width
fprintf(f, "stroke-width:%f;\r\n", Style::WidthMm(s->h.v) / 2.0); fprintf(f, "stroke-width:%f;\r\n", Style::WidthMm(hs.v) / 2.0);
fprintf(f, "stroke-linecap:round;\r\n"); fprintf(f, "stroke-linecap:round;\r\n");
fprintf(f, "stroke-linejoin:round;\r\n"); fprintf(f, "stroke-linejoin:round;\r\n");
std::string patternStr = MakeStipplePattern(pattern, stippleScale, ',', std::string patternStr = MakeStipplePattern(pattern, stippleScale, ',',
@ -1101,8 +1102,19 @@ void SvgFileWriter::StartFile() {
if(!patternStr.empty()) { if(!patternStr.empty()) {
fprintf(f, "stroke-dasharray:%s;\r\n", patternStr.c_str()); fprintf(f, "stroke-dasharray:%s;\r\n", patternStr.c_str());
} }
fprintf(f, "fill:none;\r\n"); if(s->filled) {
fprintf(f, "fill:#%02x%02x%02x;\r\n", fillRgb.red, fillRgb.green, fillRgb.blue);
}
else {
fprintf(f, "fill:none;\r\n");
}
fprintf(f, "}\r\n"); fprintf(f, "}\r\n");
};
export_style({Style::NO_STYLE});
for(auto &style : SK.style) {
Style *s = &style;
export_style(s->h);
} }
fprintf(f, "]]></style>\r\n"); fprintf(f, "]]></style>\r\n");
} }

View File

@ -1304,6 +1304,20 @@ c:
break; break;
case Command::CONSTRUCTION: { case Command::CONSTRUCTION: {
// if we are drawing
if(SS.GW.pending.operation == Pending::DRAGGING_NEW_POINT ||
SS.GW.pending.operation == Pending::DRAGGING_NEW_LINE_POINT ||
SS.GW.pending.operation == Pending::DRAGGING_NEW_ARC_POINT ||
SS.GW.pending.operation == Pending::DRAGGING_NEW_CUBIC_POINT ||
SS.GW.pending.operation == Pending::DRAGGING_NEW_RADIUS) {
for(auto &hr : SS.GW.pending.requests) {
Request* r = SK.GetRequest(hr);
r->construction = !(r->construction);
SS.MarkGroupDirty(r->group);
}
SS.GW.Invalidate();
break;
}
SS.GW.GroupSelection(); SS.GW.GroupSelection();
if(SS.GW.gs.entities == 0) { if(SS.GW.gs.entities == 0) {
Error(_("No entities are selected. Select entities before " Error(_("No entities are selected. Select entities before "

View File

@ -270,15 +270,16 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
return; return;
} }
if(pending.operation == Pending::DRAGGING_POINTS && ctrlDown) {
SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown);
SS.extraLine.ptB = UnProjectPoint(mp);
SS.extraLine.draw = true;
}
// We're currently dragging something; so do that. But if we haven't // We're currently dragging something; so do that. But if we haven't
// painted since the last time we solved, do nothing, because there's // painted since the last time we solved, do nothing, because there's
// no sense solving a frame and not displaying it. // no sense solving a frame and not displaying it.
if(!havePainted) { if(!havePainted) {
if(pending.operation == Pending::DRAGGING_POINTS && ctrlDown) {
SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown);
SS.extraLine.ptB = UnProjectPoint(mp);
SS.extraLine.draw = true;
}
return; return;
} }
@ -319,20 +320,16 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
// Don't start dragging the position about the normal // Don't start dragging the position about the normal
// until we're a little ways out, to get a reasonable // until we're a little ways out, to get a reasonable
// reference pos // reference pos
orig.mouse = mp; qt = Quaternion::IDENTITY;
break; } else {
double theta = atan2(orig.mouse.y-orig.mouseOnButtonDown.y,
orig.mouse.x-orig.mouseOnButtonDown.x);
theta -= atan2(y-orig.mouseOnButtonDown.y,
x-orig.mouseOnButtonDown.x);
Vector gn = projRight.Cross(projUp);
qt = Quaternion::From(gn, -theta);
} }
double theta = atan2(orig.mouse.y-orig.mouseOnButtonDown.y,
orig.mouse.x-orig.mouseOnButtonDown.x);
theta -= atan2(y-orig.mouseOnButtonDown.y,
x-orig.mouseOnButtonDown.x);
Vector gn = projRight.Cross(projUp);
qt = Quaternion::From(gn, -theta);
SS.extraLine.draw = true;
SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown);
SS.extraLine.ptB = UnProjectPoint(mp);
} else { } else {
double dx = -(x - orig.mouse.x); double dx = -(x - orig.mouse.x);
double dy = -(y - orig.mouse.y); double dy = -(y - orig.mouse.y);
@ -340,7 +337,6 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
qt = Quaternion::From(projUp, -s*dx).Times( qt = Quaternion::From(projUp, -s*dx).Times(
Quaternion::From(projRight, s*dy)); Quaternion::From(projRight, s*dy));
} }
orig.mouse = mp;
// Now apply this rotation to the points being dragged. // Now apply this rotation to the points being dragged.
List<hEntity> *lhe = &(pending.points); List<hEntity> *lhe = &(pending.points);
@ -353,18 +349,18 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
p = qt.Rotate(p); p = qt.Rotate(p);
p = p.Plus(SS.extraLine.ptA); p = p.Plus(SS.extraLine.ptA);
e->PointForceTo(p); e->PointForceTo(p);
SS.MarkGroupDirtyByEntity(e->h); } else {
UpdateDraggedPoint(*he, x, y);
} }
continue; } else {
Quaternion q = e->PointGetQuaternion();
Vector p = e->PointGetNum();
q = qt.Times(q);
e->PointForceQuaternionTo(q);
// Let's rotate about the selected point; so fix up the
// translation so that that point didn't move.
e->PointForceTo(p);
} }
Quaternion q = e->PointGetQuaternion();
Vector p = e->PointGetNum();
q = qt.Times(q);
e->PointForceQuaternionTo(q);
// Let's rotate about the selected point; so fix up the
// translation so that that point didn't move.
e->PointForceTo(p);
SS.MarkGroupDirtyByEntity(e->h); SS.MarkGroupDirtyByEntity(e->h);
} }
} else { } else {
@ -373,8 +369,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
UpdateDraggedPoint(*he, x, y); UpdateDraggedPoint(*he, x, y);
SS.MarkGroupDirtyByEntity(*he); SS.MarkGroupDirtyByEntity(*he);
} }
orig.mouse = mp;
} }
orig.mouse = mp;
break; break;
case Pending::DRAGGING_NEW_CUBIC_POINT: { case Pending::DRAGGING_NEW_CUBIC_POINT: {
@ -1042,6 +1038,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my, bool shiftDown, bool ct
ConstrainPointByHovered(hr.entity(1), &mouse); ConstrainPointByHovered(hr.entity(1), &mouse);
ClearSuper(); ClearSuper();
AddToPending(hr);
pending.operation = Pending::DRAGGING_NEW_RADIUS; pending.operation = Pending::DRAGGING_NEW_RADIUS;
pending.circle = hr.entity(0); pending.circle = hr.entity(0);

View File

@ -963,16 +963,17 @@ public:
} }
void SetCursor(Cursor cursor) override { void SetCursor(Cursor cursor) override {
Gdk::CursorType gdkCursorType; std::string cursor_name;
switch(cursor) { switch(cursor) {
case Cursor::POINTER: gdkCursorType = Gdk::ARROW; break; case Cursor::POINTER: cursor_name = "default"; break;
case Cursor::HAND: gdkCursorType = Gdk::HAND1; break; case Cursor::HAND: cursor_name = "pointer"; break;
default: ssassert(false, "Unexpected cursor"); default: ssassert(false, "Unexpected cursor");
} }
auto gdkWindow = gtkWindow.get_gl_widget().get_window(); auto gdkWindow = gtkWindow.get_gl_widget().get_window();
if(gdkWindow) { if(gdkWindow) {
gdkWindow->set_cursor(Gdk::Cursor::create(gdkCursorType)); gdkWindow->set_cursor(Gdk::Cursor::create(gdkWindow->get_display(), cursor_name.c_str()));
// gdkWindow->get_display()
} }
} }

View File

@ -142,7 +142,18 @@ static std::string NegateMnemonics(const std::string &label) {
return newLabel; return newLabel;
} }
static int Clamp(int x, int a, int b) { static int Clamp(int x, int a, int b, int brda, int brdb) {
// If we are outside of an edge of the monitor
// and a "border" is requested "move in" from that edge
// by "b/brdX" (the "b" parameter is the resolution)
if((x <= a) && (brda)) {
a += b / brda; // yes "b/brda" since b is the size
}
if(((x >= b) && brdb)) {
b -= b / brdb;
}
return max(a, min(x, b)); return max(a, min(x, b));
} }
@ -1084,7 +1095,7 @@ public:
return 0; return 0;
} }
} else if(wParam == VK_ESCAPE) { } else if(wParam == VK_ESCAPE) {
sscheck(SendMessageW(hWindow, msg, wParam, lParam)); window->HideEditor();
return 0; return 0;
} }
} }
@ -1218,11 +1229,14 @@ public:
sscheck(GetMonitorInfo(MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST), &mi)); sscheck(GetMonitorInfo(MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST), &mi));
// If it somehow ended up off-screen, then put it back. // If it somehow ended up off-screen, then put it back.
// and make it visible by at least this portion of the scrren
const LONG movein = 40;
RECT mrc = mi.rcMonitor; RECT mrc = mi.rcMonitor;
rc.left = Clamp(rc.left, mrc.left, mrc.right); rc.left = Clamp(rc.left, mrc.left, mrc.right, 0, movein);
rc.right = Clamp(rc.right, mrc.left, mrc.right); rc.right = Clamp(rc.right, mrc.left, mrc.right, movein, 0);
rc.top = Clamp(rc.top, mrc.top, mrc.bottom); rc.top = Clamp(rc.top, mrc.top, mrc.bottom, 0, movein);
rc.bottom = Clamp(rc.bottom, mrc.top, mrc.bottom); rc.bottom = Clamp(rc.bottom, mrc.top, mrc.bottom, movein, 0);
// And make sure the minimum size is respected. (We can freeze a size smaller // And make sure the minimum size is respected. (We can freeze a size smaller
// than minimum size if the DPI changed between runs.) // than minimum size if the DPI changed between runs.)

View File

@ -790,9 +790,9 @@ public:
static hConstraint TryConstrain(Constraint::Type type, hEntity ptA, hEntity ptB, static hConstraint TryConstrain(Constraint::Type type, hEntity ptA, hEntity ptB,
hEntity entityA, hEntity entityB = Entity::NO_ENTITY, hEntity entityA, hEntity entityB = Entity::NO_ENTITY,
bool other = false, bool other2 = false); bool other = false, bool other2 = false);
static void ConstrainArcLineTangent(Constraint *c, Entity *line, Entity *arc); static bool ConstrainArcLineTangent(Constraint *c, Entity *line, Entity *arc);
static void ConstrainCubicLineTangent(Constraint *c, Entity *line, Entity *cubic); static bool ConstrainCubicLineTangent(Constraint *c, Entity *line, Entity *cubic);
static void ConstrainCurveCurveTangent(Constraint *c, Entity *eA, Entity *eB); static bool ConstrainCurveCurveTangent(Constraint *c, Entity *eA, Entity *eB);
}; };
class hEquation { class hEquation {
@ -883,6 +883,7 @@ public:
RgbaColor color; RgbaColor color;
double width; double width;
int zIndex; int zIndex;
bool exportable;
} Default; } Default;
static const Default Defaults[]; static const Default Defaults[];
@ -890,6 +891,7 @@ public:
static std::string CnfWidth(const std::string &prefix); static std::string CnfWidth(const std::string &prefix);
static std::string CnfTextHeight(const std::string &prefix); static std::string CnfTextHeight(const std::string &prefix);
static std::string CnfPrefixToName(const std::string &prefix); static std::string CnfPrefixToName(const std::string &prefix);
static std::string CnfExportable(const std::string &prefix);
static void CreateAllDefaultStyles(); static void CreateAllDefaultStyles();
static void CreateDefaultStyle(hStyle h); static void CreateDefaultStyle(hStyle h);

View File

@ -8,22 +8,22 @@
#include "solvespace.h" #include "solvespace.h"
const Style::Default Style::Defaults[] = { const Style::Default Style::Defaults[] = {
{ { ACTIVE_GRP }, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, 4 }, { { ACTIVE_GRP }, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, 4, true },
{ { CONSTRUCTION }, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, 0 }, { { CONSTRUCTION }, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, 0, false },
{ { INACTIVE_GRP }, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, 3 }, { { INACTIVE_GRP }, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, 3, true },
{ { DATUM }, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, 0 }, { { DATUM }, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, 0, true },
{ { SOLID_EDGE }, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, 2 }, { { SOLID_EDGE }, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, 2, true },
{ { CONSTRAINT }, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, 0 }, { { CONSTRAINT }, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, 0, true },
{ { SELECTED }, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, 0 }, { { SELECTED }, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, 0, true },
{ { HOVERED }, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, 0 }, { { HOVERED }, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, 0, true },
{ { CONTOUR_FILL }, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, 0 }, { { CONTOUR_FILL }, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, 0, true },
{ { NORMALS }, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, 0 }, { { NORMALS }, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, 0, true },
{ { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 3.0, 0 }, { { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 3.0, 0, true },
{ { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0 }, { { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0, true },
{ { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0 }, { { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0, true },
{ { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 1.0, 1 }, { { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 1.0, 1, true },
{ { OUTLINE }, "Outline", RGBf(0.8, 0.8, 0.8), 3.0, 5 }, { { OUTLINE }, "Outline", RGBf(0.8, 0.8, 0.8), 3.0, 5, true },
{ { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0 } { { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0, true }
}; };
std::string Style::CnfColor(const std::string &prefix) { std::string Style::CnfColor(const std::string &prefix) {
@ -35,6 +35,9 @@ std::string Style::CnfWidth(const std::string &prefix) {
std::string Style::CnfTextHeight(const std::string &prefix) { std::string Style::CnfTextHeight(const std::string &prefix) {
return "Style_" + prefix + "_TextHeight"; return "Style_" + prefix + "_TextHeight";
} }
std::string Style::CnfExportable(const std::string &prefix) {
return "Style_" + prefix + "_Exportable";
}
std::string Style::CnfPrefixToName(const std::string &prefix) { std::string Style::CnfPrefixToName(const std::string &prefix) {
std::string name = "#def-"; std::string name = "#def-";
@ -97,7 +100,9 @@ void Style::FillDefaultStyle(Style *s, const Default *d, bool factory) {
s->textOrigin = TextOrigin::NONE; s->textOrigin = TextOrigin::NONE;
s->textAngle = 0; s->textAngle = 0;
s->visible = true; s->visible = true;
s->exportable = true; s->exportable = (factory)
? d->exportable
: settings->ThawBool(CnfExportable(d->cnfPrefix), d->exportable);
s->filled = false; s->filled = false;
s->fillColor = RGBf(0.3, 0.3, 0.3); s->fillColor = RGBf(0.3, 0.3, 0.3);
s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? StipplePattern::DASH s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? StipplePattern::DASH
@ -121,6 +126,7 @@ void Style::FreezeDefaultStyles(Platform::SettingsRef settings) {
settings->FreezeColor(CnfColor(d->cnfPrefix), Color(d->h)); settings->FreezeColor(CnfColor(d->cnfPrefix), Color(d->h));
settings->FreezeFloat(CnfWidth(d->cnfPrefix), (float)Width(d->h)); settings->FreezeFloat(CnfWidth(d->cnfPrefix), (float)Width(d->h));
settings->FreezeFloat(CnfTextHeight(d->cnfPrefix), (float)TextHeight(d->h)); settings->FreezeFloat(CnfTextHeight(d->cnfPrefix), (float)TextHeight(d->h));
settings->FreezeBool(CnfExportable(d->cnfPrefix), Exportable(d->h.v));
} }
} }
@ -850,17 +856,19 @@ void TextWindow::ShowStyleInfo() {
((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::TOP) ? RADIO_TRUE : RADIO_FALSE); ((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::TOP) ? RADIO_TRUE : RADIO_FALSE);
} }
if(s->h.v >= Style::FIRST_CUSTOM) { Printf(false, "");
Printf(false, "");
if(s->h.v >= Style::FIRST_CUSTOM) {
Printf(false, " %Fd%D%f%Lv%s show these objects on screen%E", Printf(false, " %Fd%D%f%Lv%s show these objects on screen%E",
s->h.v, &ScreenChangeStyleYesNo, s->h.v, &ScreenChangeStyleYesNo,
s->visible ? CHECK_TRUE : CHECK_FALSE); s->visible ? CHECK_TRUE : CHECK_FALSE);
}
Printf(false, " %Fd%D%f%Le%s export these objects%E", Printf(false, " %Fd%D%f%Le%s export these objects%E",
s->h.v, &ScreenChangeStyleYesNo, s->h.v, &ScreenChangeStyleYesNo,
s->exportable ? CHECK_TRUE : CHECK_FALSE); s->exportable ? CHECK_TRUE : CHECK_FALSE);
if(s->h.v >= Style::FIRST_CUSTOM) {
Printf(false, ""); Printf(false, "");
Printf(false, "To assign lines or curves to this style,"); Printf(false, "To assign lines or curves to this style,");
Printf(false, "right-click them on the drawing."); Printf(false, "right-click them on the drawing.");