Implement an image request.

pull/216/head
EvilSpirit 2016-11-29 23:49:20 +07:00 committed by whitequark
parent e2e74762f4
commit 5744d1d599
29 changed files with 1040 additions and 37 deletions

View File

@ -12,6 +12,7 @@ New sketch features:
* A new sketch in workplane group can be created based on existing workplane.
* TTF text request has two additional points on the right side, which allow
constraining the width of text.
* Image requests can now be created, similar to TTF text requests.
* Irrelevant points (e.g. arc center point) are not counted when estimating
the bounding box used to compute chord tolerance.
* When adding a constraint which has a label and is redundant with another

View File

@ -158,6 +158,7 @@ add_resources(
icons/graphics-window/equal.png
icons/graphics-window/extrude.png
icons/graphics-window/horiz.png
icons/graphics-window/image.png
icons/graphics-window/in3d.png
icons/graphics-window/lathe.png
icons/graphics-window/length.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -4,7 +4,9 @@
// Copyright 2016 Aleksey Egorov
//-----------------------------------------------------------------------------
uniform vec4 color;
uniform sampler2D texture;
void main() {
if(texture2D(texture, gl_FragCoord.xy / 32.0).a < 0.5) discard;
gl_FragColor = color;
}

View File

@ -195,6 +195,16 @@ void TextWindow::DescribeSelection() {
}
break;
}
case Entity::Type::IMAGE: {
Printf(false, "%FtIMAGE%E");
Platform::Path relativePath = e->file.RelativeTo(SS.saveFile.Parent());
if(relativePath.IsEmpty()) {
Printf(true, " file = '%Fi%s%E'", e->file.raw.c_str());
} else {
Printf(true, " file = '%Fi%s%E'", relativePath.raw.c_str());
}
break;
}
default:
Printf(true, "%Ft?? ENTITY%E");

View File

@ -394,6 +394,9 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
for(Entity &e : SK.entity) {
if(!e.IsVisible()) continue;
// If faces aren't selectable, image entities aren't either.
if(e.type == Entity::Type::IMAGE && !showFaces) continue;
// Don't hover whatever's being dragged.
if(IsFromPending(e.h.request())) {
// The one exception is when we're creating a new cubic; we

View File

@ -104,6 +104,7 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
case Type::CUBIC:
case Type::CUBIC_PERIODIC:
case Type::TTF_TEXT:
case Type::IMAGE:
refs->push_back(SK.GetEntity(point[0])->PointGetNum());
break;
@ -695,6 +696,54 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
}
return;
}
case Type::IMAGE: {
Canvas::Fill fill = {};
std::shared_ptr<Pixmap> pixmap;
switch(how) {
case DrawAs::HIDDEN: return;
case DrawAs::HOVERED: {
fill.color = Style::Color(Style::HOVERED).WithAlpha(180);
fill.pattern = Canvas::FillPattern::CHECKERED_A;
fill.zIndex = 2;
break;
}
case DrawAs::SELECTED: {
fill.color = Style::Color(Style::SELECTED).WithAlpha(180);
fill.pattern = Canvas::FillPattern::CHECKERED_B;
fill.zIndex = 1;
break;
}
default:
fill.color = RgbaColor::FromFloat(1.0f, 1.0f, 1.0f);
pixmap = SS.images[file];
break;
}
Canvas::hFill hf = canvas->GetFill(fill);
Vector v[4] = {};
for(int i = 0; i < 4; i++) {
v[i] = SK.GetEntity(point[i])->PointGetNum();
}
Vector iu = v[3].Minus(v[0]);
Vector iv = v[1].Minus(v[0]);
if(how == DrawAs::DEFAULT && pixmap == NULL) {
Canvas::Stroke stroke = Style::Stroke(Style::DRAW_ERROR);
stroke.color = stroke.color.WithAlpha(50);
Canvas::hStroke hs = canvas->GetStroke(stroke);
canvas->DrawLine(v[0], v[2], hs);
canvas->DrawLine(v[1], v[3], hs);
for(int i = 0; i < 4; i++) {
canvas->DrawLine(v[i], v[(i + 1) % 4], hs);
}
} else {
canvas->DrawPixmap(pixmap, v[0], iu, iv,
Point2d::From(0.0, 0.0), Point2d::From(1.0, 1.0), hf);
}
}
case Type::FACE_NORMAL_PT:
case Type::FACE_XPROD:

View File

