Implement an image request.

This commit is contained in:
EvilSpirit 2016-11-29 23:49:20 +07:00 committed by whitequark
parent e2e74762f4
commit 5744d1d599
29 changed files with 291 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. * 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 * TTF text request has two additional points on the right side, which allow
constraining the width of text. 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 * Irrelevant points (e.g. arc center point) are not counted when estimating
the bounding box used to compute chord tolerance. the bounding box used to compute chord tolerance.
* When adding a constraint which has a label and is redundant with another * 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/equal.png
icons/graphics-window/extrude.png icons/graphics-window/extrude.png
icons/graphics-window/horiz.png icons/graphics-window/horiz.png
icons/graphics-window/image.png
icons/graphics-window/in3d.png icons/graphics-window/in3d.png
icons/graphics-window/lathe.png icons/graphics-window/lathe.png
icons/graphics-window/length.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 // Copyright 2016 Aleksey Egorov
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
uniform vec4 color; uniform vec4 color;
uniform sampler2D texture;
void main() { void main() {
if(texture2D(texture, gl_FragCoord.xy / 32.0).a < 0.5) discard;
gl_FragColor = color; gl_FragColor = color;
} }

View File

@ -195,6 +195,16 @@ void TextWindow::DescribeSelection() {
} }
break; 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: default:
Printf(true, "%Ft?? ENTITY%E"); Printf(true, "%Ft?? ENTITY%E");

View File

