From 5744d1d599dfaeaa768e1667509eee1fda3b8aea Mon Sep 17 00:00:00 2001 From: EvilSpirit Date: Tue, 29 Nov 2016 23:49:20 +0700 Subject: [PATCH] Implement an image request. --- CHANGELOG.md | 1 + res/CMakeLists.txt | 1 + res/icons/graphics-window/image.png | Bin 0 -> 1025 bytes res/shaders/imesh.frag | 2 + src/describescreen.cpp | 10 + src/draw.cpp | 3 + src/drawentity.cpp | 49 ++++ src/entity.cpp | 8 +- src/file.cpp | 79 ++++- src/graphicswin.cpp | 10 +- src/group.cpp | 3 +- src/mouse.cpp | 24 +- src/platform/climain.cpp | 2 +- src/render/render.cpp | 2 +- src/render/rendergl2.cpp | 69 ++++- src/request.cpp | 19 +- src/sketch.h | 10 +- src/solvespace.cpp | 2 +- src/solvespace.h | 7 +- src/style.cpp | 2 +- src/textscreens.cpp | 2 +- src/toolbar.cpp | 2 + src/ui.h | 4 +- src/undoredo.cpp | 2 +- test/CMakeLists.txt | 1 + test/request/image/drawing.png | Bin 0 -> 8565 bytes test/request/image/linked.slvs | 430 ++++++++++++++++++++++++++++ test/request/image/normal.slvs | 319 +++++++++++++++++++++ test/request/image/test.cpp | 14 + 29 files changed, 1040 insertions(+), 37 deletions(-) create mode 100644 res/icons/graphics-window/image.png create mode 100644 test/request/image/drawing.png create mode 100644 test/request/image/linked.slvs create mode 100644 test/request/image/normal.slvs create mode 100644 test/request/image/test.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b194fdd..c85bda3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/res/CMakeLists.txt b/res/CMakeLists.txt index b91f2165..200eb6fb 100644 --- a/res/CMakeLists.txt +++ b/res/CMakeLists.txt @@ -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 diff --git a/res/icons/graphics-window/image.png b/res/icons/graphics-window/image.png new file mode 100644 index 0000000000000000000000000000000000000000..d652d8e77c4297cc57816378acc0b9fa50ac3429 GIT binary patch literal 1025 zcmV+c1pfPpP)(_`g8%^e{{R4h=l}px2mk>USO5SzmjD14Z`WEM zkN^M!08mU+MgRZ*0L%dX{{9040|W#F1qB5L1_lQQ2M7oV2?+@b3JMDg3k(bl4Gj$r z4h|0w4-gO#5fKp*5)u;=6BHB_6%`d078Vy57Z?~A85tQG8X6lL8yp-Q9UUDW9v&Yb zA0QwgAt50mA|fLrBP1jwB_$;$CMG8*CnzW=DJdx`Dk>{0D=aK5EiElBE-o)GFEB7L zF)=YRGBPtWGc+_bH8nLhHa0gmH#j&rIXO8xIyyT$J3Kr*Jv}`>K0ZG`KR`f0K|w)6 zLPA4BLqtSGMMXtMMn*?RM@UFWNl8gcN=i#hOH52mO-)TsPEJoxPf$=$QBhG+Qc_b> zQ&dz`RaI41R#sP6S6EnBSy@?HT3TCMTU=aRU0q#XUS3~cUtnNhVPRonVq#-sV`OAx zWo2b%W@cw+XJ}|>X=!O{YHDk1Yiw+6ZEbCCZf7mzbEC znVFfInwp!No1C1Sot>SYo}QndpP-Ll?si~=| zs;aB2tE{Z7t*x!DuCA}IuduMNv9YnTva++Yv$V9dwY9ajwzjvox45{txw*Nzy1Ki& zyS%)-y}iA@zP`V|zreu2!NI}8!otJD!^FhI#l^+O#>U6T$H>UY$;rve%F4^j%goHo z&CSiu&d$%z&(P4&(b3V;($dq@)6~?|)z#J3*4Ee8*Vx$D+1c6J+S=RO+uYpT-QC^Z z-rnEe-{9cj;o;%p;^O1ulq(=H}<;=jiC@>FMd}>gwz3>+J08?d|RE?(XmJ z@9^;O@$vEU^78ZZ^Yrxe_4W1k_V)Mp_xSku`T6=PR2gr1-#QAO}rfile.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"); diff --git a/src/draw.cpp b/src/draw.cpp index e7e007cb..1a638a7b 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -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 diff --git a/src/drawentity.cpp b/src/drawentity.cpp index 1e5a31ec..ce455f1d 100644 --- a/src/drawentity.cpp +++ b/src/drawentity.cpp @@ -104,6 +104,7 @@ void Entity::GetReferencePoints(std::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; + 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: diff --git a/src/entity.cpp b/src/entity.cpp index 8d579d1f..4f4eabe1 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -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 *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 *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); diff --git a/src/file.cpp b/src/file.cpp index 99ac2c7d..5b8447ac 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -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 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; + 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; +} diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp index e634e9e8..83d5f93e 100644 --- a/src/graphicswin.cpp +++ b/src/graphicswin.cpp @@ -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; diff --git a/src/group.cpp b/src/group.cpp index 4671c86c..07d63a8b 100644 --- a/src/group.cpp +++ b/src/group.cpp @@ -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 *el, en.style = ep->style; en.str = ep->str; en.font = ep->font; + en.file = ep->file; switch(ep->type) { case Entity::Type::WORKPLANE: diff --git a/src/mouse.cpp b/src/mouse.cpp index 3247cc59..d3dd7c9b 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -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 = {}; diff --git a/src/platform/climain.cpp b/src/platform/climain.cpp index 52881ac8..af8d7b07 100644 --- a/src/platform/climain.cpp +++ b/src/platform/climain.cpp @@ -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(), diff --git a/src/render/render.cpp b/src/render/render.cpp index a9e5e27a..16a2cc81 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -441,7 +441,7 @@ void ObjectPicker::DrawFaces(const SMesh &m, const std::vector &faces, void ObjectPicker::DrawPixmap(std::shared_ptr 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 drawFn) { diff --git a/src/render/rendergl2.cpp b/src/render/rendergl2.cpp index 5aa7110d..5830199f 100644 --- a/src/render/rendergl2.cpp +++ b/src/render/rendergl2.cpp @@ -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 Create(OpenGl2Renderer *renderer, const SIndexedMesh &im, + static std::shared_ptr 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(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 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(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(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 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 pm) override { diff --git a/src/request.cpp b/src/request.cpp index 0fdd0969..5046c018 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -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, break; } + case Type::IMAGE: { + auto image = SS.images.find(file); + if(image != SS.images.end()) { + std::shared_ptr 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, 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"); diff --git a/src/sketch.h b/src/sketch.h index 35c26023..c7950c3a 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -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 *l, Expr *expr, int index) const; void GenerateEquations(IdList *l) const; diff --git a/src/solvespace.cpp b/src/solvespace.cpp index 67f76341..9021a1e5 100644 --- a/src/solvespace.cpp +++ b/src/solvespace.cpp @@ -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; } diff --git a/src/solvespace.h b/src/solvespace.h index 77181913..82c37d40 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -620,6 +620,11 @@ public: } UndoStack; UndoStack undo; UndoStack redo; + + std::map, 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); diff --git a/src/style.cpp b/src/style.cpp index eb30516f..c08f7ed6 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -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); diff --git a/src/textscreens.cpp b/src/textscreens.cpp index 49ba0ac8..79fc46c1 100644 --- a/src/textscreens.cpp +++ b/src/textscreens.cpp @@ -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 { diff --git a/src/toolbar.cpp b/src/toolbar.cpp index 6000be74..b467d44b 100644 --- a/src/toolbar.cpp +++ b/src/toolbar.cpp @@ -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, diff --git a/src/ui.h b/src/ui.h index bcf1d237..bdef6ce0 100644 --- a/src/ui.h +++ b/src/ui.h @@ -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; diff --git a/src/undoredo.cpp b/src/undoredo.cpp index be1fef2f..1739caf1 100644 --- a/src/undoredo.cpp +++ b/src/undoredo.cpp @@ -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(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bac0fa74..bb70d978 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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 diff --git a/test/request/image/drawing.png b/test/request/image/drawing.png new file mode 100644 index 0000000000000000000000000000000000000000..56bedb7523f0a12bb142429e073d77ef8b58f8ac GIT binary patch literal 8565 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 literal 0 HcmV?d00001 diff --git a/test/request/image/linked.slvs b/test/request/image/linked.slvs new file mode 100644 index 00000000..48190587 --- /dev/null +++ b/test/request/image/linked.slvs @@ -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 + diff --git a/test/request/image/normal.slvs b/test/request/image/normal.slvs new file mode 100644 index 00000000..dab1385a --- /dev/null +++ b/test/request/image/normal.slvs @@ -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 + diff --git a/test/request/image/test.cpp b/test/request/image/test.cpp new file mode 100644 index 00000000..da0954e0 --- /dev/null +++ b/test/request/image/test.cpp @@ -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"); +}