2016-04-13 08:43:06 +00:00
|
|
|
#include "solvespace.h"
|
|
|
|
#include "libdxfrw.h"
|
2016-05-07 05:17:23 +00:00
|
|
|
#include "libdwgr.h"
|
2016-04-13 08:43:06 +00:00
|
|
|
|
|
|
|
namespace SolveSpace {
|
|
|
|
|
|
|
|
static std::string ToUpper(std::string str) {
|
|
|
|
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
class DxfImport : public DRW_Interface {
|
2016-04-13 08:43:06 +00:00
|
|
|
public:
|
|
|
|
Vector blockX;
|
|
|
|
Vector blockY;
|
2016-11-19 10:19:08 +00:00
|
|
|
Vector blockZ;
|
2016-04-13 08:43:06 +00:00
|
|
|
Vector blockT;
|
|
|
|
|
|
|
|
void invertXTransform() {
|
|
|
|
blockX.x = -blockX.x;
|
|
|
|
blockY.x = -blockY.x;
|
|
|
|
blockT.x = -blockT.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
void multBlockTransform(double x, double y, double sx, double sy, double angle) {
|
|
|
|
Vector oldX = blockX;
|
|
|
|
Vector oldY = blockY;
|
|
|
|
Vector oldT = blockT;
|
|
|
|
|
|
|
|
Vector newX = Vector::From(sx, 0.0, 0.0).RotatedAbout(Vector::From(0.0, 0.0, 1.0), angle);
|
|
|
|
Vector newY = Vector::From(0.0, sy, 0.0).RotatedAbout(Vector::From(0.0, 0.0, 1.0), angle);
|
|
|
|
Vector newT = Vector::From(x, y, 0.0);
|
|
|
|
|
|
|
|
blockX = oldX.ScaledBy(newX.x).Plus(
|
|
|
|
oldY.ScaledBy(newX.y));
|
|
|
|
|
|
|
|
blockY = oldX.ScaledBy(newY.x).Plus(
|
|
|
|
oldY.ScaledBy(newY.y));
|
|
|
|
|
|
|
|
blockT = oldX.ScaledBy(newT.x).Plus(
|
|
|
|
oldY.ScaledBy(newT.y)).Plus(oldT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void clearBlockTransform() {
|
|
|
|
blockX = Vector::From(1.0, 0.0, 0.0);
|
|
|
|
blockY = Vector::From(0.0, 1.0, 0.0);
|
2016-11-19 10:19:08 +00:00
|
|
|
blockZ = Vector::From(0.0, 0.0, 1.0);
|
2016-04-13 08:43:06 +00:00
|
|
|
blockT = Vector::From(0.0, 0.0, 0.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector blockTransform(Vector v) {
|
|
|
|
Vector r = blockT;
|
|
|
|
r = r.Plus(blockX.ScaledBy(v.x));
|
|
|
|
r = r.Plus(blockY.ScaledBy(v.y));
|
2016-11-19 10:19:08 +00:00
|
|
|
r = r.Plus(blockZ.ScaledBy(v.z));
|
2016-04-13 08:43:06 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void blockTransformArc(Vector *c, Vector *p0, Vector *p1) {
|
|
|
|
bool oldSign = p0->Minus(*c).Cross(p1->Minus(*c)).z > 0.0;
|
|
|
|
|
|
|
|
*c = blockTransform(*c);
|
|
|
|
*p0 = blockTransform(*p0);
|
|
|
|
*p1 = blockTransform(*p1);
|
|
|
|
|
|
|
|
bool newSign = p0->Minus(*c).Cross(p1->Minus(*c)).z > 0.0;
|
|
|
|
if(oldSign != newSign) std::swap(*p0, *p1);
|
|
|
|
}
|
|
|
|
|
2016-04-26 00:47:02 +00:00
|
|
|
Vector toVector(const DRW_Coord &c, bool transform = true) {
|
2016-11-19 10:19:08 +00:00
|
|
|
Vector result = Vector::From(c.x, c.y, c.z);
|
2016-04-26 00:47:02 +00:00
|
|
|
if(transform) return blockTransform(result);
|
|
|
|
return result;
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Vector toVector(const DRW_Vertex2D &c) {
|
|
|
|
Vector result = Vector::From(c.x, c.y, 0.0);
|
|
|
|
return blockTransform(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector toVector(const DRW_Vertex &c) {
|
2016-11-19 10:19:08 +00:00
|
|
|
Vector result = Vector::From(c.basePoint.x, c.basePoint.y, c.basePoint.z);
|
2016-04-13 08:43:06 +00:00
|
|
|
return blockTransform(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
double angleTo(Vector v0, Vector v1) {
|
|
|
|
Vector d = v1.Minus(v0);
|
|
|
|
double a = atan2(d.y, d.x);
|
|
|
|
return M_PI + remainder(a - M_PI, 2 * M_PI);
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector polar(double radius, double angle) {
|
|
|
|
return Vector::From(radius * cos(angle), radius * sin(angle), 0.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
hRequest createBulge(Vector p0, Vector p1, double bulge) {
|
|
|
|
bool reversed = bulge < 0.0;
|
|
|
|
double alpha = atan(bulge) * 4.0;
|
|
|
|
|
|
|
|
Vector middle = p1.Plus(p0).ScaledBy(0.5);
|
|
|
|
double dist = p1.Minus(p0).Magnitude() / 2.0;
|
|
|
|
double angle = angleTo(p0, p1);
|
|
|
|
|
|
|
|
// alpha can't be 0.0 at this point
|
|
|
|
double radius = fabs(dist / sin(alpha / 2.0));
|
|
|
|
double wu = fabs(radius * radius - dist * dist);
|
|
|
|
double h = sqrt(wu);
|
|
|
|
|
|
|
|
if(bulge > 0.0) {
|
|
|
|
angle += M_PI_2;
|
|
|
|
} else {
|
|
|
|
angle -= M_PI_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(alpha) > M_PI) {
|
|
|
|
h *= -1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector center = polar(h, angle);
|
|
|
|
center = center.Plus(middle);
|
|
|
|
|
|
|
|
if(reversed) std::swap(p0, p1);
|
|
|
|
blockTransformArc(¢er, &p0, &p1);
|
|
|
|
|
2016-05-25 12:08:19 +00:00
|
|
|
hRequest hr = SS.GW.AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false);
|
2016-04-13 08:43:06 +00:00
|
|
|
SK.GetEntity(hr.entity(1))->PointForceTo(center);
|
|
|
|
SK.GetEntity(hr.entity(2))->PointForceTo(p0);
|
|
|
|
SK.GetEntity(hr.entity(3))->PointForceTo(p1);
|
2016-04-26 00:47:02 +00:00
|
|
|
processPoint(hr.entity(1));
|
|
|
|
processPoint(hr.entity(2));
|
|
|
|
processPoint(hr.entity(3));
|
2016-04-13 08:43:06 +00:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Block {
|
|
|
|
std::vector<std::unique_ptr<DRW_Entity>> entities;
|
|
|
|
DRW_Block data;
|
|
|
|
};
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
bool asConstruction = false;
|
2016-04-13 08:43:06 +00:00
|
|
|
unsigned unknownEntities = 0;
|
|
|
|
std::map<std::string, hStyle> styles;
|
|
|
|
std::map<std::string, Block> blocks;
|
|
|
|
std::map<std::string, DRW_Layer> layers;
|
|
|
|
Block *readBlock = NULL;
|
|
|
|
const DRW_Insert *insertInsert = NULL;
|
|
|
|
|
|
|
|
template<class T>
|
|
|
|
bool addPendingBlockEntity(const T &e) {
|
|
|
|
if(readBlock == NULL) return false;
|
|
|
|
readBlock->entities.emplace_back(new T(e));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addEntity(DRW_Entity *e) {
|
|
|
|
switch(e->eType) {
|
|
|
|
case DRW::POINT:
|
|
|
|
addPoint(*static_cast<DRW_Point *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::LINE:
|
|
|
|
addLine(*static_cast<DRW_Line *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::ARC:
|
|
|
|
addArc(*static_cast<DRW_Arc *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::CIRCLE:
|
|
|
|
addCircle(*static_cast<DRW_Circle *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::POLYLINE:
|
|
|
|
addPolyline(*static_cast<DRW_Polyline *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::LWPOLYLINE:
|
|
|
|
addLWPolyline(*static_cast<DRW_LWPolyline *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::SPLINE:
|
|
|
|
addSpline(static_cast<DRW_Spline *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::INSERT:
|
|
|
|
addInsert(*static_cast<DRW_Insert *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::TEXT:
|
|
|
|
addText(*static_cast<DRW_Text *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::MTEXT:
|
|
|
|
addMText(*static_cast<DRW_MText *>(e));
|
|
|
|
break;
|
2016-04-26 00:47:02 +00:00
|
|
|
case DRW::DIMALIGNED:
|
|
|
|
addDimAlign(static_cast<DRW_DimAligned *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMLINEAR:
|
|
|
|
addDimLinear(static_cast<DRW_DimLinear *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMRADIAL:
|
|
|
|
addDimRadial(static_cast<DRW_DimRadial *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMDIAMETRIC:
|
|
|
|
addDimDiametric(static_cast<DRW_DimDiametric *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMANGULAR:
|
|
|
|
addDimAngular(static_cast<DRW_DimAngular *>(e));
|
|
|
|
break;
|
2016-04-13 08:43:06 +00:00
|
|
|
default:
|
|
|
|
unknownEntities++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
Style::TextOrigin dxfAlignToOrigin(DRW_Text::HAlign alignH, DRW_Text::VAlign alignV) {
|
|
|
|
uint32_t origin = 0;
|
2016-04-13 08:43:06 +00:00
|
|
|
switch(alignH) {
|
|
|
|
case DRW_Text::HLeft:
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
origin |= (uint32_t)Style::TextOrigin::LEFT;
|
2016-04-13 08:43:06 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DRW_Text::HMiddle:
|
|
|
|
case DRW_Text::HCenter:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DRW_Text::HRight:
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
origin |= (uint32_t)Style::TextOrigin::RIGHT;
|
2016-04-13 08:43:06 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DRW_Text::HAligned:
|
|
|
|
case DRW_Text::HFit:
|
|
|
|
default:
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
origin |= (uint32_t)Style::TextOrigin::LEFT;
|
2016-04-13 08:43:06 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(alignV) {
|
|
|
|
case DRW_Text::VBaseLine:
|
|
|
|
case DRW_Text::VBottom:
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
origin |= (uint32_t)Style::TextOrigin::BOT;
|
2016-04-13 08:43:06 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DRW_Text::VMiddle:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DRW_Text::VTop:
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
origin |= (uint32_t)Style::TextOrigin::TOP;
|
2016-04-13 08:43:06 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
origin |= (uint32_t)Style::TextOrigin::BOT;
|
2016-04-13 08:43:06 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
return (Style::TextOrigin)origin;
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DRW_Layer *getSourceLayer(const DRW_Entity *e) {
|
|
|
|
DRW_Layer *layer = NULL;
|
|
|
|
if(insertInsert != NULL) {
|
|
|
|
std::string l = insertInsert->layer;
|
|
|
|
auto bi = layers.find(l);
|
|
|
|
if(bi != layers.end()) layer = &bi->second;
|
|
|
|
} else {
|
|
|
|
std::string l = e->layer;
|
|
|
|
auto bi = layers.find(l);
|
|
|
|
if(bi != layers.end()) layer = &bi->second;
|
|
|
|
}
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
int getColor(const DRW_Entity *e) {
|
|
|
|
int col = e->color;
|
|
|
|
if(col == DRW::ColorByBlock) {
|
|
|
|
if(insertInsert != NULL) {
|
|
|
|
col = insertInsert->color;
|
|
|
|
} else {
|
|
|
|
col = 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(col == DRW::ColorByLayer) {
|
|
|
|
DRW_Layer *layer = getSourceLayer(e);
|
|
|
|
if(layer != NULL) {
|
|
|
|
col = layer->color;
|
|
|
|
} else {
|
|
|
|
col = 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
|
|
|
|
DRW_LW_Conv::lineWidth getLineWidth(const DRW_Entity *e) {
|
|
|
|
DRW_LW_Conv::lineWidth result = e->lWeight;
|
|
|
|
if(result == DRW_LW_Conv::widthByBlock) {
|
|
|
|
if(insertInsert != NULL) {
|
|
|
|
result = insertInsert->lWeight;
|
|
|
|
} else {
|
|
|
|
result = DRW_LW_Conv::widthDefault;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(result == DRW_LW_Conv::widthByLayer) {
|
|
|
|
DRW_Layer *layer = getSourceLayer(e);
|
|
|
|
if(layer != NULL) {
|
|
|
|
result = layer->lWeight;
|
|
|
|
} else {
|
|
|
|
result = DRW_LW_Conv::widthDefault;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getLineType(const DRW_Entity *e) {
|
|
|
|
std::string result = e->lineType;
|
|
|
|
if(result == "BYBLOCK") {
|
|
|
|
if(insertInsert != NULL) {
|
|
|
|
result = ToUpper(insertInsert->lineType);
|
|
|
|
} else {
|
|
|
|
result = "CONTINUOUS";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(result == "BYLAYER") {
|
|
|
|
DRW_Layer *layer = getSourceLayer(e);
|
|
|
|
if(layer != NULL) {
|
|
|
|
result = ToUpper(layer->lineType);
|
|
|
|
} else {
|
|
|
|
result = "CONTINUOUS";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-04-26 00:47:02 +00:00
|
|
|
hStyle invisibleStyle() {
|
|
|
|
std::string id = "@dxf-invisible";
|
|
|
|
|
|
|
|
auto si = styles.find(id);
|
|
|
|
if(si != styles.end()) {
|
|
|
|
return si->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
hStyle hs = { Style::CreateCustomStyle(/*rememberForUndo=*/false) };
|
|
|
|
Style *s = Style::Get(hs);
|
|
|
|
s->name = id;
|
|
|
|
s->visible = false;
|
|
|
|
|
|
|
|
styles.emplace(id, hs);
|
|
|
|
return hs;
|
|
|
|
}
|
|
|
|
|
2016-04-13 08:43:06 +00:00
|
|
|
hStyle styleFor(const DRW_Entity *e) {
|
|
|
|
// Color.
|
2019-09-10 04:07:27 +00:00
|
|
|
//! @todo which color to choose: index or RGB one?
|
2016-04-13 08:43:06 +00:00
|
|
|
int col = getColor(e);
|
|
|
|
RgbaColor c = RgbaColor::From(DRW::dxfColors[col][0],
|
|
|
|
DRW::dxfColors[col][1],
|
|
|
|
DRW::dxfColors[col][2]);
|
|
|
|
|
|
|
|
// Line width.
|
|
|
|
DRW_LW_Conv::lineWidth lw = getLineWidth(e);
|
|
|
|
double width = DRW_LW_Conv::lineWidth2dxfInt(e->lWeight) / 100.0;
|
|
|
|
if(width < 0.0) width = 1.0;
|
|
|
|
|
|
|
|
// Line stipple.
|
2019-09-10 04:07:27 +00:00
|
|
|
//! @todo Probably, we can load default autocad patterns and match it with ours.
|
2016-04-13 08:43:06 +00:00
|
|
|
std::string lineType = getLineType(e);
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
StipplePattern stipple = StipplePattern::CONTINUOUS;
|
|
|
|
for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
|
|
|
|
StipplePattern st = (StipplePattern)i;
|
|
|
|
if(lineType == DxfFileWriter::lineTypeName(st)) {
|
|
|
|
stipple = st;
|
2016-04-13 08:43:06 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Text properties.
|
|
|
|
DRW_Text::HAlign alignH = DRW_Text::HLeft;
|
|
|
|
DRW_Text::VAlign alignV = DRW_Text::VBaseLine;
|
|
|
|
double textAngle = 0.0;
|
|
|
|
double textHeight = Style::DefaultTextHeight();
|
|
|
|
|
|
|
|
if(e->eType == DRW::TEXT || e->eType == DRW::MTEXT) {
|
|
|
|
const DRW_Text *text = static_cast<const DRW_Text *>(e);
|
|
|
|
alignH = text->alignH;
|
|
|
|
alignV = text->alignV;
|
|
|
|
textHeight = text->height;
|
|
|
|
textAngle = text->angle;
|
|
|
|
// I have no idea why, but works
|
|
|
|
if(alignH == DRW_Text::HMiddle) {
|
|
|
|
alignV = DRW_Text::VMiddle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unique identifier based on style properties.
|
|
|
|
std::string id = "@dxf";
|
|
|
|
if(lw != DRW_LW_Conv::widthDefault)
|
|
|
|
id += ssprintf("-w%.4g", width);
|
|
|
|
if(lineType != "CONTINUOUS")
|
|
|
|
id += ssprintf("-%s", lineType.c_str());
|
|
|
|
if(c.red != 0 || c.green != 0 || c.blue != 0)
|
|
|
|
id += ssprintf("-#%02x%02x%02x", c.red, c.green, c.blue);
|
|
|
|
if(textHeight != Style::DefaultTextHeight())
|
|
|
|
id += ssprintf("-h%.4g", textHeight);
|
|
|
|
if(textAngle != 0.0)
|
|
|
|
id += ssprintf("-a%.5g", textAngle);
|
|
|
|
if(alignH != DRW_Text::HLeft)
|
|
|
|
id += ssprintf("-oh%d", alignH);
|
|
|
|
if(alignV != DRW_Text::VBaseLine)
|
|
|
|
id += ssprintf("-ov%d", alignV);
|
|
|
|
|
|
|
|
auto si = styles.find(id);
|
|
|
|
if(si != styles.end()) {
|
|
|
|
return si->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
hStyle hs = { Style::CreateCustomStyle(/*rememberForUndo=*/false) };
|
|
|
|
Style *s = Style::Get(hs);
|
|
|
|
if(lw != DRW_LW_Conv::widthDefault) {
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
s->widthAs = Style::UnitsAs::MM;
|
2016-04-13 08:43:06 +00:00
|
|
|
s->width = width;
|
|
|
|
s->stippleScale = 1.0 + width * 2.0;
|
|
|
|
}
|
|
|
|
s->name = id;
|
|
|
|
s->stippleType = stipple;
|
|
|
|
if(c.red != 0 || c.green != 0 || c.blue != 0) s->color = c;
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
s->textHeightAs = Style::UnitsAs::MM;
|
2016-04-13 08:43:06 +00:00
|
|
|
s->textHeight = textHeight;
|
|
|
|
s->textAngle = textAngle;
|
|
|
|
s->textOrigin = dxfAlignToOrigin(alignH, alignV);
|
|
|
|
|
|
|
|
styles.emplace(id, hs);
|
|
|
|
return hs;
|
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
void configureRequest(hRequest hr, hStyle hs) {
|
2016-04-13 08:43:06 +00:00
|
|
|
Request *r = SK.GetRequest(hr);
|
2016-11-19 10:19:08 +00:00
|
|
|
r->construction = asConstruction;
|
2016-04-13 08:43:06 +00:00
|
|
|
r->style = hs;
|
|
|
|
}
|
|
|
|
|
2016-04-26 00:47:02 +00:00
|
|
|
struct VectorHash {
|
|
|
|
size_t operator()(const Vector &v) const {
|
|
|
|
static const size_t size = std::numeric_limits<size_t>::max() / 2 - 1;
|
|
|
|
static const double eps = (4.0 * LENGTH_EPS);
|
|
|
|
|
|
|
|
double x = fabs(v.x) / eps;
|
|
|
|
double y = fabs(v.y) / eps;
|
|
|
|
|
|
|
|
size_t xs = size_t(fmod(x, double(size)));
|
|
|
|
size_t ys = size_t(fmod(y, double(size)));
|
|
|
|
|
|
|
|
return ys * size + xs;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VectorPred {
|
|
|
|
bool operator()(Vector a, Vector b) const {
|
|
|
|
return a.Equals(b, LENGTH_EPS);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unordered_map<Vector, hEntity, VectorHash, VectorPred> points;
|
|
|
|
|
|
|
|
void processPoint(hEntity he, bool constrain = true) {
|
|
|
|
Entity *e = SK.GetEntity(he);
|
|
|
|
Vector pos = e->PointGetNum();
|
|
|
|
hEntity p = findPoint(pos);
|
2019-07-09 14:44:57 +00:00
|
|
|
if(p == he) return;
|
|
|
|
if(p != Entity::NO_ENTITY) {
|
2016-04-26 00:47:02 +00:00
|
|
|
if(constrain) {
|
|
|
|
Constraint::ConstrainCoincident(he, p);
|
|
|
|
}
|
|
|
|
// We don't add point because we already
|
|
|
|
// have point in this position
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
points.emplace(pos, he);
|
|
|
|
}
|
|
|
|
|
|
|
|
hEntity findPoint(const Vector &p) {
|
|
|
|
auto it = points.find(p);
|
|
|
|
if(it == points.end()) return Entity::NO_ENTITY;
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
hEntity createOrGetPoint(const Vector &p) {
|
|
|
|
hEntity he = findPoint(p);
|
2019-07-09 14:44:57 +00:00
|
|
|
if(he != Entity::NO_ENTITY) return he;
|
2016-04-26 00:47:02 +00:00
|
|
|
|
2016-05-25 12:08:19 +00:00
|
|
|
hRequest hr = SS.GW.AddRequest(Request::Type::DATUM_POINT, /*rememberForUndo=*/false);
|
2016-04-26 00:47:02 +00:00
|
|
|
he = hr.entity(0);
|
|
|
|
SK.GetEntity(he)->PointForceTo(p);
|
|
|
|
points.emplace(p, he);
|
|
|
|
return he;
|
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
hEntity createLine(Vector p0, Vector p1, hStyle style, bool constrainHV = false) {
|
2016-04-26 00:47:02 +00:00
|
|
|
if(p0.Equals(p1)) return Entity::NO_ENTITY;
|
2016-05-25 12:08:19 +00:00
|
|
|
hRequest hr = SS.GW.AddRequest(Request::Type::LINE_SEGMENT, /*rememberForUndo=*/false);
|
2016-04-26 00:47:02 +00:00
|
|
|
SK.GetEntity(hr.entity(1))->PointForceTo(p0);
|
|
|
|
SK.GetEntity(hr.entity(2))->PointForceTo(p1);
|
|
|
|
processPoint(hr.entity(1));
|
|
|
|
processPoint(hr.entity(2));
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
if(constrainHV && SS.GW.LockedInWorkplane()) {
|
Enable exhaustive switch coverage warnings as an error, and use them.
Specifically, this enables -Wswitch=error on GCC/Clang and its MSVC
equivalent; the exact way it is handled varies slightly, but what
they all have in common is that in a switch statement over an
enumeration, any enumerand that is not explicitly (via case:) or
implicitly (via default:) handled in the switch triggers an error.
Moreover, we also change the switch statements in three ways:
* Switch statements that ought to be extended every time a new
enumerand is added (e.g. Entity::DrawOrGetDistance(), are changed
to explicitly list every single enumerand, and not have a
default: branch.
Note that the assertions are kept because it is legal for
a enumeration to have a value unlike any of its defined
enumerands, and we can e.g. read garbage from a file, or
an uninitialized variable. This requires some rearranging if
a default: branch is undesired.
* Switch statements that ought to only ever see a few select
enumerands, are changed to always assert in the default: branch.
* Switch statements that do something meaningful for a few
enumerands, and ignore everything else, are changed to do nothing
in a default: branch, under the assumption that changing them
every time an enumerand is added or removed would just result
in noise and catch no bugs.
This commit also removes the {Request,Entity,Constraint}::UNKNOWN and
Entity::DATUM_POINT enumerands, as those were just fancy names for
zeroes. They mess up switch exhaustiveness checks and most of the time
were not the best way to implement what they did anyway.
2016-05-25 06:55:50 +00:00
|
|
|
bool hasConstraint = false;
|
|
|
|
Constraint::Type cType;
|
2016-04-26 00:47:02 +00:00
|
|
|
if(fabs(p0.x - p1.x) < LENGTH_EPS) {
|
Enable exhaustive switch coverage warnings as an error, and use them.
Specifically, this enables -Wswitch=error on GCC/Clang and its MSVC
equivalent; the exact way it is handled varies slightly, but what
they all have in common is that in a switch statement over an
enumeration, any enumerand that is not explicitly (via case:) or
implicitly (via default:) handled in the switch triggers an error.
Moreover, we also change the switch statements in three ways:
* Switch statements that ought to be extended every time a new
enumerand is added (e.g. Entity::DrawOrGetDistance(), are changed
to explicitly list every single enumerand, and not have a
default: branch.
Note that the assertions are kept because it is legal for
a enumeration to have a value unlike any of its defined
enumerands, and we can e.g. read garbage from a file, or
an uninitialized variable. This requires some rearranging if
a default: branch is undesired.
* Switch statements that ought to only ever see a few select
enumerands, are changed to always assert in the default: branch.
* Switch statements that do something meaningful for a few
enumerands, and ignore everything else, are changed to do nothing
in a default: branch, under the assumption that changing them
every time an enumerand is added or removed would just result
in noise and catch no bugs.
This commit also removes the {Request,Entity,Constraint}::UNKNOWN and
Entity::DATUM_POINT enumerands, as those were just fancy names for
zeroes. They mess up switch exhaustiveness checks and most of the time
were not the best way to implement what they did anyway.
2016-05-25 06:55:50 +00:00
|
|
|
hasConstraint = true;
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
cType = Constraint::Type::VERTICAL;
|
Enable exhaustive switch coverage warnings as an error, and use them.
Specifically, this enables -Wswitch=error on GCC/Clang and its MSVC
equivalent; the exact way it is handled varies slightly, but what
they all have in common is that in a switch statement over an
enumeration, any enumerand that is not explicitly (via case:) or
implicitly (via default:) handled in the switch triggers an error.
Moreover, we also change the switch statements in three ways:
* Switch statements that ought to be extended every time a new
enumerand is added (e.g. Entity::DrawOrGetDistance(), are changed
to explicitly list every single enumerand, and not have a
default: branch.
Note that the assertions are kept because it is legal for
a enumeration to have a value unlike any of its defined
enumerands, and we can e.g. read garbage from a file, or
an uninitialized variable. This requires some rearranging if
a default: branch is undesired.
* Switch statements that ought to only ever see a few select
enumerands, are changed to always assert in the default: branch.
* Switch statements that do something meaningful for a few
enumerands, and ignore everything else, are changed to do nothing
in a default: branch, under the assumption that changing them
every time an enumerand is added or removed would just result
in noise and catch no bugs.
This commit also removes the {Request,Entity,Constraint}::UNKNOWN and
Entity::DATUM_POINT enumerands, as those were just fancy names for
zeroes. They mess up switch exhaustiveness checks and most of the time
were not the best way to implement what they did anyway.
2016-05-25 06:55:50 +00:00
|
|
|
} else if(fabs(p0.y - p1.y) < LENGTH_EPS) {
|
|
|
|
hasConstraint = true;
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
cType = Constraint::Type::HORIZONTAL;
|
2016-04-26 00:47:02 +00:00
|
|
|
}
|
Enable exhaustive switch coverage warnings as an error, and use them.
Specifically, this enables -Wswitch=error on GCC/Clang and its MSVC
equivalent; the exact way it is handled varies slightly, but what
they all have in common is that in a switch statement over an
enumeration, any enumerand that is not explicitly (via case:) or
implicitly (via default:) handled in the switch triggers an error.
Moreover, we also change the switch statements in three ways:
* Switch statements that ought to be extended every time a new
enumerand is added (e.g. Entity::DrawOrGetDistance(), are changed
to explicitly list every single enumerand, and not have a
default: branch.
Note that the assertions are kept because it is legal for
a enumeration to have a value unlike any of its defined
enumerands, and we can e.g. read garbage from a file, or
an uninitialized variable. This requires some rearranging if
a default: branch is undesired.
* Switch statements that ought to only ever see a few select
enumerands, are changed to always assert in the default: branch.
* Switch statements that do something meaningful for a few
enumerands, and ignore everything else, are changed to do nothing
in a default: branch, under the assumption that changing them
every time an enumerand is added or removed would just result
in noise and catch no bugs.
This commit also removes the {Request,Entity,Constraint}::UNKNOWN and
Entity::DATUM_POINT enumerands, as those were just fancy names for
zeroes. They mess up switch exhaustiveness checks and most of the time
were not the best way to implement what they did anyway.
2016-05-25 06:55:50 +00:00
|
|
|
if(hasConstraint) {
|
2016-04-26 00:47:02 +00:00
|
|
|
Constraint::Constrain(
|
|
|
|
cType,
|
|
|
|
Entity::NO_ENTITY,
|
|
|
|
Entity::NO_ENTITY,
|
|
|
|
hr.entity(0)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
configureRequest(hr, style);
|
2016-04-26 00:47:02 +00:00
|
|
|
return hr.entity(0);
|
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
hEntity createCircle(const Vector &c, double r, hStyle style) {
|
2016-05-25 12:08:19 +00:00
|
|
|
hRequest hr = SS.GW.AddRequest(Request::Type::CIRCLE, /*rememberForUndo=*/false);
|
2016-04-26 00:47:02 +00:00
|
|
|
SK.GetEntity(hr.entity(1))->PointForceTo(c);
|
|
|
|
processPoint(hr.entity(1));
|
|
|
|
SK.GetEntity(hr.entity(64))->DistanceForceTo(r);
|
2016-11-19 10:19:08 +00:00
|
|
|
|
|
|
|
configureRequest(hr, style);
|
2016-04-26 00:47:02 +00:00
|
|
|
return hr.entity(0);
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addLayer(const DRW_Layer &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
layers.emplace(data.name, data);
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addBlock(const DRW_Block &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
readBlock = &blocks[data.name];
|
|
|
|
readBlock->data = data;
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void endBlock() override {
|
2016-04-13 08:43:06 +00:00
|
|
|
readBlock = NULL;
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addPoint(const DRW_Point &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_Point>(data)) return;
|
|
|
|
|
2016-05-25 12:08:19 +00:00
|
|
|
hRequest hr = SS.GW.AddRequest(Request::Type::DATUM_POINT, /*rememberForUndo=*/false);
|
2016-04-13 08:43:06 +00:00
|
|
|
SK.GetEntity(hr.entity(0))->PointForceTo(toVector(data.basePoint));
|
2016-04-26 00:47:02 +00:00
|
|
|
processPoint(hr.entity(0));
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addLine(const DRW_Line &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_Line>(data)) return;
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
createLine(toVector(data.basePoint), toVector(data.secPoint), styleFor(&data),
|
|
|
|
/*constrainHV=*/true);
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addArc(const DRW_Arc &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_Arc>(data)) return;
|
|
|
|
|
2016-05-25 12:08:19 +00:00
|
|
|
hRequest hr = SS.GW.AddRequest(Request::Type::ARC_OF_CIRCLE, /*rememberForUndo=*/false);
|
2016-04-13 08:43:06 +00:00
|
|
|
double r = data.radious;
|
|
|
|
double sa = data.staangle;
|
|
|
|
double ea = data.endangle;
|
2016-11-19 10:19:08 +00:00
|
|
|
Vector c = Vector::From(data.basePoint.x, data.basePoint.y, data.basePoint.z);
|
2016-04-13 08:43:06 +00:00
|
|
|
Vector rvs = Vector::From(r * cos(sa), r * sin(sa), data.basePoint.z).Plus(c);
|
|
|
|
Vector rve = Vector::From(r * cos(ea), r * sin(ea), data.basePoint.z).Plus(c);
|
|
|
|
|
|
|
|
if(data.extPoint.z == -1.0) {
|
|
|
|
c.x = -c.x;
|
|
|
|
rvs.x = - rvs.x;
|
|
|
|
rve.x = - rve.x;
|
|
|
|
std::swap(rvs, rve);
|
|
|
|
}
|
|
|
|
|
|
|
|
blockTransformArc(&c, &rvs, &rve);
|
|
|
|
|
|
|
|
SK.GetEntity(hr.entity(1))->PointForceTo(c);
|
|
|
|
SK.GetEntity(hr.entity(2))->PointForceTo(rvs);
|
|
|
|
SK.GetEntity(hr.entity(3))->PointForceTo(rve);
|
2016-04-26 00:47:02 +00:00
|
|
|
processPoint(hr.entity(1));
|
|
|
|
processPoint(hr.entity(2));
|
|
|
|
processPoint(hr.entity(3));
|
2016-11-19 10:19:08 +00:00
|
|
|
configureRequest(hr, styleFor(&data));
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addCircle(const DRW_Circle &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_Circle>(data)) return;
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
createCircle(toVector(data.basePoint), data.radious, styleFor(&data));
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addLWPolyline(const DRW_LWPolyline &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_LWPolyline>(data)) return;
|
|
|
|
|
|
|
|
size_t vNum = data.vertlist.size();
|
|
|
|
|
|
|
|
// Check for closed polyline.
|
|
|
|
if((data.flags & 1) != 1) vNum--;
|
|
|
|
|
|
|
|
// Correct coordinate system for the case where z=-1, as described in
|
|
|
|
// http://paulbourke.net/dataformats/dxf/dxf10.html.
|
|
|
|
bool needSwapX = data.extPoint.z == -1.0;
|
|
|
|
|
|
|
|
for(size_t i = 0; i < vNum; i++) {
|
|
|
|
DRW_Vertex2D c0 = *data.vertlist[i];
|
|
|
|
DRW_Vertex2D c1 = *data.vertlist[(i + 1) % data.vertlist.size()];
|
|
|
|
|
|
|
|
if(needSwapX) {
|
|
|
|
c0.x = -c0.x;
|
|
|
|
c1.x = -c1.x;
|
|
|
|
c0.bulge = -c0.bulge;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector p0 = Vector::From(c0.x, c0.y, 0.0);
|
|
|
|
Vector p1 = Vector::From(c1.x, c1.y, 0.0);
|
|
|
|
hStyle hs = styleFor(&data);
|
|
|
|
|
|
|
|
if(EXACT(data.vertlist[i]->bulge == 0.0)) {
|
2016-11-19 10:19:08 +00:00
|
|
|
createLine(blockTransform(p0), blockTransform(p1), hs, /*constrainHV=*/true);
|
2016-04-13 08:43:06 +00:00
|
|
|
} else {
|
|
|
|
hRequest hr = createBulge(p0, p1, c0.bulge);
|
2016-11-19 10:19:08 +00:00
|
|
|
configureRequest(hr, hs);
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addPolyline(const DRW_Polyline &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_Polyline>(data)) return;
|
|
|
|
|
2017-03-10 19:39:55 +00:00
|
|
|
size_t vNum = data.vertlist.size();
|
2016-04-13 08:43:06 +00:00
|
|
|
|
|
|
|
// Check for closed polyline.
|
|
|
|
if((data.flags & 1) != 1) vNum--;
|
|
|
|
|
|
|
|
// Correct coordinate system for the case where z=-1, as described in
|
|
|
|
// http://paulbourke.net/dataformats/dxf/dxf10.html.
|
2017-03-10 19:39:55 +00:00
|
|
|
bool needSwapX = (data.extPoint.z == -1.0);
|
2016-04-13 08:43:06 +00:00
|
|
|
|
2017-03-10 19:39:55 +00:00
|
|
|
for(size_t i = 0; i < vNum; i++) {
|
2016-04-13 08:43:06 +00:00
|
|
|
DRW_Coord c0 = data.vertlist[i]->basePoint;
|
|
|
|
DRW_Coord c1 = data.vertlist[(i + 1) % data.vertlist.size()]->basePoint;
|
|
|
|
|
|
|
|
double bulge = data.vertlist[i]->bulge;
|
|
|
|
if(needSwapX) {
|
|
|
|
c0.x = -c0.x;
|
|
|
|
c1.x = -c1.x;
|
|
|
|
bulge = -bulge;
|
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
Vector p0 = Vector::From(c0.x, c0.y, c0.z);
|
|
|
|
Vector p1 = Vector::From(c1.x, c1.y, c1.z);
|
2016-04-13 08:43:06 +00:00
|
|
|
hStyle hs = styleFor(&data);
|
|
|
|
|
|
|
|
if(EXACT(bulge == 0.0)) {
|
2016-11-19 10:19:08 +00:00
|
|
|
createLine(blockTransform(p0), blockTransform(p1), hs, /*constrainHV=*/true);
|
2016-04-13 08:43:06 +00:00
|
|
|
} else {
|
|
|
|
hRequest hr = createBulge(p0, p1, bulge);
|
2016-11-19 10:19:08 +00:00
|
|
|
configureRequest(hr, hs);
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addSpline(const DRW_Spline *data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
if(data->degree != 3) return;
|
|
|
|
if(addPendingBlockEntity<DRW_Spline>(*data)) return;
|
|
|
|
|
2016-05-25 12:08:19 +00:00
|
|
|
hRequest hr = SS.GW.AddRequest(Request::Type::CUBIC, /*rememberForUndo=*/false);
|
2016-04-13 08:43:06 +00:00
|
|
|
for(int i = 0; i < 4; i++) {
|
|
|
|
SK.GetEntity(hr.entity(i + 1))->PointForceTo(toVector(*data->controllist[i]));
|
2016-04-26 00:47:02 +00:00
|
|
|
processPoint(hr.entity(i + 1));
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
2016-11-19 10:19:08 +00:00
|
|
|
configureRequest(hr, styleFor(data));
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addInsert(const DRW_Insert &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_Insert>(data)) return;
|
|
|
|
|
|
|
|
auto bi = blocks.find(data.name);
|
2016-05-18 22:51:36 +00:00
|
|
|
ssassert(bi != blocks.end(), "Inserted block does not exist");
|
2016-04-13 08:43:06 +00:00
|
|
|
Block *block = &bi->second;
|
|
|
|
|
|
|
|
// Push transform.
|
|
|
|
Vector x = blockX;
|
|
|
|
Vector y = blockY;
|
|
|
|
Vector t = blockT;
|
|
|
|
|
|
|
|
const DRW_Insert *oldInsert = insertInsert;
|
|
|
|
insertInsert = &data;
|
|
|
|
|
|
|
|
if(data.extPoint.z == -1.0) invertXTransform();
|
2016-11-19 10:19:08 +00:00
|
|
|
multBlockTransform(data.basePoint.x, data.basePoint.y, data.xscale, data.yscale,
|
|
|
|
data.angle);
|
2016-04-13 08:43:06 +00:00
|
|
|
for(auto &e : block->entities) {
|
|
|
|
addEntity(&*e);
|
|
|
|
}
|
|
|
|
|
|
|
|
insertInsert = oldInsert;
|
|
|
|
|
|
|
|
// Pop transform.
|
|
|
|
blockX = x;
|
|
|
|
blockY = y;
|
|
|
|
blockT = t;
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addMText(const DRW_MText &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_MText>(data)) return;
|
|
|
|
|
|
|
|
DRW_MText text = data;
|
|
|
|
text.secPoint = text.basePoint;
|
|
|
|
addText(text);
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addText(const DRW_Text &data) override {
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_Text>(data)) return;
|
|
|
|
|
|
|
|
Constraint c = {};
|
|
|
|
c.group = SS.GW.activeGroup;
|
|
|
|
c.workplane = SS.GW.ActiveWorkplane();
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
c.type = Constraint::Type::COMMENT;
|
2016-04-13 08:43:06 +00:00
|
|
|
if(data.alignH == DRW_Text::HLeft && data.alignV == DRW_Text::VBaseLine) {
|
|
|
|
c.disp.offset = toVector(data.basePoint);
|
|
|
|
} else {
|
|
|
|
c.disp.offset = toVector(data.secPoint);
|
|
|
|
}
|
|
|
|
c.comment = data.text;
|
|
|
|
c.disp.style = styleFor(&data);
|
2016-05-25 12:08:19 +00:00
|
|
|
Constraint::AddConstraint(&c, /*rememberForUndo=*/false);
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
2016-04-26 00:47:02 +00:00
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addDimAlign(const DRW_DimAligned *data) override {
|
2016-04-26 00:47:02 +00:00
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_DimAligned>(*data)) return;
|
|
|
|
|
|
|
|
Vector p0 = toVector(data->getDef1Point());
|
|
|
|
Vector p1 = toVector(data->getDef2Point());
|
|
|
|
Vector p2 = toVector(data->getTextPoint());
|
|
|
|
hConstraint hc = Constraint::Constrain(
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
Constraint::Type::PT_PT_DISTANCE,
|
2016-04-26 00:47:02 +00:00
|
|
|
createOrGetPoint(p0),
|
|
|
|
createOrGetPoint(p1),
|
|
|
|
Entity::NO_ENTITY
|
|
|
|
);
|
|
|
|
|
|
|
|
Constraint *c = SK.GetConstraint(hc);
|
2016-05-17 06:49:37 +00:00
|
|
|
if(data->hasActualMeasurement()) {
|
|
|
|
c->valA = data->getActualMeasurement();
|
|
|
|
} else {
|
|
|
|
c->ModifyToSatisfy();
|
|
|
|
}
|
2016-04-26 00:47:02 +00:00
|
|
|
c->disp.offset = p2.Minus(p0.Plus(p1).ScaledBy(0.5));
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addDimLinear(const DRW_DimLinear *data) override {
|
2016-04-26 00:47:02 +00:00
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_DimLinear>(*data)) return;
|
|
|
|
|
2016-05-25 12:08:19 +00:00
|
|
|
Vector p0 = toVector(data->getDef1Point(), /*transform=*/false);
|
|
|
|
Vector p1 = toVector(data->getDef2Point(), /*transform=*/false);
|
|
|
|
Vector p2 = toVector(data->getTextPoint(), /*transform=*/false);
|
2016-04-26 00:47:02 +00:00
|
|
|
|
|
|
|
double angle = data->getAngle() * PI / 180.0;
|
|
|
|
Vector dir = Vector::From(cos(angle), sin(angle), 0.0);
|
|
|
|
Vector p3 = p1.Minus(p1.ClosestPointOnLine(p2, dir)).Plus(p1);
|
|
|
|
if(p1.Minus(p3).Magnitude() < LENGTH_EPS) {
|
|
|
|
p3 = p0.Minus(p0.ClosestPointOnLine(p2, dir)).Plus(p1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector p4 = p0.ClosestPointOnLine(p1, p3.Minus(p1)).Plus(p0).ScaledBy(0.5);
|
|
|
|
|
|
|
|
p0 = blockTransform(p0);
|
|
|
|
p1 = blockTransform(p1);
|
|
|
|
p2 = blockTransform(p2);
|
|
|
|
p3 = blockTransform(p3);
|
|
|
|
p4 = blockTransform(p4);
|
|
|
|
|
|
|
|
hConstraint hc = Constraint::Constrain(
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
Constraint::Type::PT_LINE_DISTANCE,
|
2016-04-26 00:47:02 +00:00
|
|
|
createOrGetPoint(p0),
|
|
|
|
Entity::NO_ENTITY,
|
2016-11-19 10:19:08 +00:00
|
|
|
createLine(p1, p3, invisibleStyle())
|
2016-04-26 00:47:02 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
Constraint *c = SK.GetConstraint(hc);
|
2016-05-17 06:49:37 +00:00
|
|
|
if(data->hasActualMeasurement()) {
|
|
|
|
c->valA = data->getActualMeasurement();
|
|
|
|
} else {
|
|
|
|
c->ModifyToSatisfy();
|
|
|
|
}
|
2016-04-26 00:47:02 +00:00
|
|
|
c->disp.offset = p2.Minus(p4);
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addDimAngular(const DRW_DimAngular *data) override {
|
2016-04-26 00:47:02 +00:00
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_DimAngular>(*data)) return;
|
|
|
|
|
|
|
|
Vector l0p0 = toVector(data->getFirstLine1());
|
|
|
|
Vector l0p1 = toVector(data->getFirstLine2());
|
|
|
|
Vector l1p0 = toVector(data->getSecondLine1());
|
|
|
|
Vector l1p1 = toVector(data->getSecondLine2());
|
|
|
|
|
|
|
|
hConstraint hc = Constraint::Constrain(
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
Constraint::Type::ANGLE,
|
2016-04-26 00:47:02 +00:00
|
|
|
Entity::NO_ENTITY,
|
|
|
|
Entity::NO_ENTITY,
|
2016-11-19 10:19:08 +00:00
|
|
|
createLine(l0p0, l0p1, invisibleStyle()),
|
|
|
|
createLine(l1p1, l1p0, invisibleStyle()),
|
2016-04-26 00:47:02 +00:00
|
|
|
/*other=*/false,
|
|
|
|
/*other2=*/false
|
|
|
|
);
|
|
|
|
|
|
|
|
Constraint *c = SK.GetConstraint(hc);
|
|
|
|
c->ModifyToSatisfy();
|
2016-05-17 06:49:37 +00:00
|
|
|
if(data->hasActualMeasurement()) {
|
|
|
|
double actual = data->getActualMeasurement() / PI * 180.0;
|
|
|
|
if(fabs(180.0 - actual - c->valA) < fabs(actual - c->valA)) {
|
|
|
|
c->other = true;
|
|
|
|
}
|
|
|
|
c->valA = actual;
|
|
|
|
}
|
2016-04-26 00:47:02 +00:00
|
|
|
|
|
|
|
bool skew = false;
|
|
|
|
Vector pi = Vector::AtIntersectionOfLines(l0p0, l0p1, l1p0, l1p1, &skew);
|
|
|
|
if(!skew) {
|
|
|
|
c->disp.offset = toVector(data->getTextPoint()).Minus(pi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
hConstraint createDiametric(Vector cp, double r, Vector tp, double actual,
|
|
|
|
bool asRadius = false) {
|
|
|
|
hEntity he = createCircle(cp, r, invisibleStyle());
|
2016-04-26 00:47:02 +00:00
|
|
|
|
|
|
|
hConstraint hc = Constraint::Constrain(
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
Constraint::Type::DIAMETER,
|
2016-04-26 00:47:02 +00:00
|
|
|
Entity::NO_ENTITY,
|
|
|
|
Entity::NO_ENTITY,
|
|
|
|
he
|
|
|
|
);
|
|
|
|
|
|
|
|
Constraint *c = SK.GetConstraint(hc);
|
2016-05-17 06:49:37 +00:00
|
|
|
if(actual > 0.0) {
|
|
|
|
c->valA = asRadius ? actual * 2.0 : actual;
|
|
|
|
} else {
|
|
|
|
c->ModifyToSatisfy();
|
|
|
|
}
|
2016-04-26 00:47:02 +00:00
|
|
|
c->disp.offset = tp.Minus(cp);
|
|
|
|
if(asRadius) c->other = true;
|
|
|
|
return hc;
|
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addDimRadial(const DRW_DimRadial *data) override {
|
2016-04-26 00:47:02 +00:00
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_DimRadial>(*data)) return;
|
|
|
|
|
|
|
|
Vector cp = toVector(data->getCenterPoint());
|
|
|
|
Vector dp = toVector(data->getDiameterPoint());
|
|
|
|
Vector tp = toVector(data->getTextPoint());
|
2016-05-17 06:49:37 +00:00
|
|
|
double actual = -1.0;
|
|
|
|
if(data->hasActualMeasurement()) {
|
|
|
|
actual = data->getActualMeasurement();
|
|
|
|
}
|
2016-04-26 00:47:02 +00:00
|
|
|
|
2016-05-17 06:49:37 +00:00
|
|
|
createDiametric(cp, cp.Minus(dp).Magnitude(), tp, actual, /*asRadius=*/true);
|
2016-04-26 00:47:02 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addDimDiametric(const DRW_DimDiametric *data) override {
|
2016-04-26 00:47:02 +00:00
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_DimRadial>(*data)) return;
|
|
|
|
|
|
|
|
Vector dp1 = toVector(data->getDiameter1Point());
|
|
|
|
Vector dp2 = toVector(data->getDiameter2Point());
|
|
|
|
|
|
|
|
Vector cp = dp1.Plus(dp2).ScaledBy(0.5);
|
|
|
|
Vector tp = toVector(data->getTextPoint());
|
2016-05-17 06:49:37 +00:00
|
|
|
double actual = -1.0;
|
|
|
|
if(data->hasActualMeasurement()) {
|
|
|
|
actual = data->getActualMeasurement();
|
|
|
|
}
|
2016-04-26 00:47:02 +00:00
|
|
|
|
2016-05-17 06:49:37 +00:00
|
|
|
createDiametric(cp, cp.Minus(dp1).Magnitude(), tp, actual, /*asRadius=*/false);
|
2016-04-26 00:47:02 +00:00
|
|
|
}
|
|
|
|
|
2016-05-18 18:42:33 +00:00
|
|
|
void addDimAngular3P(const DRW_DimAngular3p *data) override {
|
2016-04-26 00:47:02 +00:00
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
if(addPendingBlockEntity<DRW_DimAngular3p>(*data)) return;
|
|
|
|
|
|
|
|
DRW_DimAngular dim = *static_cast<const DRW_Dimension *>(data);
|
|
|
|
dim.setFirstLine1(data->getVertexPoint());
|
|
|
|
dim.setFirstLine2(data->getFirstLine());
|
|
|
|
dim.setSecondLine1(data->getVertexPoint());
|
|
|
|
dim.setSecondLine2(data->getSecondLine());
|
|
|
|
addDimAngular(&dim);
|
|
|
|
}
|
2016-04-13 08:43:06 +00:00
|
|
|
};
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
class DxfCheck3D : public DRW_Interface {
|
|
|
|
public:
|
|
|
|
bool is3d;
|
2016-10-09 14:58:07 +00:00
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
void addEntity(DRW_Entity *e) {
|
|
|
|
switch(e->eType) {
|
|
|
|
case DRW::POINT:
|
|
|
|
addPoint(*static_cast<DRW_Point *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::LINE:
|
|
|
|
addLine(*static_cast<DRW_Line *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::ARC:
|
|
|
|
addArc(*static_cast<DRW_Arc *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::CIRCLE:
|
|
|
|
addCircle(*static_cast<DRW_Circle *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::POLYLINE:
|
|
|
|
addPolyline(*static_cast<DRW_Polyline *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::LWPOLYLINE:
|
|
|
|
addLWPolyline(*static_cast<DRW_LWPolyline *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::SPLINE:
|
|
|
|
addSpline(static_cast<DRW_Spline *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::INSERT:
|
|
|
|
addInsert(*static_cast<DRW_Insert *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::TEXT:
|
|
|
|
addText(*static_cast<DRW_Text *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::MTEXT:
|
|
|
|
addMText(*static_cast<DRW_MText *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMALIGNED:
|
|
|
|
addDimAlign(static_cast<DRW_DimAligned *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMLINEAR:
|
|
|
|
addDimLinear(static_cast<DRW_DimLinear *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMRADIAL:
|
|
|
|
addDimRadial(static_cast<DRW_DimRadial *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMDIAMETRIC:
|
|
|
|
addDimDiametric(static_cast<DRW_DimDiametric *>(e));
|
|
|
|
break;
|
|
|
|
case DRW::DIMANGULAR:
|
|
|
|
addDimAngular(static_cast<DRW_DimAngular *>(e));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-10-09 14:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
void addPoint(const DRW_Point &data) override {
|
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data.basePoint);
|
2016-05-07 05:17:23 +00:00
|
|
|
}
|
2016-10-09 14:58:07 +00:00
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
void addLine(const DRW_Line &data) override {
|
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data.basePoint);
|
|
|
|
checkCoord(data.secPoint);
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
void addArc(const DRW_Arc &data) override {
|
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data.basePoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addCircle(const DRW_Circle &data) override {
|
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data.basePoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addPolyline(const DRW_Polyline &data) override {
|
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
for(size_t i = 0; i < data.vertlist.size(); i++) {
|
|
|
|
checkCoord(data.vertlist[i]->basePoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void addSpline(const DRW_Spline *data) override {
|
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
if(data->degree != 3) return;
|
|
|
|
for(int i = 0; i < 4; i++) {
|
|
|
|
checkCoord(*data->controllist[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void addInsert(const DRW_Insert &data) override {
|
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data.basePoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addMText(const DRW_MText &data) override {
|
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
|
|
|
|
DRW_MText text = data;
|
|
|
|
text.secPoint = text.basePoint;
|
|
|
|
addText(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addText(const DRW_Text &data) override {
|
|
|
|
if(data.space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data.basePoint);
|
|
|
|
checkCoord(data.secPoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
void addDimAlign(const DRW_DimAligned *data) override {
|
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data->getDef1Point());
|
|
|
|
checkCoord(data->getDef2Point());
|
|
|
|
checkCoord(data->getTextPoint());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addDimLinear(const DRW_DimLinear *data) override {
|
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data->getDef1Point());
|
|
|
|
checkCoord(data->getDef2Point());
|
|
|
|
checkCoord(data->getTextPoint());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addDimAngular(const DRW_DimAngular *data) override {
|
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data->getFirstLine1());
|
|
|
|
checkCoord(data->getFirstLine2());
|
|
|
|
checkCoord(data->getSecondLine1());
|
|
|
|
checkCoord(data->getSecondLine2());
|
|
|
|
checkCoord(data->getTextPoint());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addDimRadial(const DRW_DimRadial *data) override {
|
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data->getCenterPoint());
|
|
|
|
checkCoord(data->getDiameterPoint());
|
|
|
|
checkCoord(data->getTextPoint());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addDimDiametric(const DRW_DimDiametric *data) override {
|
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
checkCoord(data->getDiameter1Point());
|
|
|
|
checkCoord(data->getDiameter2Point());
|
|
|
|
checkCoord(data->getTextPoint());
|
|
|
|
}
|
|
|
|
|
|
|
|
void addDimAngular3P(const DRW_DimAngular3p *data) override {
|
|
|
|
if(data->space != DRW::ModelSpace) return;
|
|
|
|
DRW_DimAngular dim = *static_cast<const DRW_Dimension *>(data);
|
|
|
|
|
|
|
|
dim.setFirstLine1(data->getVertexPoint());
|
|
|
|
dim.setFirstLine2(data->getFirstLine());
|
|
|
|
dim.setSecondLine1(data->getVertexPoint());
|
|
|
|
dim.setSecondLine2(data->getSecondLine());
|
|
|
|
addDimAngular(&dim);
|
|
|
|
}
|
|
|
|
|
|
|
|
void checkCoord(const DRW_Coord &coord) {
|
|
|
|
if(fabs(coord.z) > LENGTH_EPS) {
|
|
|
|
is3d = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-11 14:43:21 +00:00
|
|
|
static void ImportDwgDxf(const Platform::Path &filename,
|
2016-11-19 10:19:08 +00:00
|
|
|
std::function<bool(const std::string &data, DRW_Interface *intf)> read) {
|
2017-03-11 14:43:21 +00:00
|
|
|
std::string fileType = ToUpper(filename.Extension());
|
2016-10-09 14:58:07 +00:00
|
|
|
|
2016-10-09 19:58:44 +00:00
|
|
|
std::string data;
|
|
|
|
if(!ReadFile(filename, &data)) {
|
2017-03-11 14:43:21 +00:00
|
|
|
Error("Couldn't read from '%s'", filename.raw.c_str());
|
2016-10-09 14:58:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
bool asConstruction = true;
|
|
|
|
if(SS.GW.LockedInWorkplane()) {
|
|
|
|
DxfCheck3D checker = {};
|
|
|
|
read(data, &checker);
|
|
|
|
if(checker.is3d) {
|
|
|
|
Message("This %s file contains entities with non-zero Z coordinate; "
|
|
|
|
"the entire file will be imported as construction entities in 3d.",
|
|
|
|
fileType.c_str());
|
|
|
|
SS.GW.SetWorkplaneFreeIn3d();
|
|
|
|
SS.GW.EnsureValidActives();
|
|
|
|
} else {
|
|
|
|
asConstruction = false;
|
|
|
|
}
|
2016-05-07 05:17:23 +00:00
|
|
|
}
|
2016-10-09 14:58:07 +00:00
|
|
|
|
2016-11-19 10:19:08 +00:00
|
|
|
SS.UndoRemember();
|
|
|
|
|
|
|
|
DxfImport importer = {};
|
|
|
|
importer.asConstruction = asConstruction;
|
|
|
|
importer.clearBlockTransform();
|
|
|
|
if(!read(data, &importer)) {
|
|
|
|
Error("Corrupted %s file.", fileType.c_str());
|
Eliminate blocking in Error() and Message() calls.
This serves two purposes.
First, we want to (some day) convert these messages into a less
obtrustive form, something like toaster notifications, such that they
don't interrupt workflow as harshly. That would, of course, be
nonblocking.
Second, some platforms, like Emscripten, do not support nested event
loops, and it's not possible to display a modal dialog on them
synchronously.
When making this commit, I've reviewed all Error() and Message()
calls to ensure that only some of the following is true for all
of them:
* The call is followed a break or return statement that exits
an UI entry point (e.g. an MenuX function);
* The call is followed by cleanup (in fact, in this case the new
behavior is better, since even with a synchronous modal dialog
we have to be reentrant);
* The message is an informational message only and nothing
unexpected will happen if the operation proceeds in background.
In general, all Error() calls already satisfied the above conditions,
although in some cases I changed control flow aroudn them to more
clearly show that. The Message() calls that didn't satisfy these
conditions were reworked into an asynchronous form.
There are three explicit RunModal() calls left that need to be
reworked into an async form.
2018-07-19 21:54:05 +00:00
|
|
|
return;
|
2016-11-19 10:19:08 +00:00
|
|
|
}
|
|
|
|
if(importer.unknownEntities > 0) {
|
|
|
|
Message("%u %s entities of unknown type were ignored.",
|
|
|
|
importer.unknownEntities, fileType.c_str());
|
2016-05-07 05:17:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-11 14:43:21 +00:00
|
|
|
void ImportDxf(const Platform::Path &filename) {
|
2016-11-19 10:19:08 +00:00
|
|
|
ImportDwgDxf(filename, [](const std::string &data, DRW_Interface *intf) {
|
|
|
|
std::stringstream stream(data);
|
|
|
|
return dxfRW().read(stream, intf, /*ext=*/false);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-03-11 14:43:21 +00:00
|
|
|
void ImportDwg(const Platform::Path &filename) {
|
2016-11-19 10:19:08 +00:00
|
|
|
ImportDwgDxf(filename, [](const std::string &data, DRW_Interface *intf) {
|
|
|
|
std::stringstream stream(data);
|
|
|
|
return dwgR().read(stream, intf, /*ext=*/false);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-04-13 08:43:06 +00:00
|
|
|
}
|