solvespace/src/request.cpp

248 lines
8.8 KiB
C++

//-----------------------------------------------------------------------------
// Implementation of our Request class; a request is a user-created thing
// that will generate an entity (line, curve) when the sketch is generated,
// in the same way that other entities are generated automatically, like
// by an extrude or a step and repeat.
//
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
const hRequest Request::HREQUEST_REFERENCE_XY = { 1 };
const hRequest Request::HREQUEST_REFERENCE_YZ = { 2 };
const hRequest Request::HREQUEST_REFERENCE_ZX = { 3 };
struct EntReqMapping {
Request::Type reqType;
Entity::Type entType;
int points;
bool useExtraPoints;
bool hasNormal;
bool hasDistance;
};
static const EntReqMapping EntReqMap[] = {
// request type entity type pts xtra? norml dist
{ Request::Type::WORKPLANE, Entity::Type::WORKPLANE, 1, false, true, false },
{ Request::Type::DATUM_POINT, (Entity::Type)0, 1, false, false, false },
{ Request::Type::LINE_SEGMENT, Entity::Type::LINE_SEGMENT, 2, false, false, false },
{ Request::Type::CUBIC, Entity::Type::CUBIC, 4, true, false, false },
{ 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, 4, false, true, false },
{ Request::Type::IMAGE, Entity::Type::IMAGE, 4, false, true, false },
};
static void CopyEntityInfo(const EntReqMapping *te, int extraPoints,
Entity::Type *ent, Request::Type *req,
int *pts, bool *hasNormal, bool *hasDistance)
{
int points = te->points;
if(te->useExtraPoints) points += extraPoints;
if(ent) *ent = te->entType;
if(req) *req = te->reqType;
if(pts) *pts = points;
if(hasNormal) *hasNormal = te->hasNormal;
if(hasDistance) *hasDistance = te->hasDistance;
}
bool EntReqTable::GetRequestInfo(Request::Type req, int extraPoints,
Entity::Type *ent, int *pts, bool *hasNormal, bool *hasDistance)
{
for(const EntReqMapping &te : EntReqMap) {
if(req == te.reqType) {
CopyEntityInfo(&te, extraPoints, ent, NULL, pts, hasNormal, hasDistance);
return true;
}
}
return false;
}
bool EntReqTable::GetEntityInfo(Entity::Type ent, int extraPoints,
Request::Type *req, int *pts, bool *hasNormal, bool *hasDistance)
{
for(const EntReqMapping &te : EntReqMap) {
if(ent == te.entType) {
CopyEntityInfo(&te, extraPoints, NULL, req, pts, hasNormal, hasDistance);
return true;
}
}
return false;
}
Request::Type EntReqTable::GetRequestForEntity(Entity::Type ent) {
Request::Type req;
ssassert(GetEntityInfo(ent, 0, &req, NULL, NULL, NULL),
"No entity for request");
return req;
}
void Request::Generate(IdList<Entity,hEntity> *entity,
IdList<Param,hParam> *param)
{
int points = 0;
Entity::Type et = (Entity::Type)0;
bool hasNormal = false;
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;
}
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;
}
Entity e = {};
EntReqTable::GetRequestInfo(type, extraPoints, &et, &points, &hasNormal, &hasDistance);
// Generate the entity that's specific to this request.
e.type = et;
e.extraPoints = extraPoints;
e.group = group;
e.style = style;
e.workplane = workplane;
e.construction = construction;
e.str = str;
e.font = font;
e.file = file;
e.aspectRatio = aspectRatio;
e.h = h.entity(0);
// And generate entities for the points
for(i = 0; i < points; i++) {
Entity p = {};
p.workplane = workplane;
// points start from entity 1, except for datum point case
p.h = h.entity(i+((et != (Entity::Type)0) ? 1 : 0));
p.group = group;
p.style = style;
p.construction = e.construction;
if(workplane == Entity::FREE_IN_3D) {
p.type = Entity::Type::POINT_IN_3D;
// params for x y z
p.param[0] = AddParam(param, h.param(16 + 3*i + 0));
p.param[1] = AddParam(param, h.param(16 + 3*i + 1));
p.param[2] = AddParam(param, h.param(16 + 3*i + 2));
} else {
p.type = Entity::Type::POINT_IN_2D;
// params for u v
p.param[0] = AddParam(param, h.param(16 + 3*i + 0));
p.param[1] = AddParam(param, h.param(16 + 3*i + 1));
}
entity->Add(&p);
e.point[i] = p.h;
}
if(hasNormal) {
Entity n = {};
n.workplane = workplane;
n.h = h.entity(32);
n.group = group;
n.style = style;
n.construction = e.construction;
if(workplane == Entity::FREE_IN_3D) {
n.type = Entity::Type::NORMAL_IN_3D;
n.param[0] = AddParam(param, h.param(32+0));
n.param[1] = AddParam(param, h.param(32+1));
n.param[2] = AddParam(param, h.param(32+2));
n.param[3] = AddParam(param, h.param(32+3));
} else {
n.type = Entity::Type::NORMAL_IN_2D;
// and this is just a copy of the workplane quaternion,
// so no params required
}
ssassert(points >= 1, "Positioning a normal requires a point");
// The point determines where the normal gets displayed on-screen;
// it's entirely cosmetic.
n.point[0] = e.point[0];
entity->Add(&n);
e.normal = n.h;
}
if(hasDistance) {
Entity d = {};
d.workplane = workplane;
d.h = h.entity(64);
d.group = group;
d.style = style;
d.type = Entity::Type::DISTANCE;
d.param[0] = AddParam(param, h.param(64));
entity->Add(&d);
e.distance = d.h;
}
if(et != (Entity::Type)0) entity->Add(&e);
}
std::string Request::DescriptionString() const {
const char *s = "";
if(h == Request::HREQUEST_REFERENCE_XY) {
s = "#XY";
} else if(h == Request::HREQUEST_REFERENCE_YZ) {
s = "#YZ";
} else if(h == Request::HREQUEST_REFERENCE_ZX) {
s = "#ZX";
} else {
switch(type) {
case Type::WORKPLANE: s = "workplane"; break;
case Type::DATUM_POINT: s = "datum-point"; break;
case Type::LINE_SEGMENT: s = "line-segment"; break;
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::TTF_TEXT: s = "ttf-text"; break;
case Type::IMAGE: s = "image"; break;
}
}
ssassert(s != NULL, "Unexpected request type");
return ssprintf("r%03x-%s", h.v, s);
}
int Request::IndexOfPoint(hEntity he) const {
if(type == Type::DATUM_POINT) {
return (he == h.entity(0)) ? 0 : -1;
}
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
if(he == h.entity(i + 1)) {
return i;
}
}
return -1;
}
hParam Request::AddParam(IdList<Param,hParam> *param, hParam hp) {
Param pa = {};
pa.h = hp;
param->Add(&pa);
return hp;
}