From 74cb1f589c93512ecf16ef9250e48a02d5b21f29 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 11 Oct 2016 01:58:04 +0000 Subject: [PATCH] Add two more points to the TTF text request. These points can be used for constraining the width of the text (or to the width of the text). The main parts of the commit are: * TtfFont is restructured to be able to return the aspect ratio for a given string. * This aspect ratio is written to the savefile, such that even if the font is missing, the sketch would still be solved correctly. * The two additional points are constrained via perpendicularly to the two main points (which form a v vector). The compatibility features are as follows: * When the font is missing in old files, 1:1 aspect ratio is used, which works for the replacement symbol anyhow. * When the two additional points are missing in old files, their would-be positions are calculated and they are moved there, avoiding 'jumping' of underconstrained sketches. --- CHANGELOG.md | 2 + src/entity.cpp | 61 ++++++++++++++++++++++++++++++ src/file.cpp | 49 ++++++++++++++++++++++++ src/request.cpp | 25 +++++++++++- src/sketch.h | 11 +++++- src/solvespace.h | 1 + src/ttf.cpp | 52 +++++++++++++++++++++++-- src/ttf.h | 3 ++ test/request/ttf_text/normal.png | Bin 6138 -> 6158 bytes test/request/ttf_text/normal.slvs | 37 ++++++++++++++++++ 10 files changed, 233 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0beb5a9..14c9ee56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ New sketch features: boolean operation, to increase performance. * Translate and rotate groups can create n-dimensional arrays using the "difference" and "assembly" boolean operations. + * TTF text request has two additional points on the right side, which allow + constraining the width of text. * Irrelevant points (e.g. arc center point) are not counted when estimating the bounding box used to compute chord tolerance. diff --git a/src/entity.cpp b/src/entity.cpp index 3a48ed3f..ea71806b 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -367,6 +367,23 @@ ExprQuaternion EntityBase::NormalGetExprs() const { return q; } +void EntityBase::PointForceParamTo(Vector p) { + switch(type) { + case Type::POINT_IN_3D: + SK.GetParam(param[0])->val = p.x; + SK.GetParam(param[1])->val = p.y; + SK.GetParam(param[2])->val = p.z; + break; + + case Type::POINT_IN_2D: + SK.GetParam(param[0])->val = p.x; + SK.GetParam(param[1])->val = p.y; + break; + + default: ssassert(false, "Unexpected entity type"); + } +} + void EntityBase::PointForceTo(Vector p) { switch(type) { case Type::POINT_IN_3D: @@ -550,6 +567,13 @@ void EntityBase::PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) con } } +ExprVector EntityBase::PointGetExprsInWorkplane(hEntity wrkpl) const { + ExprVector r; + PointGetExprsInWorkplane(wrkpl, &r.x, &r.y); + r.z = Expr::From(0.0); + return r; +} + void EntityBase::PointForceQuaternionTo(Quaternion q) { ssassert(type == Type::POINT_N_ROT_TRANS, "Unexpected entity type"); @@ -738,6 +762,23 @@ Vector EntityBase::EndpointFinish() const { } else ssassert(false, "Unexpected entity type"); } +void EntityBase::TtfTextGetPointsExprs(ExprVector *eb, ExprVector *ec) const { + EntityBase *a = SK.GetEntity(point[0]); + EntityBase *o = SK.GetEntity(point[1]); + + // Write equations for each point in the current workplane. + // This reduces the complexity of resulting equations. + ExprVector ea = a->PointGetExprsInWorkplane(workplane); + ExprVector eo = o->PointGetExprsInWorkplane(workplane); + + // Take perpendicular vector and scale it by aspect ratio. + ExprVector eu = ea.Minus(eo); + ExprVector ev = ExprVector::From(eu.y, eu.x->Negate(), eu.z).ScaledBy(Expr::From(aspectRatio)); + + *eb = eo.Plus(ev); + *ec = eo.Plus(eu).Plus(ev); +} + void EntityBase::AddEq(IdList *l, Expr *expr, int index) const { Equation eq; eq.e = expr; @@ -752,6 +793,7 @@ void EntityBase::GenerateEquations(IdList *l) const { AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0); break; } + case Type::ARC_OF_CIRCLE: { // If this is a copied entity, with its point already fixed // with respect to each other, then we don't want to generate @@ -780,6 +822,25 @@ void EntityBase::GenerateEquations(IdList *l) const { AddEq(l, ra->Minus(rb), 0); break; } + + case Type::TTF_TEXT: { + EntityBase *b = SK.GetEntity(point[2]); + EntityBase *c = SK.GetEntity(point[3]); + ExprVector eb = b->PointGetExprsInWorkplane(workplane); + ExprVector ec = c->PointGetExprsInWorkplane(workplane); + + ExprVector ebp, ecp; + TtfTextGetPointsExprs(&ebp, &ecp); + + ExprVector beq = eb.Minus(ebp); + AddEq(l, beq.x, 0); + AddEq(l, beq.y, 1); + ExprVector ceq = ec.Minus(ecp); + AddEq(l, ceq.x, 2); + AddEq(l, ceq.y, 3); + break; + } + default: // Most entities do not generate equations. break; } diff --git a/src/file.cpp b/src/file.cpp index 4b199d19..bff0c844 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -131,6 +131,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.aspectRatio", 'f', &(SS.sv.r.aspectRatio) }, { 'e', "Entity.h.v", 'x', &(SS.sv.e.h.v) }, { 'e', "Entity.type", 'd', &(SS.sv.e.type) }, @@ -512,9 +513,57 @@ bool SolveSpaceUI::LoadFromFile(const std::string &filename) { } } + UpgradeLegacyData(); + return true; } +void SolveSpaceUI::UpgradeLegacyData() { + for(Request &r : SK.request) { + switch(r.type) { + // TTF text requests saved in versions prior to 3.0 only have two + // reference points (origin and origin plus v); version 3.0 adds two + // more points, and if we don't do anything, then they will appear + // at workplane origin, and the solver will mess up the sketch if + // it is not fully constrained. + case Request::Type::TTF_TEXT: { + IdList entity = {}; + IdList param = {}; + r.Generate(&entity, ¶m); + + // If we didn't load all of the entities and params that this + // request would generate, then add them now, so that we can + // force them to their appropriate positions. + for(Param &p : param) { + if(SK.param.FindByIdNoOops(p.h) != NULL) continue; + SK.param.Add(&p); + } + bool allPointsExist = true; + for(Entity &e : entity) { + if(SK.entity.FindByIdNoOops(e.h) != NULL) continue; + SK.entity.Add(&e); + allPointsExist = false; + } + + if(!allPointsExist) { + Entity *text = entity.FindById(r.h.entity(0)); + Entity *b = entity.FindById(text->point[2]); + Entity *c = entity.FindById(text->point[3]); + ExprVector bex, cex; + text->TtfTextGetPointsExprs(&bex, &cex); + b->PointForceParamTo(bex.Eval()); + c->PointForceParamTo(cex.Eval()); + } + entity.Clear(); + param.Clear(); + } + + default: + break; + } + } +} + bool SolveSpaceUI::LoadEntitiesFromFile(const std::string &filename, EntityList *le, SMesh *m, SShell *sh) { diff --git a/src/request.cpp b/src/request.cpp index bfef176c..999cc945 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -29,7 +29,7 @@ static const EntReqMapping EntReqMap[] = { { Request::Type::CUBIC_PERIODIC, Entity::Type::CUBIC_PERIODIC, 3, true, false, false }, { 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, 2, false, true, false }, +{ Request::Type::TTF_TEXT, Entity::Type::TTF_TEXT, 4, false, true, false }, }; static void CopyEntityInfo(const EntReqMapping *te, int extraPoints, @@ -78,7 +78,7 @@ Request::Type EntReqTable::GetRequestForEntity(Entity::Type ent) { } void Request::Generate(IdList *entity, - IdList *param) const + IdList *param) { int points = 0; Entity::Type et; @@ -86,6 +86,26 @@ void Request::Generate(IdList *entity, bool hasDistance = false; int i; + // Request-specific generation. + switch(type) { + case Type::TTF_TEXT: { + double actualAspectRatio = SS.fonts.AspectRatio(font, str); + if(EXACT(actualAspectRatio != 0.0)) { + // We could load the font, so use the actual value. + aspectRatio = actualAspectRatio; + } + if(EXACT(aspectRatio == 0.0)) { + // We couldn't load the font and we don't have anything saved, + // so just use 1:1, which is valid for the missing font symbol anyhow. + aspectRatio = 1.0; + } + break; + } + + default: // most requests don't do anything else + break; + } + Entity e = {}; EntReqTable::GetRequestInfo(type, extraPoints, &et, &points, &hasNormal, &hasDistance); @@ -98,6 +118,7 @@ void Request::Generate(IdList *entity, e.construction = construction; e.str = str; e.font = font; + e.aspectRatio = aspectRatio; e.h = h.entity(0); // And generate entities for the points diff --git a/src/sketch.h b/src/sketch.h index 7316d0f8..582ad3a6 100644 --- a/src/sketch.h +++ b/src/sketch.h @@ -316,11 +316,13 @@ public: hStyle style; bool construction; + std::string str; std::string font; + double aspectRatio; static hParam AddParam(ParamList *param, hParam hp); - void Generate(EntityList *entity, ParamList *param) const; + void Generate(EntityList *entity, ParamList *param); std::string DescriptionString() const; int IndexOfPoint(hEntity he) const; @@ -391,6 +393,7 @@ public: std::string str; std::string font; + double aspectRatio; // For entities that are derived by a transformation, the number of // times to apply the transformation. @@ -434,7 +437,9 @@ public: Vector PointGetNum() const; ExprVector PointGetExprs() const; void PointGetExprsInWorkplane(hEntity wrkpl, Expr **u, Expr **v) const; + ExprVector PointGetExprsInWorkplane(hEntity wrkpl) const; void PointForceTo(Vector v); + void PointForceParamTo(Vector v); // These apply only the POINT_N_ROT_TRANS, which has an assoc rotation Quaternion PointGetQuaternion() const; void PointForceQuaternionTo(Quaternion q); @@ -463,6 +468,8 @@ public: Vector EndpointStart() const; Vector EndpointFinish() const; + void TtfTextGetPointsExprs(ExprVector *eap, ExprVector *ebp) const; + void AddEq(IdList *l, Expr *expr, int index) const; void GenerateEquations(IdList *l) const; @@ -859,7 +866,7 @@ inline hRequest hEntity::request() const inline hGroup hEntity::group() const { hGroup r; r.v = (v >> 16) & 0x3fff; return r; } inline hEquation hEntity::equation(int i) const - { hEquation r; r.v = v | 0x40000000; return r; } + { hEquation r; r.v = v | 0x40000000 | (uint32_t)i; return r; } inline hRequest hParam::request() const { hRequest r; r.v = (v >> 16); return r; } diff --git a/src/solvespace.h b/src/solvespace.h index 05d8a657..f2998224 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -808,6 +808,7 @@ public: bool SaveToFile(const std::string &filename); bool LoadAutosaveFor(const std::string &filename); bool LoadFromFile(const std::string &filename); + void UpgradeLegacyData(); bool LoadEntitiesFromFile(const std::string &filename, EntityList *le, SMesh *m, SShell *sh); bool ReloadAllImported(bool canCancel=false); diff --git a/src/ttf.cpp b/src/ttf.cpp index e83ff57f..c04cc5dd 100644 --- a/src/ttf.cpp +++ b/src/ttf.cpp @@ -79,18 +79,28 @@ void TtfFontList::LoadAll() { loaded = true; } -void TtfFontList::PlotString(const std::string &font, const std::string &str, - SBezierList *sbl, Vector origin, Vector u, Vector v) +TtfFont *TtfFontList::LoadFont(const std::string &font) { LoadAll(); - TtfFont *tf = std::find_if(&l.elem[0], &l.elem[l.n], + TtfFont *tf = std::find_if(l.begin(), l.end(), [&](const TtfFont &tf) { return tf.FontFileBaseName() == font; }); - if(!str.empty() && tf != &l.elem[l.n]) { + if(tf != l.end()) { if(tf->fontFace == NULL) { tf->LoadFromFile(fontLibrary, /*nameOnly=*/false); } + return tf; + } else { + return NULL; + } +} + +void TtfFontList::PlotString(const std::string &font, const std::string &str, + SBezierList *sbl, Vector origin, Vector u, Vector v) +{ + TtfFont *tf = LoadFont(font); + if(!str.empty() && tf != NULL) { tf->PlotString(str, sbl, origin, u, v); } else { // No text or no font; so draw a big X for an error marker. @@ -102,6 +112,16 @@ void TtfFontList::PlotString(const std::string &font, const std::string &str, } } +double TtfFontList::AspectRatio(const std::string &font, const std::string &str) +{ + TtfFont *tf = LoadFont(font); + if(tf != NULL) { + return tf->AspectRatio(str); + } + + return 0.0; +} + //----------------------------------------------------------------------------- // Return the basename of our font filename; that's how the requests and // entities that reference us will store it. @@ -328,3 +348,27 @@ void TtfFont::PlotString(const std::string &str, dx += fontFace->glyph->advance.x; } } + +double TtfFont::AspectRatio(const std::string &str) { + ssassert(fontFace != NULL, "Expected font face to be loaded"); + + // We always request a unit size character, so the aspect ratio is the same as advance length. + double dx = 0; + for(char32_t chr : ReadUTF8(str)) { + uint32_t gid = FT_Get_Char_Index(fontFace, chr); + if (gid == 0) { + dbp("freetype: CID-to-GID mapping for CID 0x%04x failed: %s; using CID as GID", + chr, ft_error_string(gid)); + } + + if(int fterr = FT_Load_Glyph(fontFace, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) { + dbp("freetype: cannot load glyph (GID 0x%04x): %s", + gid, ft_error_string(fterr)); + break; + } + + dx += (double)fontFace->glyph->advance.x / capHeight; + } + + return dx; +} diff --git a/src/ttf.h b/src/ttf.h index ec911272..ff016c39 100644 --- a/src/ttf.h +++ b/src/ttf.h @@ -21,6 +21,7 @@ public: void PlotString(const std::string &str, SBezierList *sbl, Vector origin, Vector u, Vector v); + double AspectRatio(const std::string &str); }; class TtfFontList { @@ -33,9 +34,11 @@ public: ~TtfFontList(); void LoadAll(); + TtfFont *LoadFont(const std::string &font); void PlotString(const std::string &font, const std::string &str, SBezierList *sbl, Vector origin, Vector u, Vector v); + double AspectRatio(const std::string &font, const std::string &str); }; #endif diff --git a/test/request/ttf_text/normal.png b/test/request/ttf_text/normal.png index 3397538fac79242aa90b9c892759f58d4d98e4b4..39f789ef420e6155e2a3aae630f2056abafb44fc 100644 GIT binary patch literal 6158 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 literal 6138 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 b`fc^<0`YxWOD7Q$;=#T>hkh>Dg*x{?)DrzL diff --git a/test/request/ttf_text/normal.slvs b/test/request/ttf_text/normal.slvs index 8ea2d83e..229c63bc 100644 --- a/test/request/ttf_text/normal.slvs +++ b/test/request/ttf_text/normal.slvs @@ -130,6 +130,22 @@ Param.h.v.=00040014 Param.val=-5.00000000000000000000 AddParam +Param.h.v.=00040016 +Param.val=23.92131768269594616072 +AddParam + +Param.h.v.=00040017 +Param.val=-5.00000000000000000000 +AddParam + +Param.h.v.=00040019 +Param.val=23.92131768269594616072 +AddParam + +Param.h.v.=0004001a +Param.val=5.00000000000000000000 +AddParam + Request.h.v=00000001 Request.type=100 Request.group.v=00000001 @@ -155,6 +171,7 @@ Request.group.v=00000002 Request.construction=0 Request.str=Text Request.font=Gentium-R.ttf +Request.aspectRatio=2.89213176826959461607 AddRequest Entity.h.v=00010000 @@ -236,6 +253,8 @@ Entity.str=Text Entity.font=Gentium-R.ttf 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 @@ -259,6 +278,24 @@ Entity.actPoint.y=-5.00000000000000000000 Entity.actVisible=1 AddEntity +Entity.h.v=00040003 +Entity.type=2001 +Entity.construction=0 +Entity.workplane.v=80020000 +Entity.actPoint.x=23.92131768269594616072 +Entity.actPoint.y=-5.00000000000000000000 +Entity.actVisible=1 +AddEntity + +Entity.h.v=00040004 +Entity.type=2001 +Entity.construction=0 +Entity.workplane.v=80020000 +Entity.actPoint.x=23.92131768269594616072 +Entity.actPoint.y=5.00000000000000000000 +Entity.actVisible=1 +AddEntity + Entity.h.v=00040020 Entity.type=3001 Entity.construction=0