A big nasty change, originally just to add paste transformed. So it
does that, and adds a scale factor to that transformation (instead of just mirroring, as before), but also: * Replace the "import mirrored" mechanism with a scale factor, which if negative corresponds to a reflection as well. * Fix self-intersection checker to report a meaningful point when edges are collinear. * Don't blow an assertion on some types of invalid file; instead provide a nice error message to the user. * Clear the naked edges before each regen. * Don't create zero-length line segments by snapping a line segment's end to its beginning. [git-p4: depot-paths = "//depot/solvespace/": change = 2086]
This commit is contained in:
parent
9723f4e44f
commit
b974a4adeb
186
clipboard.cpp
186
clipboard.cpp
|
@ -114,14 +114,14 @@ void GraphicsWindow::CopySelection(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::PasteClipboard(Vector trans, double theta, bool mirror) {
|
void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
|
||||||
SS.UndoRemember();
|
|
||||||
ClearSelection();
|
ClearSelection();
|
||||||
|
|
||||||
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
||||||
Entity *wrkpln = SK.GetEntity(wrkpl->normal);
|
Entity *wrkpln = SK.GetEntity(wrkpl->normal);
|
||||||
Vector u = wrkpln->NormalU(),
|
Vector u = wrkpln->NormalU(),
|
||||||
v = wrkpln->NormalV(),
|
v = wrkpln->NormalV(),
|
||||||
|
n = wrkpln->NormalN(),
|
||||||
p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,17 +144,23 @@ void GraphicsWindow::PasteClipboard(Vector trans, double theta, bool mirror) {
|
||||||
NULL, &pts, NULL, &hasDistance);
|
NULL, &pts, NULL, &hasDistance);
|
||||||
for(int i = 0; i < pts; i++) {
|
for(int i = 0; i < pts; i++) {
|
||||||
Vector pt = cr->point[i];
|
Vector pt = cr->point[i];
|
||||||
if(mirror) pt.x *= -1;
|
// We need the reflection to occur within the workplane; it may
|
||||||
pt = Vector::From( cos(theta)*pt.x + sin(theta)*pt.y,
|
// otherwise correspond to just a rotation as projected.
|
||||||
-sin(theta)*pt.x + cos(theta)*pt.y,
|
if(scale < 0) {
|
||||||
0);
|
pt.x *= -1;
|
||||||
|
}
|
||||||
|
// Likewise the scale, which could otherwise take us out of the
|
||||||
|
// workplane.
|
||||||
|
pt = pt.ScaledBy(fabs(scale));
|
||||||
pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0));
|
pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0));
|
||||||
pt = pt.Plus(p);
|
pt = pt.Plus(p);
|
||||||
|
pt = pt.RotatedAbout(n, theta);
|
||||||
pt = pt.Plus(trans);
|
pt = pt.Plus(trans);
|
||||||
SK.GetEntity(hr.entity(i+1))->PointForceTo(pt);
|
SK.GetEntity(hr.entity(i+1))->PointForceTo(pt);
|
||||||
}
|
}
|
||||||
if(hasDistance) {
|
if(hasDistance) {
|
||||||
SK.GetEntity(hr.entity(64))->DistanceForceTo(cr->distance);
|
SK.GetEntity(hr.entity(64))->DistanceForceTo(
|
||||||
|
cr->distance*fabs(scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
cr->newReq = hr;
|
cr->newReq = hr;
|
||||||
|
@ -181,14 +187,31 @@ void GraphicsWindow::MenuClipboard(int id) {
|
||||||
|
|
||||||
switch(id) {
|
switch(id) {
|
||||||
case MNU_PASTE: {
|
case MNU_PASTE: {
|
||||||
|
SS.UndoRemember();
|
||||||
Vector trans = SS.GW.projRight.ScaledBy(80/SS.GW.scale).Plus(
|
Vector trans = SS.GW.projRight.ScaledBy(80/SS.GW.scale).Plus(
|
||||||
SS.GW.projUp .ScaledBy(40/SS.GW.scale));
|
SS.GW.projUp .ScaledBy(40/SS.GW.scale));
|
||||||
SS.GW.PasteClipboard(trans, 0, false);
|
SS.GW.PasteClipboard(trans, 0, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MNU_PASTE_TRANSFORM:
|
case MNU_PASTE_TRANSFORM: {
|
||||||
|
if(SS.clipboard.r.n == 0) {
|
||||||
|
Error("Clipboard is empty; nothing to paste.");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *wrkpl = SK.GetEntity(SS.GW.ActiveWorkplane());
|
||||||
|
Vector p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
||||||
|
SS.TW.shown.paste.times = 1;
|
||||||
|
SS.TW.shown.paste.trans = Vector::From(0, 0, 0);
|
||||||
|
SS.TW.shown.paste.theta = 0;
|
||||||
|
SS.TW.shown.paste.origin = p;
|
||||||
|
SS.TW.shown.paste.scale = 1;
|
||||||
|
SS.TW.GoToScreen(TextWindow::SCREEN_PASTE_TRANSFORMED);
|
||||||
|
SS.GW.ForceTextWindowShown();
|
||||||
|
SS.later.showTW = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case MNU_COPY:
|
case MNU_COPY:
|
||||||
SS.GW.CopySelection();
|
SS.GW.CopySelection();
|
||||||
|
@ -210,3 +233,148 @@ void GraphicsWindow::MenuClipboard(int id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TextWindow::EditControlDoneForPaste(char *s) {
|
||||||
|
switch(edit.meaning) {
|
||||||
|
case EDIT_PASTE_TIMES_REPEATED: {
|
||||||
|
int v = atoi(s);
|
||||||
|
if(v > 0) {
|
||||||
|
shown.paste.times = v;
|
||||||
|
} else {
|
||||||
|
Error("Number of copies to paste must be at least one.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EDIT_PASTE_ANGLE:
|
||||||
|
shown.paste.theta = WRAP_SYMMETRIC(atof(s)*PI/180, 2*PI);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EDIT_PASTE_SCALE: {
|
||||||
|
double v = atof(s);
|
||||||
|
if(fabs(v) > 1e-6) {
|
||||||
|
shown.paste.scale = v;
|
||||||
|
} else {
|
||||||
|
Error("Scale cannot be zero.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextWindow::ScreenChangePasteTransformed(int link, DWORD v) {
|
||||||
|
char str[300];
|
||||||
|
switch(link) {
|
||||||
|
case 't':
|
||||||
|
sprintf(str, "%d", SS.TW.shown.paste.times);
|
||||||
|
ShowTextEditControl(10, 12, str);
|
||||||
|
SS.TW.edit.meaning = EDIT_PASTE_TIMES_REPEATED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
sprintf(str, "%.3f", SS.TW.shown.paste.theta*180/PI);
|
||||||
|
ShowTextEditControl(12, 12, str);
|
||||||
|
SS.TW.edit.meaning = EDIT_PASTE_ANGLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
sprintf(str, "%.3f", SS.TW.shown.paste.scale);
|
||||||
|
ShowTextEditControl(18, 12, str);
|
||||||
|
SS.TW.edit.meaning = EDIT_PASTE_SCALE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextWindow::ScreenPasteTransformed(int link, DWORD v) {
|
||||||
|
SS.GW.GroupSelection();
|
||||||
|
switch(link) {
|
||||||
|
case 'o':
|
||||||
|
if(SS.GW.gs.points == 1 && SS.GW.gs.n == 1) {
|
||||||
|
Entity *e = SK.GetEntity(SS.GW.gs.point[0]);
|
||||||
|
SS.TW.shown.paste.origin = e->PointGetNum();
|
||||||
|
} else {
|
||||||
|
Error("Select one point to define origin of rotation.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
if(SS.GW.gs.points == 2 && SS.GW.gs.n == 2) {
|
||||||
|
Entity *pa = SK.GetEntity(SS.GW.gs.point[0]),
|
||||||
|
*pb = SK.GetEntity(SS.GW.gs.point[1]);
|
||||||
|
SS.TW.shown.paste.trans =
|
||||||
|
(pb->PointGetNum()).Minus(pa->PointGetNum());
|
||||||
|
} else {
|
||||||
|
Error("Select two points to define translation vector.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g': {
|
||||||
|
if(fabs(SS.TW.shown.paste.theta) < LENGTH_EPS &&
|
||||||
|
SS.TW.shown.paste.trans.Magnitude() < LENGTH_EPS &&
|
||||||
|
SS.TW.shown.paste.times != 1)
|
||||||
|
{
|
||||||
|
Message("Transformation is identity. So all copies will be "
|
||||||
|
"exactly on top of each other.");
|
||||||
|
}
|
||||||
|
if(SS.TW.shown.paste.times*SS.clipboard.r.n > 100) {
|
||||||
|
Error("Too many items to paste; split this into smaller "
|
||||||
|
"pastes.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!SS.GW.LockedInWorkplane()) {
|
||||||
|
Error("No workplane active.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Entity *wrkpl = SK.GetEntity(SS.GW.ActiveWorkplane());
|
||||||
|
Entity *wrkpln = SK.GetEntity(wrkpl->normal);
|
||||||
|
Vector wn = wrkpln->NormalN();
|
||||||
|
SS.UndoRemember();
|
||||||
|
for(int i = 0; i < SS.TW.shown.paste.times; i++) {
|
||||||
|
Vector trans = SS.TW.shown.paste.trans.ScaledBy(i+1),
|
||||||
|
origin = SS.TW.shown.paste.origin;
|
||||||
|
double theta = SS.TW.shown.paste.theta*(i+1);
|
||||||
|
// desired transformation is Q*(p - o) + o + t =
|
||||||
|
// Q*p - Q*o + o + t = Q*p + (t + o - Q*o)
|
||||||
|
Vector t = trans.Plus(
|
||||||
|
origin).Minus(
|
||||||
|
origin.RotatedAbout(wn, theta));
|
||||||
|
|
||||||
|
SS.GW.PasteClipboard(t, theta, SS.TW.shown.paste.scale);
|
||||||
|
}
|
||||||
|
SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
|
||||||
|
SS.later.showTW = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SS.GW.ClearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextWindow::ShowPasteTransformed(void) {
|
||||||
|
Printf(true, "%FtPASTE TRANSFORMED%E");
|
||||||
|
Printf(true, "%Ba %FtREPEAT%E %d time%s %Fl%Lt%f[change]%E",
|
||||||
|
shown.paste.times, (shown.paste.times == 1) ? "" : "s",
|
||||||
|
&ScreenChangePasteTransformed);
|
||||||
|
Printf(false, "%Bd %FtROTATE%E %@° %Fl%Lr%f[change]%E",
|
||||||
|
shown.paste.theta*180/PI,
|
||||||
|
&ScreenChangePasteTransformed);
|
||||||
|
Printf(false, "%Ba %FtABOUT PT%E (%s, %s, %s) %Fl%Lo%f[use selected]%E",
|
||||||
|
SS.MmToString(shown.paste.origin.x),
|
||||||
|
SS.MmToString(shown.paste.origin.y),
|
||||||
|
SS.MmToString(shown.paste.origin.z),
|
||||||
|
&ScreenPasteTransformed);
|
||||||
|
Printf(false, "%Bd %FtTRANSLATE%E (%s, %s, %s) %Fl%Lt%f[use selected]%E",
|
||||||
|
SS.MmToString(shown.paste.trans.x),
|
||||||
|
SS.MmToString(shown.paste.trans.y),
|
||||||
|
SS.MmToString(shown.paste.trans.z),
|
||||||
|
&ScreenPasteTransformed);
|
||||||
|
Printf(false, "%Ba %FtSCALE%E %@ %Fl%Ls%f[change]%E",
|
||||||
|
shown.paste.scale,
|
||||||
|
&ScreenChangePasteTransformed);
|
||||||
|
|
||||||
|
Printf(true, " %Fl%Lg%fpaste transformed now%E", &ScreenPasteTransformed);
|
||||||
|
|
||||||
|
Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
2
dsc.h
2
dsc.h
|
@ -39,7 +39,7 @@ public:
|
||||||
Quaternion ToThe(double p);
|
Quaternion ToThe(double p);
|
||||||
Quaternion Inverse(void);
|
Quaternion Inverse(void);
|
||||||
Quaternion Times(Quaternion b);
|
Quaternion Times(Quaternion b);
|
||||||
Quaternion MirrorZ(void);
|
Quaternion Mirror(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Vector {
|
class Vector {
|
||||||
|
|
16
file.cpp
16
file.cpp
|
@ -93,7 +93,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
|
||||||
{ 'g', "Group.visible", 'b', &(SS.sv.g.visible) },
|
{ 'g', "Group.visible", 'b', &(SS.sv.g.visible) },
|
||||||
{ 'g', "Group.suppress", 'b', &(SS.sv.g.suppress) },
|
{ 'g', "Group.suppress", 'b', &(SS.sv.g.suppress) },
|
||||||
{ 'g', "Group.relaxConstraints", 'b', &(SS.sv.g.relaxConstraints) },
|
{ 'g', "Group.relaxConstraints", 'b', &(SS.sv.g.relaxConstraints) },
|
||||||
{ 'g', "Group.mirror", 'b', &(SS.sv.g.mirror) },
|
{ 'g', "Group.scale", 'f', &(SS.sv.g.scale) },
|
||||||
{ 'g', "Group.remap", 'M', &(SS.sv.g.remap) },
|
{ 'g', "Group.remap", 'M', &(SS.sv.g.remap) },
|
||||||
{ 'g', "Group.impFile", 'P', &(SS.sv.g.impFile) },
|
{ 'g', "Group.impFile", 'P', &(SS.sv.g.impFile) },
|
||||||
{ 'g', "Group.impFileRel", 'P', &(SS.sv.g.impFileRel) },
|
{ 'g', "Group.impFileRel", 'P', &(SS.sv.g.impFileRel) },
|
||||||
|
@ -378,11 +378,14 @@ void SolveSpace::LoadUsingTable(char *key, char *val) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(SAVED[i].type == 0) oops();
|
if(SAVED[i].type == 0) {
|
||||||
|
fileLoadError = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SolveSpace::LoadFromFile(char *filename) {
|
bool SolveSpace::LoadFromFile(char *filename) {
|
||||||
allConsistent = false;
|
allConsistent = false;
|
||||||
|
fileLoadError = false;
|
||||||
|
|
||||||
fh = fopen(filename, "rb");
|
fh = fopen(filename, "rb");
|
||||||
if(!fh) {
|
if(!fh) {
|
||||||
|
@ -400,6 +403,7 @@ bool SolveSpace::LoadFromFile(char *filename) {
|
||||||
SK.param.Clear();
|
SK.param.Clear();
|
||||||
SK.style.Clear();
|
SK.style.Clear();
|
||||||
memset(&sv, 0, sizeof(sv));
|
memset(&sv, 0, sizeof(sv));
|
||||||
|
sv.g.scale = 1; // default is 1, not 0; so legacy files need this
|
||||||
|
|
||||||
char line[1024];
|
char line[1024];
|
||||||
while(fgets(line, sizeof(line), fh)) {
|
while(fgets(line, sizeof(line), fh)) {
|
||||||
|
@ -420,6 +424,7 @@ bool SolveSpace::LoadFromFile(char *filename) {
|
||||||
} else if(strcmp(line, "AddGroup")==0) {
|
} else if(strcmp(line, "AddGroup")==0) {
|
||||||
SK.group.Add(&(sv.g));
|
SK.group.Add(&(sv.g));
|
||||||
ZERO(&(sv.g));
|
ZERO(&(sv.g));
|
||||||
|
sv.g.scale = 1; // default is 1, not 0; so legacy files need this
|
||||||
} else if(strcmp(line, "AddParam")==0) {
|
} else if(strcmp(line, "AddParam")==0) {
|
||||||
// params are regenerated, but we want to preload the values
|
// params are regenerated, but we want to preload the values
|
||||||
// for initial guesses
|
// for initial guesses
|
||||||
|
@ -450,12 +455,17 @@ bool SolveSpace::LoadFromFile(char *filename) {
|
||||||
{
|
{
|
||||||
// ignore the mesh or shell, since we regenerate that
|
// ignore the mesh or shell, since we regenerate that
|
||||||
} else {
|
} else {
|
||||||
oops();
|
fileLoadError = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fh);
|
fclose(fh);
|
||||||
|
|
||||||
|
if(fileLoadError) {
|
||||||
|
Error("Unrecognized data in file. This file may be corrupt, or "
|
||||||
|
"from a new version of the program.");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ void SolveSpace::GenerateAll(void) {
|
||||||
// All clean; so just regenerate the entities, and don't solve anything.
|
// All clean; so just regenerate the entities, and don't solve anything.
|
||||||
GenerateAll(-1, -1);
|
GenerateAll(-1, -1);
|
||||||
} else {
|
} else {
|
||||||
|
SS.nakedEdges.Clear();
|
||||||
GenerateAll(firstDirty, lastVisible);
|
GenerateAll(firstDirty, lastVisible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||||
{ 1, "Paste &Transformed...\tCtrl+T", MNU_PASTE_TRANSFORM,'T'|C, mClip },
|
{ 1, "Paste &Transformed...\tCtrl+T", MNU_PASTE_TRANSFORM,'T'|C, mClip },
|
||||||
{ 1, "&Delete\tDel", MNU_DELETE, 127, mClip },
|
{ 1, "&Delete\tDel", MNU_DELETE, 127, mClip },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
{ 1, "Select Edge Cha&in\tCtrl+I", MNU_SELECT_CHAIN, 'I'|C, mEdit },
|
{ 1, "Select &Edge Chain\tCtrl+E", MNU_SELECT_CHAIN, 'E'|C, mEdit },
|
||||||
{ 1, "Invert &Selection\tCtrl+A", MNU_INVERT_SEL, 'A'|C, mEdit },
|
{ 1, "Invert &Selection\tCtrl+A", MNU_INVERT_SEL, 'A'|C, mEdit },
|
||||||
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
|
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
|
||||||
|
|
||||||
|
|
17
group.cpp
17
group.cpp
|
@ -27,6 +27,7 @@ void Group::MenuGroup(int id) {
|
||||||
ZERO(&g);
|
ZERO(&g);
|
||||||
g.visible = true;
|
g.visible = true;
|
||||||
g.color = RGB(100, 100, 100);
|
g.color = RGB(100, 100, 100);
|
||||||
|
g.scale = 1;
|
||||||
|
|
||||||
if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
|
if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
|
||||||
strcpy(g.impFile, RecentFile[id-RECENT_IMPORT]);
|
strcpy(g.impFile, RecentFile[id-RECENT_IMPORT]);
|
||||||
|
@ -681,8 +682,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
|
||||||
en.param[5] = qvy;
|
en.param[5] = qvy;
|
||||||
en.param[6] = qvz;
|
en.param[6] = qvz;
|
||||||
}
|
}
|
||||||
en.numPoint = ep->actPoint;
|
en.numPoint = (ep->actPoint).ScaledBy(scale);
|
||||||
if(mirror) en.numPoint.z *= -1;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Entity::NORMAL_N_COPY:
|
case Entity::NORMAL_N_COPY:
|
||||||
|
@ -704,7 +704,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
|
||||||
en.param[3] = qvz;
|
en.param[3] = qvz;
|
||||||
}
|
}
|
||||||
en.numNormal = ep->actNormal;
|
en.numNormal = ep->actNormal;
|
||||||
if(mirror) en.numNormal = en.numNormal.MirrorZ();
|
if(scale < 0) en.numNormal = en.numNormal.Mirror();
|
||||||
|
|
||||||
en.point[0] = Remap(ep->point[0], remap);
|
en.point[0] = Remap(ep->point[0], remap);
|
||||||
break;
|
break;
|
||||||
|
@ -712,7 +712,7 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
|
||||||
case Entity::DISTANCE_N_COPY:
|
case Entity::DISTANCE_N_COPY:
|
||||||
case Entity::DISTANCE:
|
case Entity::DISTANCE:
|
||||||
en.type = Entity::DISTANCE_N_COPY;
|
en.type = Entity::DISTANCE_N_COPY;
|
||||||
en.numDistance = ep->actDistance;
|
en.numDistance = ep->actDistance*fabs(scale);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Entity::FACE_NORMAL_PT:
|
case Entity::FACE_NORMAL_PT:
|
||||||
|
@ -739,13 +739,8 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
|
||||||
en.param[5] = qvy;
|
en.param[5] = qvy;
|
||||||
en.param[6] = qvz;
|
en.param[6] = qvz;
|
||||||
}
|
}
|
||||||
en.numPoint = ep->actPoint;
|
en.numPoint = (ep->actPoint).ScaledBy(scale);
|
||||||
en.numNormal = ep->actNormal;
|
en.numNormal = (ep->actNormal).ScaledBy(scale);
|
||||||
if(mirror) {
|
|
||||||
if(en.type != Entity::FACE_N_ROT_TRANS) oops();
|
|
||||||
en.numPoint.z *= -1;
|
|
||||||
en.numNormal.vz *= -1;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
|
|
@ -119,7 +119,7 @@ void Group::GenerateForStepAndRepeat(T *steps, T *outs) {
|
||||||
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
|
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
|
||||||
trans = trans.ScaledBy(ap);
|
trans = trans.ScaledBy(ap);
|
||||||
transd.MakeFromTransformationOf(steps,
|
transd.MakeFromTransformationOf(steps,
|
||||||
trans, Quaternion::IDENTITY, false);
|
trans, Quaternion::IDENTITY, 1.0);
|
||||||
} else {
|
} else {
|
||||||
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
|
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
|
||||||
double theta = ap * SK.GetParam(h.param(3))->val;
|
double theta = ap * SK.GetParam(h.param(3))->val;
|
||||||
|
@ -128,7 +128,7 @@ void Group::GenerateForStepAndRepeat(T *steps, T *outs) {
|
||||||
Quaternion q = Quaternion::From(c, s*axis.x, s*axis.y, s*axis.z);
|
Quaternion q = Quaternion::From(c, s*axis.x, s*axis.y, s*axis.z);
|
||||||
// Rotation is centered at t; so A(x - t) + t = Ax + (t - At)
|
// Rotation is centered at t; so A(x - t) + t = Ax + (t - At)
|
||||||
transd.MakeFromTransformationOf(steps,
|
transd.MakeFromTransformationOf(steps,
|
||||||
trans.Minus(q.Rotate(trans)), q, false);
|
trans.Minus(q.Rotate(trans)), q, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to rewrite any plane face entities to the transformed ones.
|
// We need to rewrite any plane face entities to the transformed ones.
|
||||||
|
@ -292,10 +292,10 @@ void Group::GenerateShellAndMesh(void) {
|
||||||
SK.GetParam(h.param(5))->val,
|
SK.GetParam(h.param(5))->val,
|
||||||
SK.GetParam(h.param(6))->val };
|
SK.GetParam(h.param(6))->val };
|
||||||
|
|
||||||
thisMesh.MakeFromTransformationOf(&impMesh, offset, q, mirror);
|
thisMesh.MakeFromTransformationOf(&impMesh, offset, q, scale);
|
||||||
thisMesh.RemapFaces(this, 0);
|
thisMesh.RemapFaces(this, 0);
|
||||||
|
|
||||||
thisShell.MakeFromTransformationOf(&impShell, offset, q, mirror);
|
thisShell.MakeFromTransformationOf(&impShell, offset, q, scale);
|
||||||
thisShell.RemapFaces(this, 0);
|
thisShell.RemapFaces(this, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
mesh.cpp
12
mesh.cpp
|
@ -293,16 +293,16 @@ void SMesh::MakeFromAssemblyOf(SMesh *a, SMesh *b) {
|
||||||
MakeFromCopyOf(b);
|
MakeFromCopyOf(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMesh::MakeFromTransformationOf(SMesh *a, Vector trans, Quaternion q,
|
void SMesh::MakeFromTransformationOf(SMesh *a,
|
||||||
bool mirror)
|
Vector trans, Quaternion q, double scale)
|
||||||
{
|
{
|
||||||
STriangle *tr;
|
STriangle *tr;
|
||||||
for(tr = a->l.First(); tr; tr = a->l.NextAfter(tr)) {
|
for(tr = a->l.First(); tr; tr = a->l.NextAfter(tr)) {
|
||||||
STriangle tt = *tr;
|
STriangle tt = *tr;
|
||||||
if(mirror) {
|
tt.a = (tt.a).ScaledBy(scale);
|
||||||
tt.a.z *= -1;
|
tt.b = (tt.b).ScaledBy(scale);
|
||||||
tt.b.z *= -1;
|
tt.c = (tt.c).ScaledBy(scale);
|
||||||
tt.c.z *= -1;
|
if(scale < 0) {
|
||||||
// The mirroring would otherwise turn a closed mesh inside out.
|
// The mirroring would otherwise turn a closed mesh inside out.
|
||||||
SWAP(Vector, tt.a, tt.b);
|
SWAP(Vector, tt.a, tt.b);
|
||||||
}
|
}
|
||||||
|
|
18
mouse.cpp
18
mouse.cpp
|
@ -980,10 +980,28 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case DRAGGING_NEW_LINE_POINT: {
|
case DRAGGING_NEW_LINE_POINT: {
|
||||||
|
if(hover.entity.v) {
|
||||||
|
Entity *e = SK.GetEntity(hover.entity);
|
||||||
|
if(e->IsPoint()) {
|
||||||
|
hRequest hrl = pending.point.request();
|
||||||
|
Entity *sp = SK.GetEntity(hrl.entity(1));
|
||||||
|
if(( e->PointGetNum()).Equals(
|
||||||
|
(sp->PointGetNum())))
|
||||||
|
{
|
||||||
|
// If we constrained by the hovered point, then we
|
||||||
|
// would create a zero-length line segment. That's
|
||||||
|
// not good, so just stop drawing.
|
||||||
|
ClearPending();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(ConstrainPointByHovered(pending.point)) {
|
if(ConstrainPointByHovered(pending.point)) {
|
||||||
ClearPending();
|
ClearPending();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new line segment, so that we continue drawing.
|
// Create a new line segment, so that we continue drawing.
|
||||||
hRequest hr = AddRequest(Request::LINE_SEGMENT);
|
hRequest hr = AddRequest(Request::LINE_SEGMENT);
|
||||||
SK.GetEntity(hr.entity(1))->PointForceTo(v);
|
SK.GetEntity(hr.entity(1))->PointForceTo(v);
|
||||||
|
|
25
polygon.cpp
25
polygon.cpp
|
@ -70,8 +70,13 @@ bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) {
|
||||||
Vector dthis = b.Minus(a);
|
Vector dthis = b.Minus(a);
|
||||||
double tthis_eps = LENGTH_EPS/dthis.Magnitude();
|
double tthis_eps = LENGTH_EPS/dthis.Magnitude();
|
||||||
|
|
||||||
if(ea.Equals(a) && eb.Equals(b)) return true;
|
if((ea.Equals(a) && eb.Equals(b)) ||
|
||||||
if(eb.Equals(a) && ea.Equals(b)) return true;
|
(eb.Equals(a) && ea.Equals(b)))
|
||||||
|
{
|
||||||
|
if(ppi) *ppi = a;
|
||||||
|
if(spl) spl->Add(a);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
dist_a = a.DistanceToLine(ea, d),
|
dist_a = a.DistanceToLine(ea, d),
|
||||||
dist_b = b.DistanceToLine(ea, d);
|
dist_b = b.DistanceToLine(ea, d);
|
||||||
|
@ -87,18 +92,26 @@ bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) {
|
||||||
}
|
}
|
||||||
// The edges are coincident. Make sure that neither endpoint lies
|
// The edges are coincident. Make sure that neither endpoint lies
|
||||||
// on the other
|
// on the other
|
||||||
|
bool inters = false;
|
||||||
double t;
|
double t;
|
||||||
t = a.Minus(ea).DivPivoting(d);
|
t = a.Minus(ea).DivPivoting(d);
|
||||||
if(t > t_eps && t < (1 - t_eps)) return true;
|
if(t > t_eps && t < (1 - t_eps)) inters = true;
|
||||||
t = b.Minus(ea).DivPivoting(d);
|
t = b.Minus(ea).DivPivoting(d);
|
||||||
if(t > t_eps && t < (1 - t_eps)) return true;
|
if(t > t_eps && t < (1 - t_eps)) inters = true;
|
||||||
t = ea.Minus(a).DivPivoting(dthis);
|
t = ea.Minus(a).DivPivoting(dthis);
|
||||||
if(t > tthis_eps && t < (1 - tthis_eps)) return true;
|
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
|
||||||
t = eb.Minus(a).DivPivoting(dthis);
|
t = eb.Minus(a).DivPivoting(dthis);
|
||||||
if(t > tthis_eps && t < (1 - tthis_eps)) return true;
|
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
|
||||||
|
|
||||||
|
if(inters) {
|
||||||
|
if(ppi) *ppi = a;
|
||||||
|
if(spl) spl->Add(a);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
// So coincident but disjoint, okay.
|
// So coincident but disjoint, okay.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Lines are not parallel, so look for an intersection.
|
// Lines are not parallel, so look for an intersection.
|
||||||
pi = Vector::AtIntersectionOfLines(ea, eb, a, b,
|
pi = Vector::AtIntersectionOfLines(ea, eb, a, b,
|
||||||
|
|
|
@ -231,8 +231,8 @@ public:
|
||||||
void MakeFromDifferenceOf(SMesh *a, SMesh *b);
|
void MakeFromDifferenceOf(SMesh *a, SMesh *b);
|
||||||
|
|
||||||
void MakeFromCopyOf(SMesh *a);
|
void MakeFromCopyOf(SMesh *a);
|
||||||
void MakeFromTransformationOf(SMesh *a, Vector trans, Quaternion q,
|
void MakeFromTransformationOf(SMesh *a,
|
||||||
bool mirror);
|
Vector trans, Quaternion q, double scale);
|
||||||
void MakeFromAssemblyOf(SMesh *a, SMesh *b);
|
void MakeFromAssemblyOf(SMesh *a, SMesh *b);
|
||||||
|
|
||||||
void MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d);
|
void MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d);
|
||||||
|
|
2
sketch.h
2
sketch.h
|
@ -102,7 +102,7 @@ public:
|
||||||
bool visible;
|
bool visible;
|
||||||
bool suppress;
|
bool suppress;
|
||||||
bool relaxConstraints;
|
bool relaxConstraints;
|
||||||
bool mirror;
|
double scale;
|
||||||
|
|
||||||
bool clean;
|
bool clean;
|
||||||
bool vvMeshClean;
|
bool vvMeshClean;
|
||||||
|
|
|
@ -625,6 +625,7 @@ public:
|
||||||
static void RemoveFromRecentList(char *file);
|
static void RemoveFromRecentList(char *file);
|
||||||
static void AddToRecentList(char *file);
|
static void AddToRecentList(char *file);
|
||||||
char saveFile[MAX_PATH];
|
char saveFile[MAX_PATH];
|
||||||
|
bool fileLoadError;
|
||||||
bool unsaved;
|
bool unsaved;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char type;
|
char type;
|
||||||
|
|
|
@ -687,7 +687,7 @@ void SShell::MakeFromAssemblyOf(SShell *a, SShell *b) {
|
||||||
for(i = 0; i < 2; i++) {
|
for(i = 0; i < 2; i++) {
|
||||||
ab = (i == 0) ? a : b;
|
ab = (i == 0) ? a : b;
|
||||||
for(c = ab->curve.First(); c; c = ab->curve.NextAfter(c)) {
|
for(c = ab->curve.First(); c; c = ab->curve.NextAfter(c)) {
|
||||||
cn = SCurve::FromTransformationOf(c, t, q, false);
|
cn = SCurve::FromTransformationOf(c, t, q, 1.0);
|
||||||
cn.source = (i == 0) ? SCurve::FROM_A : SCurve::FROM_B;
|
cn.source = (i == 0) ? SCurve::FROM_A : SCurve::FROM_B;
|
||||||
// surfA and surfB are wrong now, and we can't fix them until
|
// surfA and surfB are wrong now, and we can't fix them until
|
||||||
// we've assigned IDs to the surfaces. So we'll get that later.
|
// we've assigned IDs to the surfaces. So we'll get that later.
|
||||||
|
@ -700,7 +700,7 @@ void SShell::MakeFromAssemblyOf(SShell *a, SShell *b) {
|
||||||
for(i = 0; i < 2; i++) {
|
for(i = 0; i < 2; i++) {
|
||||||
ab = (i == 0) ? a : b;
|
ab = (i == 0) ? a : b;
|
||||||
for(s = ab->surface.First(); s; s = ab->surface.NextAfter(s)) {
|
for(s = ab->surface.First(); s; s = ab->surface.NextAfter(s)) {
|
||||||
sn = SSurface::FromTransformationOf(s, t, q, false, true);
|
sn = SSurface::FromTransformationOf(s, t, q, 1.0, true);
|
||||||
// All the trim curve IDs get rewritten; we know the new handles
|
// All the trim curve IDs get rewritten; we know the new handles
|
||||||
// to the curves since we recorded them in the previous step.
|
// to the curves since we recorded them in the previous step.
|
||||||
STrimBy *stb;
|
STrimBy *stb;
|
||||||
|
|
|
@ -95,11 +95,11 @@ void SBezier::GetBoundingProjd(Vector u, Vector orig,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SBezier SBezier::TransformedBy(Vector t, Quaternion q, bool mirror) {
|
SBezier SBezier::TransformedBy(Vector t, Quaternion q, double scale) {
|
||||||
SBezier ret = *this;
|
SBezier ret = *this;
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i <= deg; i++) {
|
for(i = 0; i <= deg; i++) {
|
||||||
if(mirror) ret.ctrl[i].z *= -1;
|
ret.ctrl[i] = (ret.ctrl[i]).ScaledBy(scale);
|
||||||
ret.ctrl[i] = (q.Rotate(ret.ctrl[i])).Plus(t);
|
ret.ctrl[i] = (q.Rotate(ret.ctrl[i])).Plus(t);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -189,7 +189,7 @@ SBezier SBezier::InPerspective(Vector u, Vector v, Vector n,
|
||||||
Quaternion q = Quaternion::From(u, v);
|
Quaternion q = Quaternion::From(u, v);
|
||||||
q = q.Inverse();
|
q = q.Inverse();
|
||||||
// we want Q*(p - o) = Q*p - Q*o
|
// we want Q*(p - o) = Q*p - Q*o
|
||||||
SBezier ret = this->TransformedBy(q.Rotate(origin).ScaledBy(-1), q, false);
|
SBezier ret = this->TransformedBy(q.Rotate(origin).ScaledBy(-1), q, 1.0);
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i <= deg; i++) {
|
for(i = 0; i <= deg; i++) {
|
||||||
Vector4 ct = Vector4::From(ret.weight[i], ret.ctrl[i]);
|
Vector4 ct = Vector4::From(ret.weight[i], ret.ctrl[i]);
|
||||||
|
@ -740,22 +740,22 @@ void SBezierLoopSetSet::Clear(void) {
|
||||||
l.Clear();
|
l.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
SCurve SCurve::FromTransformationOf(SCurve *a, Vector t, Quaternion q,
|
SCurve SCurve::FromTransformationOf(SCurve *a,
|
||||||
bool mirror)
|
Vector t, Quaternion q, double scale)
|
||||||
{
|
{
|
||||||
SCurve ret;
|
SCurve ret;
|
||||||
ZERO(&ret);
|
ZERO(&ret);
|
||||||
|
|
||||||
ret.h = a->h;
|
ret.h = a->h;
|
||||||
ret.isExact = a->isExact;
|
ret.isExact = a->isExact;
|
||||||
ret.exact = (a->exact).TransformedBy(t, q, mirror);
|
ret.exact = (a->exact).TransformedBy(t, q, scale);
|
||||||
ret.surfA = a->surfA;
|
ret.surfA = a->surfA;
|
||||||
ret.surfB = a->surfB;
|
ret.surfB = a->surfB;
|
||||||
|
|
||||||
SCurvePt *p;
|
SCurvePt *p;
|
||||||
for(p = a->pts.First(); p; p = a->pts.NextAfter(p)) {
|
for(p = a->pts.First(); p; p = a->pts.NextAfter(p)) {
|
||||||
SCurvePt pp = *p;
|
SCurvePt pp = *p;
|
||||||
if(mirror) pp.p.z *= -1;
|
pp.p = (pp.p).ScaledBy(scale);
|
||||||
pp.p = (q.Rotate(pp.p)).Plus(t);
|
pp.p = (q.Rotate(pp.p)).Plus(t);
|
||||||
ret.pts.Add(&pp);
|
ret.pts.Add(&pp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,8 +132,8 @@ SSurface SSurface::FromPlane(Vector pt, Vector u, Vector v) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q,
|
SSurface SSurface::FromTransformationOf(SSurface *a,
|
||||||
bool mirror,
|
Vector t, Quaternion q, double scale,
|
||||||
bool includingTrims)
|
bool includingTrims)
|
||||||
{
|
{
|
||||||
SSurface ret;
|
SSurface ret;
|
||||||
|
@ -149,7 +149,7 @@ SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q,
|
||||||
for(i = 0; i <= 3; i++) {
|
for(i = 0; i <= 3; i++) {
|
||||||
for(j = 0; j <= 3; j++) {
|
for(j = 0; j <= 3; j++) {
|
||||||
ret.ctrl[i][j] = a->ctrl[i][j];
|
ret.ctrl[i][j] = a->ctrl[i][j];
|
||||||
if(mirror) ret.ctrl[i][j].z *= -1;
|
ret.ctrl[i][j] = (ret.ctrl[i][j]).ScaledBy(scale);
|
||||||
ret.ctrl[i][j] = (q.Rotate(ret.ctrl[i][j])).Plus(t);
|
ret.ctrl[i][j] = (q.Rotate(ret.ctrl[i][j])).Plus(t);
|
||||||
|
|
||||||
ret.weight[i][j] = a->weight[i][j];
|
ret.weight[i][j] = a->weight[i][j];
|
||||||
|
@ -160,17 +160,15 @@ SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q,
|
||||||
STrimBy *stb;
|
STrimBy *stb;
|
||||||
for(stb = a->trim.First(); stb; stb = a->trim.NextAfter(stb)) {
|
for(stb = a->trim.First(); stb; stb = a->trim.NextAfter(stb)) {
|
||||||
STrimBy n = *stb;
|
STrimBy n = *stb;
|
||||||
if(mirror) {
|
n.start = n.start.ScaledBy(scale);
|
||||||
n.start.z *= -1;
|
n.finish = n.finish.ScaledBy(scale);
|
||||||
n.finish.z *= -1;
|
|
||||||
}
|
|
||||||
n.start = (q.Rotate(n.start)) .Plus(t);
|
n.start = (q.Rotate(n.start)) .Plus(t);
|
||||||
n.finish = (q.Rotate(n.finish)).Plus(t);
|
n.finish = (q.Rotate(n.finish)).Plus(t);
|
||||||
ret.trim.Add(&n);
|
ret.trim.Add(&n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mirror) {
|
if(scale < 0) {
|
||||||
// If we mirror every surface of a shell, then it will end up inside
|
// If we mirror every surface of a shell, then it will end up inside
|
||||||
// out. So fix that here.
|
// out. So fix that here.
|
||||||
ret.Reverse();
|
ret.Reverse();
|
||||||
|
@ -543,7 +541,7 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1,
|
||||||
SCurve sc;
|
SCurve sc;
|
||||||
ZERO(&sc);
|
ZERO(&sc);
|
||||||
sc.isExact = true;
|
sc.isExact = true;
|
||||||
sc.exact = sb->TransformedBy(t0, Quaternion::IDENTITY, false);
|
sc.exact = sb->TransformedBy(t0, Quaternion::IDENTITY, 1.0);
|
||||||
(sc.exact).MakePwlInto(&(sc.pts));
|
(sc.exact).MakePwlInto(&(sc.pts));
|
||||||
sc.surfA = hs0;
|
sc.surfA = hs0;
|
||||||
sc.surfB = hsext;
|
sc.surfB = hsext;
|
||||||
|
@ -551,7 +549,7 @@ void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1,
|
||||||
|
|
||||||
ZERO(&sc);
|
ZERO(&sc);
|
||||||
sc.isExact = true;
|
sc.isExact = true;
|
||||||
sc.exact = sb->TransformedBy(t1, Quaternion::IDENTITY, false);
|
sc.exact = sb->TransformedBy(t1, Quaternion::IDENTITY, 1.0);
|
||||||
(sc.exact).MakePwlInto(&(sc.pts));
|
(sc.exact).MakePwlInto(&(sc.pts));
|
||||||
sc.surfA = hs1;
|
sc.surfA = hs1;
|
||||||
sc.surfB = hsext;
|
sc.surfB = hsext;
|
||||||
|
@ -689,7 +687,7 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
|
||||||
if(revs.d[j].v) {
|
if(revs.d[j].v) {
|
||||||
ZERO(&sc);
|
ZERO(&sc);
|
||||||
sc.isExact = true;
|
sc.isExact = true;
|
||||||
sc.exact = sb->TransformedBy(ts, qs, false);
|
sc.exact = sb->TransformedBy(ts, qs, 1.0);
|
||||||
(sc.exact).MakePwlInto(&(sc.pts));
|
(sc.exact).MakePwlInto(&(sc.pts));
|
||||||
sc.surfA = revs.d[j];
|
sc.surfA = revs.d[j];
|
||||||
sc.surfB = revs.d[WRAP(j-1, 4)];
|
sc.surfB = revs.d[WRAP(j-1, 4)];
|
||||||
|
@ -816,25 +814,25 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
|
||||||
|
|
||||||
void SShell::MakeFromCopyOf(SShell *a) {
|
void SShell::MakeFromCopyOf(SShell *a) {
|
||||||
MakeFromTransformationOf(a,
|
MakeFromTransformationOf(a,
|
||||||
Vector::From(0, 0, 0), Quaternion::IDENTITY, false);
|
Vector::From(0, 0, 0), Quaternion::IDENTITY, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SShell::MakeFromTransformationOf(SShell *a,
|
void SShell::MakeFromTransformationOf(SShell *a,
|
||||||
Vector t, Quaternion q, bool mirror)
|
Vector t, Quaternion q, double scale)
|
||||||
{
|
{
|
||||||
booleanFailed = false;
|
booleanFailed = false;
|
||||||
|
|
||||||
SSurface *s;
|
SSurface *s;
|
||||||
for(s = a->surface.First(); s; s = a->surface.NextAfter(s)) {
|
for(s = a->surface.First(); s; s = a->surface.NextAfter(s)) {
|
||||||
SSurface n;
|
SSurface n;
|
||||||
n = SSurface::FromTransformationOf(s, t, q, mirror, true);
|
n = SSurface::FromTransformationOf(s, t, q, scale, true);
|
||||||
surface.Add(&n); // keeping the old ID
|
surface.Add(&n); // keeping the old ID
|
||||||
}
|
}
|
||||||
|
|
||||||
SCurve *c;
|
SCurve *c;
|
||||||
for(c = a->curve.First(); c; c = a->curve.NextAfter(c)) {
|
for(c = a->curve.First(); c; c = a->curve.NextAfter(c)) {
|
||||||
SCurve n;
|
SCurve n;
|
||||||
n = SCurve::FromTransformationOf(c, t, q, mirror);
|
n = SCurve::FromTransformationOf(c, t, q, scale);
|
||||||
curve.Add(&n); // keeping the old ID
|
curve.Add(&n); // keeping the old ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ public:
|
||||||
bool IsCircle(Vector axis, Vector *center, double *r);
|
bool IsCircle(Vector axis, Vector *center, double *r);
|
||||||
bool IsRational(void);
|
bool IsRational(void);
|
||||||
|
|
||||||
SBezier TransformedBy(Vector t, Quaternion q, bool mirror);
|
SBezier TransformedBy(Vector t, Quaternion q, double scale);
|
||||||
SBezier InPerspective(Vector u, Vector v, Vector n,
|
SBezier InPerspective(Vector u, Vector v, Vector n,
|
||||||
Vector origin, double cameraTan);
|
Vector origin, double cameraTan);
|
||||||
void ScaleSelfBy(double s);
|
void ScaleSelfBy(double s);
|
||||||
|
@ -190,7 +190,7 @@ public:
|
||||||
hSSurface surfB;
|
hSSurface surfB;
|
||||||
|
|
||||||
static SCurve FromTransformationOf(SCurve *a, Vector t, Quaternion q,
|
static SCurve FromTransformationOf(SCurve *a, Vector t, Quaternion q,
|
||||||
bool mirror);
|
double scale);
|
||||||
SCurve MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
|
SCurve MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
|
||||||
SSurface *srfA, SSurface *srfB);
|
SSurface *srfA, SSurface *srfB);
|
||||||
void RemoveShortSegments(SSurface *srfA, SSurface *srfB);
|
void RemoveShortSegments(SSurface *srfA, SSurface *srfB);
|
||||||
|
@ -260,7 +260,7 @@ public:
|
||||||
double thetas, double thetaf);
|
double thetas, double thetaf);
|
||||||
static SSurface FromPlane(Vector pt, Vector u, Vector v);
|
static SSurface FromPlane(Vector pt, Vector u, Vector v);
|
||||||
static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q,
|
static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q,
|
||||||
bool mirror,
|
double scale,
|
||||||
bool includingTrims);
|
bool includingTrims);
|
||||||
void ScaleSelfBy(double s);
|
void ScaleSelfBy(double s);
|
||||||
|
|
||||||
|
@ -384,8 +384,8 @@ public:
|
||||||
Vector edge_n_in, Vector edge_n_out, Vector surf_n);
|
Vector edge_n_in, Vector edge_n_out, Vector surf_n);
|
||||||
|
|
||||||
void MakeFromCopyOf(SShell *a);
|
void MakeFromCopyOf(SShell *a);
|
||||||
void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q,
|
void MakeFromTransformationOf(SShell *a,
|
||||||
bool mirror);
|
Vector trans, Quaternion q, double scale);
|
||||||
void MakeFromAssemblyOf(SShell *a, SShell *b);
|
void MakeFromAssemblyOf(SShell *a, SShell *b);
|
||||||
void MergeCoincidentSurfaces(void);
|
void MergeCoincidentSurfaces(void);
|
||||||
|
|
||||||
|
|
|
@ -235,10 +235,6 @@ void TextWindow::ScreenChangeGroupOption(int link, DWORD v) {
|
||||||
g->suppress = !(g->suppress);
|
g->suppress = !(g->suppress);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm':
|
|
||||||
g->mirror = !(g->mirror);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'r':
|
case 'r':
|
||||||
g->relaxConstraints = !(g->relaxConstraints);
|
g->relaxConstraints = !(g->relaxConstraints);
|
||||||
break;
|
break;
|
||||||
|
@ -315,6 +311,15 @@ void TextWindow::ScreenChangeGroupName(int link, DWORD v) {
|
||||||
SS.TW.edit.meaning = EDIT_GROUP_NAME;
|
SS.TW.edit.meaning = EDIT_GROUP_NAME;
|
||||||
SS.TW.edit.group.v = v;
|
SS.TW.edit.group.v = v;
|
||||||
}
|
}
|
||||||
|
void TextWindow::ScreenChangeGroupScale(int link, DWORD v) {
|
||||||
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
||||||
|
|
||||||
|
char str[1024];
|
||||||
|
sprintf(str, "%.3f", g->scale);
|
||||||
|
ShowTextEditControl(17, 9, str);
|
||||||
|
SS.TW.edit.meaning = EDIT_GROUP_SCALE;
|
||||||
|
SS.TW.edit.group.v = v;
|
||||||
|
}
|
||||||
void TextWindow::ScreenDeleteGroup(int link, DWORD v) {
|
void TextWindow::ScreenDeleteGroup(int link, DWORD v) {
|
||||||
SS.UndoRemember();
|
SS.UndoRemember();
|
||||||
|
|
||||||
|
@ -449,11 +454,9 @@ void TextWindow::ShowGroupInfo(void) {
|
||||||
&TextWindow::ScreenChangeGroupOption,
|
&TextWindow::ScreenChangeGroupOption,
|
||||||
(!sup ? "" : "no"), (!sup ? "no" : ""));
|
(!sup ? "" : "no"), (!sup ? "no" : ""));
|
||||||
|
|
||||||
Printf(false, "%FtMIRROR%E %Fh%f%Lm%s%E%Fs%s%E / %Fh%f%Lm%s%E%Fs%s%E",
|
Printf(true, "%FtSCALE BY%E %# %Fl%Ll%f%D[change]%E",
|
||||||
&TextWindow::ScreenChangeGroupOption,
|
g->scale,
|
||||||
(g->mirror ? "" : "yes"), (g->mirror ? "yes" : ""),
|
&TextWindow::ScreenChangeGroupScale, g->h.v);
|
||||||
&TextWindow::ScreenChangeGroupOption,
|
|
||||||
(!g->mirror ? "" : "no"), (!g->mirror ? "no" : ""));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool relax = g->relaxConstraints;
|
bool relax = g->relaxConstraints;
|
||||||
|
@ -734,6 +737,23 @@ void TextWindow::EditControlDone(char *s) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case EDIT_GROUP_SCALE: {
|
||||||
|
Expr *e = Expr::From(s);
|
||||||
|
if(e) {
|
||||||
|
double ev = e->Eval();
|
||||||
|
if(fabs(ev) < 1e-6) {
|
||||||
|
Error("Scale cannot be zero.");
|
||||||
|
} else {
|
||||||
|
Group *g = SK.GetGroup(edit.group);
|
||||||
|
g->scale = ev;
|
||||||
|
SS.MarkGroupDirty(g->h);
|
||||||
|
SS.later.generateAll = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Error("Not a valid number or expression: '%s'", s);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case EDIT_HELIX_TURNS:
|
case EDIT_HELIX_TURNS:
|
||||||
case EDIT_HELIX_PITCH:
|
case EDIT_HELIX_PITCH:
|
||||||
case EDIT_HELIX_DRADIUS: {
|
case EDIT_HELIX_DRADIUS: {
|
||||||
|
@ -783,9 +803,11 @@ void TextWindow::EditControlDone(char *s) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
bool st = EditControlDoneForStyles(s),
|
int cnt = 0;
|
||||||
cf = EditControlDoneForConfiguration(s);
|
if(EditControlDoneForStyles(s)) cnt++;
|
||||||
if(st && cf) {
|
if(EditControlDoneForConfiguration(s)) cnt++;
|
||||||
|
if(EditControlDoneForPaste(s)) cnt++;
|
||||||
|
if(cnt > 1) {
|
||||||
// The identifiers were somehow assigned not uniquely?
|
// The identifiers were somehow assigned not uniquely?
|
||||||
oops();
|
oops();
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,7 +216,9 @@ void TextWindow::Show(void) {
|
||||||
Printf(false, "%s", SS.GW.pending.description);
|
Printf(false, "%s", SS.GW.pending.description);
|
||||||
Printf(true, "%Fl%f%Ll(cancel operation)%E",
|
Printf(true, "%Fl%f%Ll(cancel operation)%E",
|
||||||
&TextWindow::ScreenUnselectAll);
|
&TextWindow::ScreenUnselectAll);
|
||||||
} else if(gs.n > 0 || gs.constraints > 0) {
|
} else if((gs.n > 0 || gs.constraints > 0) &&
|
||||||
|
shown.screen != SCREEN_PASTE_TRANSFORMED)
|
||||||
|
{
|
||||||
if(edit.meaning != EDIT_TTF_TEXT) HideTextEditControl();
|
if(edit.meaning != EDIT_TTF_TEXT) HideTextEditControl();
|
||||||
ShowHeader(false);
|
ShowHeader(false);
|
||||||
DescribeSelection();
|
DescribeSelection();
|
||||||
|
@ -235,6 +237,7 @@ void TextWindow::Show(void) {
|
||||||
case SCREEN_MESH_VOLUME: ShowMeshVolume(); break;
|
case SCREEN_MESH_VOLUME: ShowMeshVolume(); break;
|
||||||
case SCREEN_LIST_OF_STYLES: ShowListOfStyles(); break;
|
case SCREEN_LIST_OF_STYLES: ShowListOfStyles(); break;
|
||||||
case SCREEN_STYLE_INFO: ShowStyleInfo(); break;
|
case SCREEN_STYLE_INFO: ShowStyleInfo(); break;
|
||||||
|
case SCREEN_PASTE_TRANSFORMED: ShowPasteTransformed(); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Printf(false, "");
|
Printf(false, "");
|
||||||
|
|
22
ui.h
22
ui.h
|
@ -54,6 +54,7 @@ public:
|
||||||
static const int SCREEN_MESH_VOLUME = 5;
|
static const int SCREEN_MESH_VOLUME = 5;
|
||||||
static const int SCREEN_LIST_OF_STYLES = 6;
|
static const int SCREEN_LIST_OF_STYLES = 6;
|
||||||
static const int SCREEN_STYLE_INFO = 7;
|
static const int SCREEN_STYLE_INFO = 7;
|
||||||
|
static const int SCREEN_PASTE_TRANSFORMED = 8;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int screen;
|
int screen;
|
||||||
|
|
||||||
|
@ -65,6 +66,14 @@ public:
|
||||||
double dimFinish;
|
double dimFinish;
|
||||||
int dimSteps;
|
int dimSteps;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int times;
|
||||||
|
Vector trans;
|
||||||
|
double theta;
|
||||||
|
Vector origin;
|
||||||
|
double scale;
|
||||||
|
} paste;
|
||||||
|
|
||||||
double volume;
|
double volume;
|
||||||
} ShownState;
|
} ShownState;
|
||||||
ShownState shown;
|
ShownState shown;
|
||||||
|
@ -73,6 +82,7 @@ public:
|
||||||
// For multiple groups
|
// For multiple groups
|
||||||
static const int EDIT_TIMES_REPEATED = 1;
|
static const int EDIT_TIMES_REPEATED = 1;
|
||||||
static const int EDIT_GROUP_NAME = 2;
|
static const int EDIT_GROUP_NAME = 2;
|
||||||
|
static const int EDIT_GROUP_SCALE = 3;
|
||||||
// For the configuraiton screen
|
// For the configuraiton screen
|
||||||
static const int EDIT_LIGHT_DIRECTION = 10;
|
static const int EDIT_LIGHT_DIRECTION = 10;
|
||||||
static const int EDIT_LIGHT_INTENSITY = 11;
|
static const int EDIT_LIGHT_INTENSITY = 11;
|
||||||
|
@ -102,6 +112,10 @@ public:
|
||||||
static const int EDIT_STYLE_NAME = 55;
|
static const int EDIT_STYLE_NAME = 55;
|
||||||
static const int EDIT_BACKGROUND_COLOR = 56;
|
static const int EDIT_BACKGROUND_COLOR = 56;
|
||||||
static const int EDIT_BACKGROUND_IMG_SCALE = 57;
|
static const int EDIT_BACKGROUND_IMG_SCALE = 57;
|
||||||
|
// For paste transforming
|
||||||
|
static const int EDIT_PASTE_TIMES_REPEATED = 60;
|
||||||
|
static const int EDIT_PASTE_ANGLE = 61;
|
||||||
|
static const int EDIT_PASTE_SCALE = 62;
|
||||||
struct {
|
struct {
|
||||||
int meaning;
|
int meaning;
|
||||||
int i;
|
int i;
|
||||||
|
@ -125,6 +139,7 @@ public:
|
||||||
void ShowStyleInfo(void);
|
void ShowStyleInfo(void);
|
||||||
void ShowStepDimension(void);
|
void ShowStepDimension(void);
|
||||||
void ShowMeshVolume(void);
|
void ShowMeshVolume(void);
|
||||||
|
void ShowPasteTransformed(void);
|
||||||
// Special screen, based on selection
|
// Special screen, based on selection
|
||||||
void DescribeSelection(void);
|
void DescribeSelection(void);
|
||||||
|
|
||||||
|
@ -178,11 +193,14 @@ public:
|
||||||
static void ScreenStepDimFinish(int link, DWORD v);
|
static void ScreenStepDimFinish(int link, DWORD v);
|
||||||
static void ScreenStepDimGo(int link, DWORD v);
|
static void ScreenStepDimGo(int link, DWORD v);
|
||||||
|
|
||||||
|
static void ScreenPasteTransformed(int link, DWORD v);
|
||||||
|
|
||||||
static void ScreenHome(int link, DWORD v);
|
static void ScreenHome(int link, DWORD v);
|
||||||
|
|
||||||
// These ones do stuff with the edit control
|
// These ones do stuff with the edit control
|
||||||
static void ScreenChangeExprA(int link, DWORD v);
|
static void ScreenChangeExprA(int link, DWORD v);
|
||||||
static void ScreenChangeGroupName(int link, DWORD v);
|
static void ScreenChangeGroupName(int link, DWORD v);
|
||||||
|
static void ScreenChangeGroupScale(int link, DWORD v);
|
||||||
static void ScreenChangeLightDirection(int link, DWORD v);
|
static void ScreenChangeLightDirection(int link, DWORD v);
|
||||||
static void ScreenChangeLightIntensity(int link, DWORD v);
|
static void ScreenChangeLightIntensity(int link, DWORD v);
|
||||||
static void ScreenChangeColor(int link, DWORD v);
|
static void ScreenChangeColor(int link, DWORD v);
|
||||||
|
@ -198,9 +216,11 @@ public:
|
||||||
static void ScreenChangeStyleColor(int link, DWORD v);
|
static void ScreenChangeStyleColor(int link, DWORD v);
|
||||||
static void ScreenChangeBackgroundColor(int link, DWORD v);
|
static void ScreenChangeBackgroundColor(int link, DWORD v);
|
||||||
static void ScreenChangeBackgroundImageScale(int link, DWORD v);
|
static void ScreenChangeBackgroundImageScale(int link, DWORD v);
|
||||||
|
static void ScreenChangePasteTransformed(int link, DWORD v);
|
||||||
|
|
||||||
bool EditControlDoneForStyles(char *s);
|
bool EditControlDoneForStyles(char *s);
|
||||||
bool EditControlDoneForConfiguration(char *s);
|
bool EditControlDoneForConfiguration(char *s);
|
||||||
|
bool EditControlDoneForPaste(char *s);
|
||||||
void EditControlDone(char *s);
|
void EditControlDone(char *s);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -317,7 +337,7 @@ public:
|
||||||
static void MenuRequest(int id);
|
static void MenuRequest(int id);
|
||||||
void DeleteSelection(void);
|
void DeleteSelection(void);
|
||||||
void CopySelection(void);
|
void CopySelection(void);
|
||||||
void PasteClipboard(Vector trans, double theta, bool mirror);
|
void PasteClipboard(Vector trans, double theta, double scale);
|
||||||
static void MenuClipboard(int id);
|
static void MenuClipboard(int id);
|
||||||
|
|
||||||
// The width and height (in pixels) of the window.
|
// The width and height (in pixels) of the window.
|
||||||
|
|
6
util.cpp
6
util.cpp
|
@ -344,11 +344,11 @@ Quaternion Quaternion::Times(Quaternion b) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
Quaternion Quaternion::MirrorZ(void) {
|
Quaternion Quaternion::Mirror(void) {
|
||||||
Vector u = RotationU(),
|
Vector u = RotationU(),
|
||||||
v = RotationV();
|
v = RotationV();
|
||||||
u.z *= -1;
|
u = u.ScaledBy(-1);
|
||||||
v.z *= -1;
|
v = v.ScaledBy(-1);
|
||||||
return Quaternion::From(u, v);
|
return Quaternion::From(u, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
paste transformed
|
|
||||||
de-select after left-clicking nothing, keep sel on drag?
|
de-select after left-clicking nothing, keep sel on drag?
|
||||||
bbox selection is select-only, not toggle?
|
bbox selection is select-only, not toggle?
|
||||||
show and modify view parameters (translate, rotate, scale)
|
show and modify view parameters (translate, rotate, scale)
|
||||||
|
context menu to hide / suppress groups by entity?
|
||||||
|
|
||||||
-----
|
-----
|
||||||
associative entities from solid model, as a special group
|
associative entities from solid model, as a special group
|
||||||
|
|
Loading…
Reference in New Issue
Block a user