@ -394,6 +394,9 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
for(Entity &e : SK.entity) { for(Entity &e : SK.entity) {
if(!e.IsVisible()) continue; 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. // Don't hover whatever's being dragged.
if(IsFromPending(e.h.request())) { if(IsFromPending(e.h.request())) {
// The one exception is when we're creating a new cubic; we // 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:
case Type::CUBIC_PERIODIC: case Type::CUBIC_PERIODIC:
case Type::TTF_TEXT: case Type::TTF_TEXT:
case Type::IMAGE:
refs->push_back(SK.GetEntity(point[0])->PointGetNum()); refs->push_back(SK.GetEntity(point[0])->PointGetNum());
break; break;
@ -695,6 +696,54 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
} }
return; 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_NORMAL_PT:
case Type::FACE_XPROD: case Type::FACE_XPROD:

View File

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

View File

@ -33,6 +33,7 @@ void SolveSpaceUI::ClearExisting() {
SK.entity.Clear(); SK.entity.Clear();
SK.param.Clear(); SK.param.Clear();
images.clear();
} }
hGroup SolveSpaceUI::CreateDefaultDrawingGroup() { hGroup SolveSpaceUI::CreateDefaultDrawingGroup() {
@ -131,6 +132,7 @@ const SolveSpaceUI::SaveTable SolveSpaceUI::SAVED[] = {
{ 'r', "Request.style", 'x', &(SS.sv.r.style) }, { 'r', "Request.style", 'x', &(SS.sv.r.style) },
{ 'r', "Request.str", 'S', &(SS.sv.r.str) }, { 'r', "Request.str", 'S', &(SS.sv.r.str) },
{ 'r', "Request.font", 'S', &(SS.sv.r.font) }, { 'r', "Request.font", 'S', &(SS.sv.r.font) },
{ 'r', "Request.file", 'P', &(SS.sv.r.file) },
{ 'r', "Request.aspectRatio", 'f', &(SS.sv.r.aspectRatio) }, { 'r', "Request.aspectRatio", 'f', &(SS.sv.r.aspectRatio) },
{ 'e', "Entity.h.v", 'x', &(SS.sv.e.h.v) }, { '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.style", 'x', &(SS.sv.e.style) },
{ 'e', "Entity.str", 'S', &(SS.sv.e.str) }, { 'e', "Entity.str", 'S', &(SS.sv.e.str) },
{ 'e', "Entity.font", 'S', &(SS.sv.e.font) }, { '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[0].v", 'x', &(SS.sv.e.point[0].v) },
{ 'e', "Entity.point[1].v", 'x', &(SS.sv.e.point[1].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) }, { '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 'x': sscanf(val, "%x", &u); p->x()= u; break;
case 'P': { 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; break;
} }
@ -537,7 +543,7 @@ bool SolveSpaceUI::LoadFromFile(const Platform::Path &filename, bool canCancel)
NewFile(); NewFile();
} }
} }
if(!ReloadAllImported(filename, canCancel)) { if(!ReloadAllLinked(filename, canCancel)) {
return false; return false;
} }
UpgradeLegacyData(); UpgradeLegacyData();
@ -577,7 +583,7 @@ void SolveSpaceUI::UpgradeLegacyData() {
Entity *b = entity.FindById(text->point[2]); Entity *b = entity.FindById(text->point[2]);
Entity *c = entity.FindById(text->point[3]); Entity *c = entity.FindById(text->point[3]);
ExprVector bex, cex; ExprVector bex, cex;
text->TtfTextGetPointsExprs(&bex, &cex); text->RectGetPointsExprs(&bex, &cex);
b->PointForceParamTo(bex.Eval()); b->PointForceParamTo(bex.Eval());
c->PointForceParamTo(cex.Eval()); c->PointForceParamTo(cex.Eval());
} }
@ -819,11 +825,11 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityLi
return true; 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; std::map<Platform::Path, Platform::Path, Platform::PathLess> linkMap;
allConsistent = false; allConsistent = false;
for(Group &g : SK.group) { for(Group &g : SK.group) {
if(g.type != Group::Type::LINKED) continue; if(g.type != Group::Type::LINKED) continue;
@ -838,10 +844,16 @@ bool SolveSpaceUI::ReloadAllImported(const Platform::Path &filename, bool canCan
try_again: try_again:
if(LoadEntitiesFromFile(g.linkFile, &g.impEntity, &g.impMesh, &g.impShell)) { 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) { } else if(linkMap.count(g.linkFile) == 0) {
// The file was moved; prompt the user for its new location. // 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: { case DIALOG_YES: {
Platform::Path newLinkFile; Platform::Path newLinkFile;
if(GetOpenFile(&newLinkFile, "", SlvsFileFilter)) { 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; 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, N_("&Bezier Cubic Spline"), Command::CUBIC, 'B', TN, mReq },
{ 1, NULL, Command::NONE, 0, TN, NULL }, { 1, NULL, Command::NONE, 0, TN, NULL },
{ 1, N_("&Text in TrueType Font"), Command::TTF_TEXT, 'T', TN, mReq }, { 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, NULL, Command::NONE, 0, TN, NULL },
{ 1, N_("To&ggle Construction"), Command::CONSTRUCTION, 'G', TN, mReq }, { 1, N_("To&ggle Construction"), Command::CONSTRUCTION, 'G', TN, mReq },
{ 1, N_("Tangent &Arc at Point"), Command::TANGENT_ARC, S|'A', 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; break;
case Command::REGEN_ALL: case Command::REGEN_ALL:
SS.ReloadAllImported(SS.saveFile); SS.images.clear();
SS.ReloadAllLinked(SS.saveFile);
SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE); SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
SS.ScheduleShowTW(); SS.ScheduleShowTW();
break; break;
@ -1015,6 +1017,12 @@ void GraphicsWindow::MenuRequest(Command id) {
case Command::WORKPLANE: s = _("click origin of workplane"); goto c; case Command::WORKPLANE: s = _("click origin of workplane"); goto c;
case Command::RECTANGLE: s = _("click one corner of rectangle"); 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::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: c:
SS.GW.pending.operation = GraphicsWindow::Pending::COMMAND; SS.GW.pending.operation = GraphicsWindow::Pending::COMMAND;
SS.GW.pending.command = id; SS.GW.pending.command = id;

View File

@ -274,7 +274,7 @@ void Group::MenuGroup(Command id) {
Group *gg = SK.GetGroup(g.h); Group *gg = SK.GetGroup(g.h);
if(gg->type == Type::LINKED) { if(gg->type == Type::LINKED) {
SS.ReloadAllImported(SS.saveFile); SS.ReloadAllLinked(SS.saveFile);
} }
gg->clean = false; gg->clean = false;
SS.GW.activeGroup = gg->h; SS.GW.activeGroup = gg->h;
@ -801,6 +801,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
en.style = ep->style; en.style = ep->style;
en.str = ep->str; en.str = ep->str;
en.font = ep->font; en.font = ep->font;
en.file = ep->file;
switch(ep->type) { switch(ep->type) {
case Entity::Type::WORKPLANE: 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 ||
e->type == Entity::Type::CUBIC_PERIODIC || e->type == Entity::Type::CUBIC_PERIODIC ||
e->type == Entity::Type::CIRCLE || e->type == Entity::Type::CIRCLE ||
e->type == Entity::Type::TTF_TEXT) e->type == Entity::Type::TTF_TEXT ||
e->type == Entity::Type::IMAGE)
{ {
int pts; int pts;
EntReqTable::GetEntityInfo(e->type, e->extraPoints, EntReqTable::GetEntityInfo(e->type, e->extraPoints,
@ -1146,6 +1147,27 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
break; 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: { case Command::COMMENT: {
ClearSuper(); ClearSuper();
Constraint c = {}; Constraint c = {};

View File

@ -76,7 +76,7 @@ File formats:
export-wireframe:%s export-wireframe:%s
export-mesh:%s export-mesh:%s
export-surfaces:%s export-surfaces:%s
)", FormatListFromFileFilter(PngFileFilter).c_str(), )", FormatListFromFileFilter(RasterFileFilter).c_str(),
FormatListFromFileFilter(VectorFileFilter).c_str(), FormatListFromFileFilter(VectorFileFilter).c_str(),
FormatListFromFileFilter(Vector3dFileFilter).c_str(), FormatListFromFileFilter(Vector3dFileFilter).c_str(),
FormatListFromFileFilter(MeshFileFilter).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, void ObjectPicker::DrawPixmap(std::shared_ptr<const Pixmap> pm,
const Vector &o, const Vector &u, const Vector &v, const Vector &o, const Vector &u, const Vector &v,
const Point2d &ta, const Point2d &tb, Canvas::hFill hcf) { 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) { bool ObjectPicker::Pick(std::function<void()> drawFn) {

View File

@ -769,11 +769,11 @@ public:
virtual Canvas::Layer GetLayer() const override { return stroke.layer; }; virtual Canvas::Layer GetLayer() const override { return stroke.layer; };
virtual int GetZIndex() const override { return stroke.zIndex; }; 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) { Canvas::Stroke *stroke) {
PointDrawCall *dc = new PointDrawCall(); PointDrawCall *dc = new PointDrawCall();
dc->stroke = *stroke; dc->stroke = *stroke;
dc->handle = renderer->imeshRenderer.Add(im); dc->handle = renderer->imeshRenderer.Add(mesh);
return std::shared_ptr<DrawCall>(dc); 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 { class MeshDrawCall : public DrawCall {
public: public:
// Key // Key
@ -813,22 +849,22 @@ public:
return std::shared_ptr<DrawCall>(dc); 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); glCullFace(cullFace);
ssglDepthRange(fill->layer, fill->zIndex); ssglDepthRange(fill.layer, fill.zIndex);
if(fill->pattern != Canvas::FillPattern::SOLID) { if(fill.pattern != Canvas::FillPattern::SOLID) {
renderer->SelectMask(fill->pattern); renderer->SelectMask(fill.pattern);
} else if(fill->texture) { } else if(fill.texture) {
renderer->SelectTexture(fill->texture); renderer->SelectTexture(fill.texture);
} else { } else {
renderer->SelectMask(Canvas::FillPattern::SOLID); renderer->SelectMask(Canvas::FillPattern::SOLID);
} }
if(isShaded) { if(isShaded) {
renderer->meshRenderer.UseShaded(renderer->lighting); renderer->meshRenderer.UseShaded(renderer->lighting);
} else { } 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 { void Draw(OpenGl2Renderer *renderer) override {
@ -836,8 +872,8 @@ public:
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
if(hasFillBack) if(hasFillBack)
DrawFace(renderer, GL_FRONT, &fillBack); DrawFace(renderer, GL_FRONT, fillBack);
DrawFace(renderer, GL_BACK, &fillFront); DrawFace(renderer, GL_BACK, fillFront);
glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_POLYGON_OFFSET_FILL);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
@ -977,7 +1013,14 @@ public:
void DrawPixmap(std::shared_ptr<const Pixmap> pm, void DrawPixmap(std::shared_ptr<const Pixmap> pm,
const Vector &o, const Vector &u, const Vector &v, const Vector &o, const Vector &u, const Vector &v,
const Point2d &ta, const Point2d &tb, hFill hcf) override { 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 { 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::CIRCLE, Entity::Type::CIRCLE, 1, false, true, true },
{ Request::Type::ARC_OF_CIRCLE, Entity::Type::ARC_OF_CIRCLE, 3, false, true, false }, { 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::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, static void CopyEntityInfo(const EntReqMapping *te, int extraPoints,
@ -102,6 +103,20 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
break; 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 default: // most requests don't do anything else
break; break;
} }
@ -118,6 +133,7 @@ void Request::Generate(IdList<Entity,hEntity> *entity,
e.construction = construction; e.construction = construction;
e.str = str; e.str = str;
e.font = font; e.font = font;
e.file = file;
e.aspectRatio = aspectRatio; e.aspectRatio = aspectRatio;
e.h = h.entity(0); e.h = h.entity(0);
@ -205,8 +221,9 @@ std::string Request::DescriptionString() const {
case Type::CUBIC: s = "cubic-bezier"; break; case Type::CUBIC: s = "cubic-bezier"; break;
case Type::CUBIC_PERIODIC: s = "periodic-cubic"; break; case Type::CUBIC_PERIODIC: s = "periodic-cubic"; break;
case Type::CIRCLE: s = "circle"; 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::TTF_TEXT: s = "ttf-text"; break;
case Type::IMAGE: s = "image"; break;
} }
} }
ssassert(s != NULL, "Unexpected request type"); ssassert(s != NULL, "Unexpected request type");

View File

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

View File

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

View File

@ -620,6 +620,11 @@ public:
} UndoStack; } UndoStack;
UndoStack undo; UndoStack undo;
UndoStack redo; 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 UndoEnableMenus();
void UndoRemember(); void UndoRemember();
void UndoUndo(); void UndoUndo();
@ -743,7 +748,7 @@ public:
void UpgradeLegacyData(); void UpgradeLegacyData();
bool LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le, bool LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
SMesh *m, SShell *sh); 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 // And the various export options
void ExportAsPngTo(const Platform::Path &filename); void ExportAsPngTo(const Platform::Path &filename);
void ExportMeshTo(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') { if(link == 'l') {
Platform::Path bgImageFile; Platform::Path bgImageFile;
if(GetOpenFile(&bgImageFile, "", PngFileFilter)) { if(GetOpenFile(&bgImageFile, "", RasterFileFilter)) {
FILE *f = OpenFile(bgImageFile, "rb"); FILE *f = OpenFile(bgImageFile, "rb");
if(f) { if(f) {
SS.bgImage.pixmap = Pixmap::ReadPng(f); SS.bgImage.pixmap = Pixmap::ReadPng(f);

View File

@ -327,7 +327,7 @@ void TextWindow::ShowGroupInfo() {
} }
} else if(g->type == Group::Type::LINKED) { } else if(g->type == Group::Type::LINKED) {
Printf(true, " %Ftlink geometry from file%E"); 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()) { if(relativePath.IsEmpty()) {
Printf(false, "%Ba '%s'", g->linkFile.raw.c_str()); Printf(false, "%Ba '%s'", g->linkFile.raw.c_str());
} else { } else {

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Binary file not shown.

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");
}