@ -785,7 +785,10 @@ Vector EntityBase::EndpointFinish() const {
} else ssassert(false, "Unexpected entity type");
}
void EntityBase::TtfTextGetPointsExprs(ExprVector *eb, ExprVector *ec) const {
void EntityBase::RectGetPointsExprs(ExprVector *eb, ExprVector *ec) const {
ssassert(type == Type::TTF_TEXT || type == Type::IMAGE,
"Unexpected entity type");
EntityBase *a = SK.GetEntity(point[0]);
EntityBase *o = SK.GetEntity(point[1]);
@ -846,6 +849,7 @@ void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) const {
break;
}
case Type::IMAGE:
case Type::TTF_TEXT: {
if(SK.GetEntity(point[0])->type != Type::POINT_IN_2D) break;
EntityBase *b = SK.GetEntity(point[2]);
@ -854,7 +858,7 @@ void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) const {
ExprVector ec = c->PointGetExprsInWorkplane(workplane);
ExprVector ebp, ecp;
TtfTextGetPointsExprs(&ebp, &ecp);
RectGetPointsExprs(&ebp, &ecp);
ExprVector beq = eb.Minus(ebp);
AddEq(l, beq.x, 0);

View File

@ -33,6 +33,7 @@ void SolveSpaceUI::ClearExisting() {
SK.entity.Clear();
SK.param.Clear();
images.clear();
}
hGroup SolveSpaceUI::CreateDefaultDrawingGroup() {
@ -131,6 +132,7 @@ const SolveSpaceUI::SaveTable SolveSpaceUI::SAVED[] = {
{ 'r', "Request.style", 'x', &(SS.sv.r.style) },
{ 'r', "Request.str", 'S', &(SS.sv.r.str) },
{ 'r', "Request.font", 'S', &(SS.sv.r.font) },
{ 'r', "Request.file", 'P', &(SS.sv.r.file) },
{ 'r', "Request.aspectRatio", 'f', &(SS.sv.r.aspectRatio) },
{ 'e', "Entity.h.v", 'x', &(SS.sv.e.h.v) },
@ -139,6 +141,7 @@ const SolveSpaceUI::SaveTable SolveSpaceUI::SAVED[] = {
{ 'e', "Entity.style", 'x', &(SS.sv.e.style) },
{ 'e', "Entity.str", 'S', &(SS.sv.e.str) },
{ 'e', "Entity.font", 'S', &(SS.sv.e.font) },
{ 'e', "Entity.file", 'P', &(SS.sv.e.file) },
{ 'e', "Entity.point[0].v", 'x', &(SS.sv.e.point[0].v) },
{ 'e', "Entity.point[1].v", 'x', &(SS.sv.e.point[1].v) },
{ 'e', "Entity.point[2].v", 'x', &(SS.sv.e.point[2].v) },
@ -408,7 +411,10 @@ void SolveSpaceUI::LoadUsingTable(const Platform::Path &filename, char *key, cha
case 'x': sscanf(val, "%x", &u); p->x()= u; break;
case 'P': {
p->P() = filename.Parent().Join(Platform::Path::FromPortable(val));
Platform::Path path = Platform::Path::FromPortable(val);
if(!path.IsEmpty()) {
p->P() = filename.Parent().Join(path).Expand();
}
break;
}
@ -537,7 +543,7 @@ bool SolveSpaceUI::LoadFromFile(const Platform::Path &filename, bool canCancel)
NewFile();
}
}
if(!ReloadAllImported(filename, canCancel)) {
if(!ReloadAllLinked(filename, canCancel)) {
return false;
}
UpgradeLegacyData();
@ -577,7 +583,7 @@ void SolveSpaceUI::UpgradeLegacyData() {
Entity *b = entity.FindById(text->point[2]);
Entity *c = entity.FindById(text->point[3]);
ExprVector bex, cex;
text->TtfTextGetPointsExprs(&bex, &cex);
text->RectGetPointsExprs(&bex, &cex);
b->PointForceParamTo(bex.Eval());
c->PointForceParamTo(cex.Eval());
}
@ -819,11 +825,11 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityLi
return true;
}
bool SolveSpaceUI::ReloadAllImported(const Platform::Path &filename, bool canCancel)
{
bool SolveSpaceUI::ReloadAllLinked(const Platform::Path &saveFile, bool canCancel) {
std::map<Platform::Path, Platform::Path, Platform::PathLess> linkMap;
allConsistent = false;
for(Group &g : SK.group) {
if(g.type != Group::Type::LINKED) continue;
@ -838,10 +844,16 @@ bool SolveSpaceUI::ReloadAllImported(const Platform::Path &filename, bool canCan
try_again:
if(LoadEntitiesFromFile(g.linkFile, &g.impEntity, &g.impMesh, &g.impShell)) {
// We loaded the data, good.
// We loaded the data, good. Now import its dependencies as well.
for(Entity &e : g.impEntity) {
if(e.type != Entity::Type::IMAGE) continue;
if(!ReloadLinkedImage(g.linkFile, &e.file, canCancel)) {
return false;
}
}
} else if(linkMap.count(g.linkFile) == 0) {
// The file was moved; prompt the user for its new location.
switch(LocateImportedFileYesNoCancel(g.linkFile.RelativeTo(filename), canCancel)) {
switch(LocateImportedFileYesNoCancel(g.linkFile.RelativeTo(saveFile), canCancel)) {
case DIALOG_YES: {
Platform::Path newLinkFile;
if(GetOpenFile(&newLinkFile, "", SlvsFileFilter)) {
@ -867,6 +879,59 @@ try_again:
}
}
for(Request &r : SK.request) {
if(r.type != Request::Type::IMAGE) continue;
if(!ReloadLinkedImage(saveFile, &r.file, canCancel)) {
return false;
}
}
return true;
}
bool SolveSpaceUI::ReloadLinkedImage(const Platform::Path &saveFile,
Platform::Path *filename, bool canCancel) {
std::shared_ptr<Pixmap> pixmap;
bool promptOpenFile = false;
if(filename->IsEmpty()) {
// We're prompting the user for a new image.
promptOpenFile = true;
} else {
auto image = SS.images.find(*filename);
if(image != SS.images.end()) return true;
pixmap = Pixmap::ReadPng(*filename);
if(pixmap == NULL) {
// The file was moved; prompt the user for its new location.
switch(LocateImportedFileYesNoCancel(filename->RelativeTo(saveFile), canCancel)) {
case DIALOG_YES:
promptOpenFile = true;
break;
case DIALOG_NO:
// We don't know where the file is, record it as absent.
break;
case DIALOG_CANCEL:
return false;
}
}
}
if(promptOpenFile) {
if(GetOpenFile(filename, "", RasterFileFilter)) {
pixmap = Pixmap::ReadPng(*filename);
if(pixmap == NULL) {
Error("The image '%s' is corrupted.", filename->raw.c_str());
}
// We know where the file is now, good.
} else if(canCancel) {
return false;
}
}
// We loaded the data, good.
SS.images[*filename] = pixmap;
return true;
}

View File

@ -112,6 +112,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, N_("&Bezier Cubic Spline"), Command::CUBIC, 'B', TN, mReq },
{ 1, NULL, Command::NONE, 0, TN, NULL },
{ 1, N_("&Text in TrueType Font"), Command::TTF_TEXT, 'T', TN, mReq },
{ 1, N_("&Image"), Command::IMAGE, 0, TN, mReq },
{ 1, NULL, Command::NONE, 0, TN, NULL },
{ 1, N_("To&ggle Construction"), Command::CONSTRUCTION, 'G', TN, mReq },
{ 1, N_("Tangent &Arc at Point"), Command::TANGENT_ARC, S|'A', TN, mReq },
@ -943,7 +944,8 @@ void GraphicsWindow::MenuEdit(Command id) {
break;
case Command::REGEN_ALL:
SS.ReloadAllImported(SS.saveFile);
SS.images.clear();
SS.ReloadAllLinked(SS.saveFile);
SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
SS.ScheduleShowTW();
break;
@ -1015,6 +1017,12 @@ void GraphicsWindow::MenuRequest(Command id) {
case Command::WORKPLANE: s = _("click origin of workplane"); goto c;
case Command::RECTANGLE: s = _("click one corner of rectangle"); goto c;
case Command::TTF_TEXT: s = _("click top left of text"); goto c;
case Command::IMAGE:
if(!SS.ReloadLinkedImage(SS.saveFile, &SS.GW.pending.filename,
/*canCancel=*/true)) {
return;
}
s = _("click top left of image"); goto c;
c:
SS.GW.pending.operation = GraphicsWindow::Pending::COMMAND;
SS.GW.pending.command = id;

View File

@ -274,7 +274,7 @@ void Group::MenuGroup(Command id) {
Group *gg = SK.GetGroup(g.h);
if(gg->type == Type::LINKED) {
SS.ReloadAllImported(SS.saveFile);
SS.ReloadAllLinked(SS.saveFile);
}
gg->clean = false;
SS.GW.activeGroup = gg->h;
@ -801,6 +801,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
en.style = ep->style;
en.str = ep->str;
en.font = ep->font;
en.file = ep->file;
switch(ep->type) {
case Entity::Type::WORKPLANE:

View File

@ -51,7 +51,8 @@ void GraphicsWindow::StartDraggingByEntity(hEntity he) {
e->type == Entity::Type::CUBIC ||
e->type == Entity::Type::CUBIC_PERIODIC ||
e->type == Entity::Type::CIRCLE ||
e->type == Entity::Type::TTF_TEXT)
e->type == Entity::Type::TTF_TEXT ||
e->type == Entity::Type::IMAGE)
{
int pts;
EntReqTable::GetEntityInfo(e->type, e->extraPoints,
@ -1146,6 +1147,27 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
break;
}
case Command::IMAGE: {
if(!SS.GW.LockedInWorkplane()) {
Error(_("Can't draw image in 3d; first, activate a workplane "
"with Sketch -> In Workplane."));
ClearSuper();
break;
}
hr = AddRequest(Request::Type::IMAGE);
AddToPending(hr);
Request *r = SK.GetRequest(hr);
r->file = pending.filename;
SK.GetEntity(hr.entity(1))->PointForceTo(v);
SK.GetEntity(hr.entity(2))->PointForceTo(v);
pending.operation = Pending::DRAGGING_NEW_POINT;
pending.point = hr.entity(2);
pending.description = "click to place bottom left of image";
break;
}
case Command::COMMENT: {
ClearSuper();
Constraint c = {};

View File

@ -76,7 +76,7 @@ File formats:
export-wireframe:%s
export-mesh:%s
export-surfaces:%s
)", FormatListFromFileFilter(PngFileFilter).c_str(),
)", FormatListFromFileFilter(RasterFileFilter).c_str(),
FormatListFromFileFilter(VectorFileFilter).c_str(),
FormatListFromFileFilter(Vector3dFileFilter).c_str(),
FormatListFromFileFilter(MeshFileFilter).c_str(),

View File

@ -441,7 +441,7 @@ void ObjectPicker::DrawFaces(const SMesh &m, const std::vector<uint32_t> &faces,
void ObjectPicker::DrawPixmap(std::shared_ptr<const Pixmap> pm,
const Vector &o, const Vector &u, const Vector &v,
const Point2d &ta, const Point2d &tb, Canvas::hFill hcf) {
ssassert(false, "Not implemented");
DrawQuad(o, o.Plus(u), o.Plus(u).Plus(v), o.Plus(v), hcf);
}
bool ObjectPicker::Pick(std::function<void()> drawFn) {

View File

@ -769,11 +769,11 @@ public:
virtual Canvas::Layer GetLayer() const override { return stroke.layer; };
virtual int GetZIndex() const override { return stroke.zIndex; };
static std::shared_ptr<DrawCall> Create(OpenGl2Renderer *renderer, const SIndexedMesh &im,
static std::shared_ptr<DrawCall> Create(OpenGl2Renderer *renderer, const SIndexedMesh &mesh,
Canvas::Stroke *stroke) {
PointDrawCall *dc = new PointDrawCall();
dc->stroke = *stroke;
dc->handle = renderer->imeshRenderer.Add(im);
dc->handle = renderer->imeshRenderer.Add(mesh);
return std::shared_ptr<DrawCall>(dc);
}
@ -788,6 +788,42 @@ public:
}
};
class PixmapDrawCall : public DrawCall {
public:
// Key
Canvas::Fill fill;
// Data
IndexedMeshRenderer::Handle handle;
virtual Canvas::Layer GetLayer() const override { return fill.layer; };
virtual int GetZIndex() const override { return fill.zIndex; };
static std::shared_ptr<DrawCall> Create(OpenGl2Renderer *renderer, const SIndexedMesh &mesh,
Canvas::Fill *fill) {
PixmapDrawCall *dc = new PixmapDrawCall();
dc->fill = *fill;
dc->handle = renderer->imeshRenderer.Add(mesh);
return std::shared_ptr<DrawCall>(dc);
}
void Draw(OpenGl2Renderer *renderer) override {
ssglDepthRange(fill.layer, fill.zIndex);
if(fill.pattern != Canvas::FillPattern::SOLID) {
renderer->SelectMask(fill.pattern);
} else if(fill.texture) {
renderer->SelectTexture(fill.texture);
} else {
renderer->SelectMask(Canvas::FillPattern::SOLID);
}
renderer->imeshRenderer.UseFilled(fill);
renderer->imeshRenderer.Draw(handle);
}
void Remove(OpenGl2Renderer *renderer) override {
renderer->imeshRenderer.Remove(handle);
}
};
class MeshDrawCall : public DrawCall {
public:
// Key
@ -813,22 +849,22 @@ public:
return std::shared_ptr<DrawCall>(dc);
}
void DrawFace(OpenGl2Renderer *renderer, GLenum cullFace, Canvas::Fill *fill) {
void DrawFace(OpenGl2Renderer *renderer, GLenum cullFace, const Canvas::Fill &fill) {
glCullFace(cullFace);
ssglDepthRange(fill->layer, fill->zIndex);
if(fill->pattern != Canvas::FillPattern::SOLID) {
renderer->SelectMask(fill->pattern);
} else if(fill->texture) {
renderer->SelectTexture(fill->texture);
ssglDepthRange(fill.layer, fill.zIndex);
if(fill.pattern != Canvas::FillPattern::SOLID) {
renderer->SelectMask(fill.pattern);
} else if(fill.texture) {
renderer->SelectTexture(fill.texture);
} else {
renderer->SelectMask(Canvas::FillPattern::SOLID);
}
if(isShaded) {
renderer->meshRenderer.UseShaded(renderer->lighting);
} else {
renderer->meshRenderer.UseFilled(*fill);
renderer->meshRenderer.UseFilled(fill);
}
renderer->meshRenderer.Draw(handle, /*useColors=*/fill->color.IsEmpty(), fill->color);
renderer->meshRenderer.Draw(handle, /*useColors=*/fill.color.IsEmpty(), fill.color);
}
void Draw(OpenGl2Renderer *renderer) override {
@ -836,8 +872,8 @@ public:
glEnable(GL_CULL_FACE);
if(hasFillBack)
DrawFace(renderer, GL_FRONT, &fillBack);
DrawFace(renderer, GL_BACK, &fillFront);
DrawFace(renderer, GL_FRONT, fillBack);
DrawFace(renderer, GL_BACK, fillFront);
glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_CULL_FACE);
@ -977,7 +1013,14 @@ public:
void DrawPixmap(std::shared_ptr<const Pixmap> pm,
const Vector &o, const Vector &u, const Vector &v,
const Point2d &ta, const Point2d &tb, hFill hcf) override {
ssassert(false, "Not implemented");
Fill fill = *fills.FindById(hcf);
fill.texture = pm;
hcf = GetFill(fill);
SIndexedMesh mesh = {};
mesh.AddPixmap(o, u, v, ta, tb);
drawCalls.emplace(PixmapDrawCall::Create(renderer, mesh, fills.FindByIdNoOops(hcf)));
mesh.Clear();
}
void InvalidatePixmap(std::shared_ptr<const Pixmap> pm) override {

View File

@ -30,6 +30,7 @@ static const EntReqMapping EntReqMap[] = {
{ Request::Type::CIRCLE, Entity::Type::CIRCLE, 1, false, true, true },
{ Request::Type::ARC_OF_CIRCLE, Entity::Type::ARC_OF_CIRCLE, 3, false, true, false },
{ Request::Type::TTF_TEXT, Entity::Type::TTF_TEXT, 4, false, true, false },
{ Request::Type::IMAGE, Entity::Type::IMAGE, 4, false, true, false },
};
static void CopyEntityInfo(const EntReqMapping *te, int extraPoints,
@ -102,6 +103,20 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
break;
}
case Type::IMAGE: {
auto image = SS.images.find(file);
if(image != SS.images.end()) {
std::shared_ptr<Pixmap> pixmap = (*image).second;
if(pixmap != NULL) {
aspectRatio = (double)pixmap->width / (double)pixmap->height;
}
}
if(EXACT(aspectRatio == 0.0)) {
aspectRatio = 1.0;
}
break;
}
default: // most requests don't do anything else
break;
}
@ -118,6 +133,7 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
e.construction = construction;
e.str = str;
e.font = font;
e.file = file;
e.aspectRatio = aspectRatio;
e.h = h.entity(0);
@ -205,8 +221,9 @@ std::string Request::DescriptionString() const {
case Type::CUBIC: s = "cubic-bezier"; break;
case Type::CUBIC_PERIODIC: s = "periodic-cubic"; break;
case Type::CIRCLE: s = "circle"; break;
case Type::ARC_OF_CIRCLE: s = "arc-of-circle;"; break;
case Type::ARC_OF_CIRCLE: s = "arc-of-circle"; break;
case Type::TTF_TEXT: s = "ttf-text"; break;
case Type::IMAGE: s = "image"; break;
}
}
ssassert(s != NULL, "Unexpected request type");

View File

@ -312,7 +312,8 @@ public:
CUBIC_PERIODIC = 301,
CIRCLE = 400,
ARC_OF_CIRCLE = 500,
TTF_TEXT = 600
TTF_TEXT = 600,
IMAGE = 700
};
Request::Type type;
@ -326,6 +327,7 @@ public:
std::string str;
std::string font;
Platform::Path file;
double aspectRatio;
static hParam AddParam(ParamList *param, hParam hp);
@ -375,7 +377,8 @@ public:
CUBIC_PERIODIC = 12001,
CIRCLE = 13000,
ARC_OF_CIRCLE = 14000,
TTF_TEXT = 15000
TTF_TEXT = 15000,
IMAGE = 16000
};
Type type;
@ -400,6 +403,7 @@ public:
std::string str;
std::string font;
Platform::Path file;
double aspectRatio;
// For entities that are derived by a transformation, the number of
@ -476,7 +480,7 @@ public:
Vector EndpointStart() const;
Vector EndpointFinish() const;
void TtfTextGetPointsExprs(ExprVector *eap, ExprVector *ebp) const;
void RectGetPointsExprs(ExprVector *eap, ExprVector *ebp) const;
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) const;
void GenerateEquations(IdList<Equation,hEquation> *l) const;

View File

@ -457,7 +457,7 @@ void SolveSpaceUI::MenuFile(Command id) {
case Command::EXPORT_PNG: {
Platform::Path exportFile = SS.saveFile;
if(!GetSaveFile(&exportFile, "", PngFileFilter)) break;
if(!GetSaveFile(&exportFile, "", RasterFileFilter)) break;
SS.ExportAsPngTo(exportFile);
break;
}

View File

@ -620,6 +620,11 @@ public:
} UndoStack;
UndoStack undo;
UndoStack redo;
std::map<Platform::Path, std::shared_ptr<Pixmap>, Platform::PathLess> images;
bool ReloadLinkedImage(const Platform::Path &saveFile, Platform::Path *filename,
bool canCancel);
void UndoEnableMenus();
void UndoRemember();
void UndoUndo();
@ -743,7 +748,7 @@ public:
void UpgradeLegacyData();
bool LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
SMesh *m, SShell *sh);
bool ReloadAllImported(const Platform::Path &filename, bool canCancel = false);
bool ReloadAllLinked(const Platform::Path &filename, bool canCancel = false);
// And the various export options
void ExportAsPngTo(const Platform::Path &filename);
void ExportMeshTo(const Platform::Path &filename);

View File

@ -394,7 +394,7 @@ void TextWindow::ScreenBackgroundImage(int link, uint32_t v) {
if(link == 'l') {
Platform::Path bgImageFile;
if(GetOpenFile(&bgImageFile, "", PngFileFilter)) {
if(GetOpenFile(&bgImageFile, "", RasterFileFilter)) {
FILE *f = OpenFile(bgImageFile, "rb");
if(f) {
SS.bgImage.pixmap = Pixmap::ReadPng(f);

View File

@ -327,7 +327,7 @@ void TextWindow::ShowGroupInfo() {
}
} else if(g->type == Group::Type::LINKED) {
Printf(true, " %Ftlink geometry from file%E");
Platform::Path relativePath =g->linkFile.RelativeTo(SS.saveFile.Parent());
Platform::Path relativePath = g->linkFile.RelativeTo(SS.saveFile.Parent());
if(relativePath.IsEmpty()) {
Printf(false, "%Ba '%s'", g->linkFile.raw.c_str());
} else {

View File

@ -24,6 +24,8 @@ static ToolIcon Toolbar[] = {
N_("Sketch arc of a circle"), {} },
{ "text", Command::TTF_TEXT,
N_("Sketch curves from text in a TrueType font"), {} },
{ "image", Command::IMAGE,
N_("Sketch image from a file"), {} },
{ "tangent-arc", Command::TANGENT_ARC,
N_("Create tangent arc at selected point"), {} },
{ "bezier", Command::CUBIC,

View File

@ -70,7 +70,7 @@ const FileFilter SlvsFileFilter[] = {
{ NULL, {} }
};
// PNG format bitmap
const FileFilter PngFileFilter[] = {
const FileFilter RasterFileFilter[] = {
{ N_("PNG file"), { "png" } },
{ NULL, {} }
};
@ -173,6 +173,7 @@ enum class Command : uint32_t {
RECTANGLE,
CUBIC,
TTF_TEXT,
IMAGE,
SPLIT_CURVES,
TANGENT_ARC,
CONSTRUCTION,
@ -712,6 +713,7 @@ public:
hConstraint constraint;
const char *description;
Platform::Path filename;
bool hasSuggestion;
Constraint::Type suggestion;

View File

@ -138,7 +138,7 @@ void SolveSpaceUI::PopOntoCurrentFrom(UndoStack *uk) {
// sketch just changed a lot.
SS.GW.ClearSuper();
SS.TW.ClearSuper();
SS.ReloadAllImported(SS.saveFile);
SS.ReloadAllLinked(SS.saveFile);
SS.GenerateAll(SolveSpaceUI::Generate::ALL);
SS.ScheduleShowTW();

View File

@ -54,6 +54,7 @@ set(testsuite_SOURCES
request/cubic/test.cpp
request/cubic_periodic/test.cpp
request/datum_point/test.cpp
request/image/test.cpp
request/line_segment/test.cpp
request/ttf_text/test.cpp
group/link/test.cpp

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -0,0 +1,430 @@
±²³SolveSpaceREVa
Group.h.v=00000001
Group.type=5000
Group.name=#references
Group.color=ff000000
Group.skipFirst=0
Group.predef.swapUV=0
Group.predef.negateU=0
Group.predef.negateV=0
Group.visible=1
Group.suppress=0
Group.relaxConstraints=0
Group.allowRedundant=0
Group.allDimsReference=0
Group.scale=1.00000000000000000000
Group.remap={
}
AddGroup
Group.h.v=00000002
Group.type=5001
Group.order=1
Group.name=sketch-in-plane
Group.activeWorkplane.v=80020000
Group.color=ff000000
Group.subtype=6000
Group.skipFirst=0
Group.predef.q.w=1.00000000000000000000
Group.predef.origin.v=00010001
Group.predef.swapUV=0
Group.predef.negateU=0
Group.predef.negateV=0
Group.visible=1
Group.suppress=0
Group.relaxConstraints=0
Group.allowRedundant=0
Group.allDimsReference=0
Group.scale=1.00000000000000000000
Group.remap={
}
AddGroup
Group.h.v=00000003
Group.type=5300
Group.order=2
Group.name=normal
Group.activeWorkplane.v=00010000
Group.color=00646464
Group.skipFirst=0
Group.meshCombine=2
Group.predef.swapUV=0
Group.predef.negateU=0
Group.predef.negateV=0
Group.visible=1
Group.suppress=0
Group.relaxConstraints=0
Group.allowRedundant=0
Group.allDimsReference=0
Group.scale=1.00000000000000000000
Group.remap={
1 00010000 0
2 00010001 0
3 00010020 0
4 00020000 0
5 00020001 0
6 00020020 0
7 00030000 0
8 00030001 0
9 00030020 0
10 00040000 0
11 00040001 0
12 00040002 0
13 00040003 0
14 00040004 0
15 00040020 0
16 80020000 0
17 80020001 0
18 80020002 0
}
Group.impFileRel=normal.slvs
AddGroup
Param.h.v.=00010010
AddParam
Param.h.v.=00010011
AddParam
Param.h.v.=00010012
AddParam
Param.h.v.=00010020
Param.val=1.00000000000000000000
AddParam
Param.h.v.=00010021
AddParam
Param.h.v.=00010022
AddParam
Param.h.v.=00010023
AddParam
Param.h.v.=00020010
AddParam
Param.h.v.=00020011
AddParam
Param.h.v.=00020012
AddParam
Param.h.v.=00020020
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00020021
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00020022
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00020023
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00030010
AddParam
Param.h.v.=00030011
AddParam
Param.h.v.=00030012
AddParam
Param.h.v.=00030020
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00030021
Param.val=-0.50000000000000000000
AddParam
Param.h.v.=00030022
Param.val=-0.50000000000000000000
AddParam
Param.h.v.=00030023
Param.val=-0.50000000000000000000
AddParam
Param.h.v.=40000001
Param.val=1.00000000000000000000
AddParam
Param.h.v.=80030000
Param.val=5.00000000000000000000
AddParam
Param.h.v.=80030001
Param.val=5.00000000000000000000
AddParam
Param.h.v.=80030002
AddParam
Param.h.v.=80030003
Param.val=1.00000000000000000000
AddParam
Param.h.v.=80030004
AddParam
Param.h.v.=80030005
AddParam
Param.h.v.=80030006
AddParam
Request.h.v=00000001
Request.type=100
Request.group.v=00000001
Request.construction=0
AddRequest
Request.h.v=00000002
Request.type=100
Request.group.v=00000001
Request.construction=0
AddRequest
Request.h.v=00000003
Request.type=100
Request.group.v=00000001
Request.construction=0
AddRequest
Entity.h.v=00010000
Entity.type=10000
Entity.construction=0
Entity.point[0].v=00010001
Entity.normal.v=00010020
Entity.actVisible=1
AddEntity
Entity.h.v=00010001
Entity.type=2000
Entity.construction=1
Entity.actVisible=1
AddEntity
Entity.h.v=00010020
Entity.type=3000
Entity.construction=0
Entity.point[0].v=00010001
Entity.actNormal.w=1.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=00020000
Entity.type=10000
Entity.construction=0
Entity.point[0].v=00020001
Entity.normal.v=00020020
Entity.actVisible=1
AddEntity
Entity.h.v=00020001
Entity.type=2000
Entity.construction=1
Entity.actVisible=1
AddEntity
Entity.h.v=00020020
Entity.type=3000
Entity.construction=0
Entity.point[0].v=00020001
Entity.actNormal.w=0.50000000000000000000
Entity.actNormal.vx=0.50000000000000000000
Entity.actNormal.vy=0.50000000000000000000
Entity.actNormal.vz=0.50000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=00030000
Entity.type=10000
Entity.construction=0
Entity.point[0].v=00030001
Entity.normal.v=00030020
Entity.actVisible=1
AddEntity
Entity.h.v=00030001
Entity.type=2000
Entity.construction=1
Entity.actVisible=1
AddEntity
Entity.h.v=00030020
Entity.type=3000
Entity.construction=0
Entity.point[0].v=00030001
Entity.actNormal.w=0.50000000000000000000
Entity.actNormal.vx=-0.50000000000000000000
Entity.actNormal.vy=-0.50000000000000000000
Entity.actNormal.vz=-0.50000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80020000
Entity.type=10000
Entity.construction=0
Entity.point[0].v=80020002
Entity.normal.v=80020001
Entity.actVisible=0
AddEntity
Entity.h.v=80020001
Entity.type=3010
Entity.construction=0
Entity.point[0].v=80020002
Entity.actNormal.w=1.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80020002
Entity.type=2012
Entity.construction=1
Entity.actVisible=1
AddEntity
Entity.h.v=80030002
Entity.type=2011
Entity.construction=1
Entity.actPoint.x=5.00000000000000000000
Entity.actPoint.y=5.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80030003
Entity.type=3011
Entity.construction=0
Entity.point[0].v=80030002
Entity.actNormal.w=1.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80030005
Entity.type=2011
Entity.construction=1
Entity.actPoint.x=5.00000000000000000000
Entity.actPoint.y=5.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80030006
Entity.type=3011
Entity.construction=0
Entity.point[0].v=80030005
Entity.actNormal.w=0.50000000000000000000
Entity.actNormal.vx=0.50000000000000000000
Entity.actNormal.vy=0.50000000000000000000
Entity.actNormal.vz=0.50000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80030008
Entity.type=2011
Entity.construction=1
Entity.actPoint.x=5.00000000000000000000
Entity.actPoint.y=5.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80030009
Entity.type=3011
Entity.construction=0
Entity.point[0].v=80030008
Entity.actNormal.w=0.50000000000000000000
Entity.actNormal.vx=-0.50000000000000000000
Entity.actNormal.vy=-0.50000000000000000000
Entity.actNormal.vz=-0.50000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=8003000a
Entity.type=16000
Entity.construction=0
Entity.file=drawing.png
Entity.point[0].v=8003000b
Entity.point[1].v=8003000c
Entity.point[2].v=8003000d
Entity.point[3].v=8003000e
Entity.normal.v=8003000f
Entity.actVisible=1
AddEntity
Entity.h.v=8003000b
Entity.type=2011
Entity.construction=0
Entity.actPoint.x=5.00000000000000000000
Entity.actPoint.y=15.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=8003000c
Entity.type=2011
Entity.construction=0
Entity.actPoint.x=5.00000000000000000000
Entity.actPoint.y=5.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=8003000d
Entity.type=2011
Entity.construction=0
Entity.actPoint.x=20.00000000000000000000
Entity.actPoint.y=5.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=8003000e
Entity.type=2011
Entity.construction=0
Entity.actPoint.x=20.00000000000000000000
Entity.actPoint.y=15.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=8003000f
Entity.type=3011
Entity.construction=0
Entity.point[0].v=8003000b
Entity.actNormal.w=1.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80030011
Entity.type=3011
Entity.construction=0
Entity.point[0].v=80030012
Entity.actNormal.w=1.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80030012
Entity.type=2011
Entity.construction=1
Entity.actPoint.x=5.00000000000000000000
Entity.actPoint.y=5.00000000000000000000
Entity.actVisible=1
AddEntity
Constraint.h.v=00000001
Constraint.type=110
Constraint.group.v=00000003
Constraint.workplane.v=00010000
Constraint.valP.v=40000001
Constraint.entityA.v=80030006
Constraint.entityB.v=00020020
Constraint.other=0
Constraint.other2=0
Constraint.reference=0
AddConstraint

View File

@ -0,0 +1,319 @@
±²³SolveSpaceREVa
Group.h.v=00000001
Group.type=5000
Group.name=#references
Group.color=ff000000
Group.skipFirst=0
Group.predef.swapUV=0
Group.predef.negateU=0
Group.predef.negateV=0
Group.visible=1
Group.suppress=0
Group.relaxConstraints=0
Group.allowRedundant=0
Group.allDimsReference=0
Group.scale=1.00000000000000000000
Group.remap={
}
AddGroup
Group.h.v=00000002
Group.type=5001
Group.order=1
Group.name=sketch-in-plane
Group.activeWorkplane.v=80020000
Group.color=ff000000
Group.subtype=6000
Group.skipFirst=0
Group.predef.q.w=1.00000000000000000000
Group.predef.origin.v=00010001
Group.predef.swapUV=0
Group.predef.negateU=0
Group.predef.negateV=0
Group.visible=1
Group.suppress=0
Group.relaxConstraints=0
Group.allowRedundant=0
Group.allDimsReference=0
Group.scale=1.00000000000000000000
Group.remap={
}
AddGroup
Param.h.v.=00010010
AddParam
Param.h.v.=00010011
AddParam
Param.h.v.=00010012
AddParam
Param.h.v.=00010020
Param.val=1.00000000000000000000
AddParam
Param.h.v.=00010021
AddParam
Param.h.v.=00010022
AddParam
Param.h.v.=00010023
AddParam
Param.h.v.=00020010
AddParam
Param.h.v.=00020011
AddParam
Param.h.v.=00020012
AddParam
Param.h.v.=00020020
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00020021
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00020022
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00020023
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00030010
AddParam
Param.h.v.=00030011
AddParam
Param.h.v.=00030012
AddParam
Param.h.v.=00030020
Param.val=0.50000000000000000000
AddParam
Param.h.v.=00030021
Param.val=-0.50000000000000000000
AddParam
Param.h.v.=00030022
Param.val=-0.50000000000000000000
AddParam
Param.h.v.=00030023
Param.val=-0.50000000000000000000
AddParam
Param.h.v.=00040010
AddParam
Param.h.v.=00040011
Param.val=10.00000000000000000000
AddParam
Param.h.v.=00040013
AddParam
Param.h.v.=00040014
AddParam
Param.h.v.=00040016
Param.val=15.00000000000000000000
AddParam
Param.h.v.=00040017
AddParam
Param.h.v.=00040019
Param.val=15.00000000000000000000
AddParam
Param.h.v.=0004001a
Param.val=10.00000000000000000000
AddParam
Request.h.v=00000001
Request.type=100
Request.group.v=00000001
Request.construction=0
AddRequest
Request.h.v=00000002
Request.type=100
Request.group.v=00000001
Request.construction=0
AddRequest
Request.h.v=00000003
Request.type=100
Request.group.v=00000001
Request.construction=0
AddRequest
Request.h.v=00000004
Request.type=700
Request.workplane.v=80020000
Request.group.v=00000002
Request.construction=0
Request.file=drawing.png
Request.aspectRatio=1.50000000000000000000
AddRequest
Entity.h.v=00010000
Entity.type=10000
Entity.construction=0
Entity.point[0].v=00010001
Entity.normal.v=00010020
Entity.actVisible=1
AddEntity
Entity.h.v=00010001
Entity.type=2000
Entity.construction=1
Entity.actVisible=1
AddEntity
Entity.h.v=00010020
Entity.type=3000
Entity.construction=0
Entity.point[0].v=00010001
Entity.actNormal.w=1.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=00020000
Entity.type=10000
Entity.construction=0
Entity.point[0].v=00020001
Entity.normal.v=00020020
Entity.actVisible=1
AddEntity
Entity.h.v=00020001
Entity.type=2000
Entity.construction=1
Entity.actVisible=1
AddEntity
Entity.h.v=00020020
Entity.type=3000
Entity.construction=0
Entity.point[0].v=00020001
Entity.actNormal.w=0.50000000000000000000
Entity.actNormal.vx=0.50000000000000000000
Entity.actNormal.vy=0.50000000000000000000
Entity.actNormal.vz=0.50000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=00030000
Entity.type=10000
Entity.construction=0
Entity.point[0].v=00030001
Entity.normal.v=00030020
Entity.actVisible=1
AddEntity
Entity.h.v=00030001
Entity.type=2000
Entity.construction=1
Entity.actVisible=1
AddEntity
Entity.h.v=00030020
Entity.type=3000
Entity.construction=0
Entity.point[0].v=00030001
Entity.actNormal.w=0.50000000000000000000
Entity.actNormal.vx=-0.50000000000000000000
Entity.actNormal.vy=-0.50000000000000000000
Entity.actNormal.vz=-0.50000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=00040000
Entity.type=16000
Entity.construction=0
Entity.file=drawing.png
Entity.point[0].v=00040001
Entity.point[1].v=00040002
Entity.point[2].v=00040003
Entity.point[3].v=00040004
Entity.normal.v=00040020
Entity.workplane.v=80020000
Entity.actVisible=1
AddEntity
Entity.h.v=00040001
Entity.type=2001
Entity.construction=0
Entity.workplane.v=80020000
Entity.actPoint.y=10.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=00040002
Entity.type=2001
Entity.construction=0
Entity.workplane.v=80020000
Entity.actVisible=1
AddEntity
Entity.h.v=00040003
Entity.type=2001
Entity.construction=0
Entity.workplane.v=80020000
Entity.actPoint.x=15.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=00040004
Entity.type=2001
Entity.construction=0
Entity.workplane.v=80020000
Entity.actPoint.x=15.00000000000000000000
Entity.actPoint.y=10.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=00040020
Entity.type=3001
Entity.construction=0
Entity.point[0].v=00040001
Entity.workplane.v=80020000
Entity.actNormal.w=1.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80020000
Entity.type=10000
Entity.construction=0
Entity.point[0].v=80020002
Entity.normal.v=80020001
Entity.actVisible=1
AddEntity
Entity.h.v=80020001
Entity.type=3010
Entity.construction=0
Entity.point[0].v=80020002
Entity.actNormal.w=1.00000000000000000000
Entity.actVisible=1
AddEntity
Entity.h.v=80020002
Entity.type=2012
Entity.construction=1
Entity.actVisible=1
AddEntity

View File

@ -0,0 +1,14 @@
#include "harness.h"
TEST_CASE(normal_roundtrip) {
CHECK_LOAD("normal.slvs");
// Can't render images through cairo for now.
// CHECK_RENDER("normal.png");
CHECK_SAVE("normal.slvs");
}
TEST_CASE(linked_roundtrip) {
CHECK_LOAD("linked.slvs");
// CHECK_RENDER("linked.png");
CHECK_SAVE("linked.slvs");
}