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:
Jonathan Westhues 2009-12-15 04:26:22 -08:00
parent 9723f4e44f
commit b974a4adeb
22 changed files with 342 additions and 93 deletions

View File

@ -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
View File

@ -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 {

View File

@ -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;
} }

View File

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

View File

@ -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 },

View File

@ -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: {

View File

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

View File

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

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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
} }
} }

View File

@ -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);

View File

@ -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();
} }

View File

@ -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
View File

@ -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.

View File

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

View File

@ -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