2013-07-28 22:08:34 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// The clipboard that gets manipulated when the user selects Edit -> Cut,
|
|
|
|
// Copy, Paste, etc.; may contain entities only, not constraints.
|
|
|
|
//
|
|
|
|
// Copyright 2008-2013 Jonathan Westhues.
|
|
|
|
//-----------------------------------------------------------------------------
|
2009-12-04 08:08:41 +00:00
|
|
|
#include "solvespace.h"
|
|
|
|
|
2015-03-23 17:49:04 +00:00
|
|
|
void SolveSpaceUI::Clipboard::Clear(void) {
|
2009-12-04 08:08:41 +00:00
|
|
|
c.Clear();
|
|
|
|
r.Clear();
|
|
|
|
}
|
|
|
|
|
2015-12-30 12:36:31 +00:00
|
|
|
bool SolveSpaceUI::Clipboard::ContainsEntity(hEntity he) {
|
|
|
|
if(he.v == Entity::NO_ENTITY.v)
|
|
|
|
return true;
|
|
|
|
|
2009-12-04 08:08:41 +00:00
|
|
|
ClipboardRequest *cr;
|
|
|
|
for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
|
2015-12-30 12:36:31 +00:00
|
|
|
if(cr->oldEnt.v == he.v)
|
|
|
|
return true;
|
|
|
|
|
2009-12-04 08:08:41 +00:00
|
|
|
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
|
2015-12-30 12:36:31 +00:00
|
|
|
if(cr->oldPointEnt[i].v == he.v)
|
|
|
|
return true;
|
2009-12-04 08:08:41 +00:00
|
|
|
}
|
|
|
|
}
|
2015-12-30 12:36:31 +00:00
|
|
|
return false;
|
2009-12-04 08:08:41 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 12:36:31 +00:00
|
|
|
hEntity SolveSpaceUI::Clipboard::NewEntityFor(hEntity he) {
|
|
|
|
if(he.v == Entity::NO_ENTITY.v)
|
|
|
|
return Entity::NO_ENTITY;
|
|
|
|
|
|
|
|
ClipboardRequest *cr;
|
|
|
|
for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
|
|
|
|
if(cr->oldEnt.v == he.v)
|
|
|
|
return cr->newReq.entity(0);
|
|
|
|
|
|
|
|
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
|
|
|
|
if(cr->oldPointEnt[i].v == he.v)
|
|
|
|
return cr->newReq.entity(1+i);
|
|
|
|
}
|
2009-12-04 08:08:41 +00:00
|
|
|
}
|
2015-12-30 12:36:31 +00:00
|
|
|
oops();
|
2009-12-04 08:08:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphicsWindow::DeleteSelection(void) {
|
|
|
|
SK.request.ClearTags();
|
|
|
|
SK.constraint.ClearTags();
|
|
|
|
List<Selection> *ls = &(selection);
|
|
|
|
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
|
|
|
hRequest r = { 0 };
|
|
|
|
if(s->entity.v && s->entity.isFromRequest()) {
|
|
|
|
r = s->entity.request();
|
|
|
|
}
|
|
|
|
if(r.v && !r.IsFromReferences()) {
|
|
|
|
SK.request.Tag(r, 1);
|
|
|
|
}
|
|
|
|
if(s->constraint.v) {
|
|
|
|
SK.constraint.Tag(s->constraint, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SK.constraint.RemoveTagged();
|
|
|
|
// Note that this regenerates and clears the selection, to avoid
|
|
|
|
// lingering references to the just-deleted items.
|
|
|
|
DeleteTaggedRequests();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GraphicsWindow::CopySelection(void) {
|
|
|
|
SS.clipboard.Clear();
|
|
|
|
|
|
|
|
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
|
|
|
Entity *wrkpln = SK.GetEntity(wrkpl->normal);
|
|
|
|
Vector u = wrkpln->NormalU(),
|
|
|
|
v = wrkpln->NormalV(),
|
|
|
|
n = wrkpln->NormalN(),
|
|
|
|
p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
|
|
|
|
|
|
|
List<Selection> *ls = &(selection);
|
|
|
|
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
|
|
|
if(!s->entity.v) continue;
|
|
|
|
// Work only on entities that have requests that will generate them.
|
|
|
|
Entity *e = SK.GetEntity(s->entity);
|
|
|
|
bool hasDistance;
|
|
|
|
int req, pts;
|
|
|
|
if(!EntReqTable::GetEntityInfo(e->type, e->extraPoints,
|
|
|
|
&req, &pts, NULL, &hasDistance))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(req == Request::WORKPLANE) continue;
|
|
|
|
|
2015-03-27 15:31:23 +00:00
|
|
|
ClipboardRequest cr = {};
|
2009-12-04 08:08:41 +00:00
|
|
|
cr.type = req;
|
|
|
|
cr.extraPoints = e->extraPoints;
|
|
|
|
cr.style = e->style;
|
2015-11-06 08:40:12 +00:00
|
|
|
cr.str = e->str;
|
|
|
|
cr.font = e->font;
|
2009-12-04 08:08:41 +00:00
|
|
|
cr.construction = e->construction;
|
2013-10-19 05:36:45 +00:00
|
|
|
{for(int i = 0; i < pts; i++) {
|
2009-12-04 08:08:41 +00:00
|
|
|
Vector pt = SK.GetEntity(e->point[i])->PointGetNum();
|
|
|
|
pt = pt.Minus(p);
|
|
|
|
pt = pt.DotInToCsys(u, v, n);
|
|
|
|
cr.point[i] = pt;
|
2013-10-19 05:36:45 +00:00
|
|
|
}}
|
2009-12-04 08:08:41 +00:00
|
|
|
if(hasDistance) {
|
|
|
|
cr.distance = SK.GetEntity(e->distance)->DistanceGetNum();
|
|
|
|
}
|
|
|
|
|
|
|
|
cr.oldEnt = e->h;
|
|
|
|
for(int i = 0; i < pts; i++) {
|
|
|
|
cr.oldPointEnt[i] = e->point[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
SS.clipboard.r.Add(&cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
Constraint *c;
|
|
|
|
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
2015-12-30 12:36:31 +00:00
|
|
|
if(!SS.clipboard.ContainsEntity(c->ptA) ||
|
|
|
|
!SS.clipboard.ContainsEntity(c->ptB) ||
|
|
|
|
!SS.clipboard.ContainsEntity(c->entityA) ||
|
|
|
|
!SS.clipboard.ContainsEntity(c->entityB) ||
|
|
|
|
!SS.clipboard.ContainsEntity(c->entityC) ||
|
|
|
|
!SS.clipboard.ContainsEntity(c->entityD)) {
|
2009-12-04 08:08:41 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
SS.clipboard.c.Add(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-15 12:26:22 +00:00
|
|
|
void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
|
2009-12-04 08:08:41 +00:00
|
|
|
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
|
|
|
Entity *wrkpln = SK.GetEntity(wrkpl->normal);
|
|
|
|
Vector u = wrkpln->NormalU(),
|
|
|
|
v = wrkpln->NormalV(),
|
2009-12-15 12:26:22 +00:00
|
|
|
n = wrkpln->NormalN(),
|
2009-12-04 08:08:41 +00:00
|
|
|
p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
|
|
|
|
|
|
|
ClipboardRequest *cr;
|
|
|
|
for(cr = SS.clipboard.r.First(); cr; cr = SS.clipboard.r.NextAfter(cr)) {
|
|
|
|
hRequest hr = AddRequest(cr->type, false);
|
|
|
|
Request *r = SK.GetRequest(hr);
|
|
|
|
r->extraPoints = cr->extraPoints;
|
|
|
|
r->style = cr->style;
|
2015-11-06 08:40:12 +00:00
|
|
|
r->str = cr->str;
|
|
|
|
r->font = cr->font;
|
2009-12-04 08:08:41 +00:00
|
|
|
r->construction = cr->construction;
|
|
|
|
// Need to regen to get the right number of points, if extraPoints
|
|
|
|
// changed.
|
|
|
|
SS.GenerateAll(-1, -1);
|
|
|
|
SS.MarkGroupDirty(r->group);
|
|
|
|
bool hasDistance;
|
2009-12-21 16:44:00 +00:00
|
|
|
int i, pts;
|
2009-12-04 08:08:41 +00:00
|
|
|
EntReqTable::GetRequestInfo(r->type, r->extraPoints,
|
|
|
|
NULL, &pts, NULL, &hasDistance);
|
2009-12-21 16:44:00 +00:00
|
|
|
for(i = 0; i < pts; i++) {
|
2009-12-04 08:08:41 +00:00
|
|
|
Vector pt = cr->point[i];
|
2009-12-15 12:26:22 +00:00
|
|
|
// We need the reflection to occur within the workplane; it may
|
|
|
|
// otherwise correspond to just a rotation as projected.
|
|
|
|
if(scale < 0) {
|
|
|
|
pt.x *= -1;
|
|
|
|
}
|
|
|
|
// Likewise the scale, which could otherwise take us out of the
|
|
|
|
// workplane.
|
|
|
|
pt = pt.ScaledBy(fabs(scale));
|
2009-12-04 08:08:41 +00:00
|
|
|
pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0));
|
|
|
|
pt = pt.Plus(p);
|
2009-12-15 12:26:22 +00:00
|
|
|
pt = pt.RotatedAbout(n, theta);
|
2009-12-04 08:08:41 +00:00
|
|
|
pt = pt.Plus(trans);
|
|
|
|
SK.GetEntity(hr.entity(i+1))->PointForceTo(pt);
|
|
|
|
}
|
|
|
|
if(hasDistance) {
|
2009-12-15 12:26:22 +00:00
|
|
|
SK.GetEntity(hr.entity(64))->DistanceForceTo(
|
|
|
|
cr->distance*fabs(scale));
|
2009-12-04 08:08:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cr->newReq = hr;
|
2010-01-03 10:26:15 +00:00
|
|
|
MakeSelected(hr.entity(0));
|
2009-12-21 16:44:00 +00:00
|
|
|
for(i = 0; i < pts; i++) {
|
2010-01-03 10:26:15 +00:00
|
|
|
MakeSelected(hr.entity(i+1));
|
2009-12-21 16:44:00 +00:00
|
|
|
}
|
2009-12-04 08:08:41 +00:00
|
|
|
}
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2015-12-30 12:36:31 +00:00
|
|
|
Constraint *cc;
|
|
|
|
for(cc = SS.clipboard.c.First(); cc; cc = SS.clipboard.c.NextAfter(cc)) {
|
|
|
|
Constraint c = {};
|
|
|
|
c.group = SS.GW.activeGroup;
|
|
|
|
c.workplane = SS.GW.ActiveWorkplane();
|
|
|
|
c.type = cc->type;
|
|
|
|
c.valA = cc->valA;
|
|
|
|
c.ptA = SS.clipboard.NewEntityFor(cc->ptA);
|
|
|
|
c.ptB = SS.clipboard.NewEntityFor(cc->ptB);
|
|
|
|
c.entityA = SS.clipboard.NewEntityFor(cc->entityA);
|
|
|
|
c.entityB = SS.clipboard.NewEntityFor(cc->entityB);
|
|
|
|
c.entityC = SS.clipboard.NewEntityFor(cc->entityC);
|
|
|
|
c.entityD = SS.clipboard.NewEntityFor(cc->entityD);
|
|
|
|
c.other = cc->other;
|
|
|
|
c.other2 = cc->other2;
|
|
|
|
c.reference = cc->reference;
|
|
|
|
c.disp = cc->disp;
|
|
|
|
Constraint::AddConstraint(&c);
|
2009-12-04 08:08:41 +00:00
|
|
|
}
|
|
|
|
|
2015-03-18 17:02:11 +00:00
|
|
|
SS.ScheduleGenerateAll();
|
2009-12-04 08:08:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphicsWindow::MenuClipboard(int id) {
|
|
|
|
if(id != MNU_DELETE && !SS.GW.LockedInWorkplane()) {
|
2010-01-16 18:15:40 +00:00
|
|
|
Error("Cut, paste, and copy work only in a workplane.\n\n"
|
2009-12-04 08:08:41 +00:00
|
|
|
"Select one with Sketch -> In Workplane.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(id) {
|
2015-03-29 00:30:52 +00:00
|
|
|
case MNU_PASTE: {
|
2009-12-15 12:26:22 +00:00
|
|
|
SS.UndoRemember();
|
2009-12-04 08:08:41 +00:00
|
|
|
Vector trans = SS.GW.projRight.ScaledBy(80/SS.GW.scale).Plus(
|
|
|
|
SS.GW.projUp .ScaledBy(40/SS.GW.scale));
|
2010-01-04 03:17:09 +00:00
|
|
|
SS.GW.ClearSelection();
|
2009-12-15 12:26:22 +00:00
|
|
|
SS.GW.PasteClipboard(trans, 0, 1);
|
2009-12-04 08:08:41 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-12-15 12:26:22 +00:00
|
|
|
case MNU_PASTE_TRANSFORM: {
|
|
|
|
if(SS.clipboard.r.n == 0) {
|
|
|
|
Error("Clipboard is empty; nothing to paste.");
|
|
|
|
break;
|
|
|
|
}
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2009-12-15 12:26:22 +00:00
|
|
|
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();
|
2015-03-18 17:02:11 +00:00
|
|
|
SS.ScheduleShowTW();
|
2009-12-04 08:08:41 +00:00
|
|
|
break;
|
2009-12-15 12:26:22 +00:00
|
|
|
}
|
2009-12-04 08:08:41 +00:00
|
|
|
|
|
|
|
case MNU_COPY:
|
|
|
|
SS.GW.CopySelection();
|
|
|
|
SS.GW.ClearSelection();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MNU_CUT:
|
|
|
|
SS.UndoRemember();
|
|
|
|
SS.GW.CopySelection();
|
|
|
|
SS.GW.DeleteSelection();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MNU_DELETE:
|
|
|
|
SS.UndoRemember();
|
|
|
|
SS.GW.DeleteSelection();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: oops();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-16 19:51:20 +00:00
|
|
|
bool TextWindow::EditControlDoneForPaste(const char *s) {
|
2010-10-12 03:13:41 +00:00
|
|
|
Expr *e;
|
2009-12-15 12:26:22 +00:00
|
|
|
switch(edit.meaning) {
|
|
|
|
case EDIT_PASTE_TIMES_REPEATED: {
|
2010-10-12 03:13:41 +00:00
|
|
|
e = Expr::From(s, true);
|
|
|
|
if(!e) break;
|
|
|
|
int v = (int)e->Eval();
|
2009-12-15 12:26:22 +00:00
|
|
|
if(v > 0) {
|
|
|
|
shown.paste.times = v;
|
|
|
|
} else {
|
|
|
|
Error("Number of copies to paste must be at least one.");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EDIT_PASTE_ANGLE:
|
2010-10-12 03:13:41 +00:00
|
|
|
e = Expr::From(s, true);
|
|
|
|
if(!e) break;
|
|
|
|
shown.paste.theta = WRAP_SYMMETRIC((e->Eval())*PI/180, 2*PI);
|
2009-12-15 12:26:22 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EDIT_PASTE_SCALE: {
|
2010-10-12 03:13:41 +00:00
|
|
|
e = Expr::From(s, true);
|
|
|
|
double v = e->Eval();
|
2009-12-15 12:26:22 +00:00
|
|
|
if(fabs(v) > 1e-6) {
|
|
|
|
shown.paste.scale = v;
|
|
|
|
} else {
|
|
|
|
Error("Scale cannot be zero.");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Use C99 integer types and C++ boolean types/values
This change comprehensively replaces the use of Microsoft-standard integer
and boolean types with their C99/C++ standard equivalents, as the latter is
more appropriate for a cross-platform application. With matter-of-course
exceptions in the Win32-specific code, the types/values have been converted
as follows:
QWORD --> uint64_t
SQWORD --> int64_t
DWORD --> uint32_t
SDWORD --> int32_t
WORD --> uint16_t
SWORD --> int16_t
BYTE --> uint8_t
BOOL --> bool
TRUE --> true
FALSE --> false
The following related changes are also included:
* Added C99 integer type definitions for Windows, as stdint.h is not
available prior to Visual Studio 2010
* Changed types of some variables in the SolveSpace class from 'int' to
'bool', as they actually represent boolean settings
* Implemented new Cnf{Freeze,Thaw}Bool() functions to support boolean
variables in the Registry
* Cnf{Freeze,Thaw}DWORD() are now Cnf{Freeze,Thaw}Int()
* TtfFont::Get{WORD,DWORD}() are now TtfFont::Get{USHORT,ULONG}() (names
inspired by the OpenType spec)
* RGB colors are packed into an integer of type uint32_t (nee DWORD), but
in a few places, these were represented by an int; these have been
corrected to uint32_t
2013-10-02 05:45:13 +00:00
|
|
|
void TextWindow::ScreenChangePasteTransformed(int link, uint32_t v) {
|
2009-12-15 12:26:22 +00:00
|
|
|
switch(link) {
|
|
|
|
case 't':
|
2016-01-27 05:13:04 +00:00
|
|
|
SS.TW.ShowEditControl(10, 13, ssprintf("%d", SS.TW.shown.paste.times));
|
2009-12-15 12:26:22 +00:00
|
|
|
SS.TW.edit.meaning = EDIT_PASTE_TIMES_REPEATED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'r':
|
2016-01-27 05:13:04 +00:00
|
|
|
SS.TW.ShowEditControl(12, 13, ssprintf("%.3f", SS.TW.shown.paste.theta*180/PI));
|
2009-12-15 12:26:22 +00:00
|
|
|
SS.TW.edit.meaning = EDIT_PASTE_ANGLE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 's':
|
2016-01-27 05:13:04 +00:00
|
|
|
SS.TW.ShowEditControl(18, 13, ssprintf("%.3f", SS.TW.shown.paste.scale));
|
2009-12-15 12:26:22 +00:00
|
|
|
SS.TW.edit.meaning = EDIT_PASTE_SCALE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Use C99 integer types and C++ boolean types/values
This change comprehensively replaces the use of Microsoft-standard integer
and boolean types with their C99/C++ standard equivalents, as the latter is
more appropriate for a cross-platform application. With matter-of-course
exceptions in the Win32-specific code, the types/values have been converted
as follows:
QWORD --> uint64_t
SQWORD --> int64_t
DWORD --> uint32_t
SDWORD --> int32_t
WORD --> uint16_t
SWORD --> int16_t
BYTE --> uint8_t
BOOL --> bool
TRUE --> true
FALSE --> false
The following related changes are also included:
* Added C99 integer type definitions for Windows, as stdint.h is not
available prior to Visual Studio 2010
* Changed types of some variables in the SolveSpace class from 'int' to
'bool', as they actually represent boolean settings
* Implemented new Cnf{Freeze,Thaw}Bool() functions to support boolean
variables in the Registry
* Cnf{Freeze,Thaw}DWORD() are now Cnf{Freeze,Thaw}Int()
* TtfFont::Get{WORD,DWORD}() are now TtfFont::Get{USHORT,ULONG}() (names
inspired by the OpenType spec)
* RGB colors are packed into an integer of type uint32_t (nee DWORD), but
in a few places, these were represented by an int; these have been
corrected to uint32_t
2013-10-02 05:45:13 +00:00
|
|
|
void TextWindow::ScreenPasteTransformed(int link, uint32_t v) {
|
2009-12-15 12:26:22 +00:00
|
|
|
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.");
|
|
|
|
}
|
2010-01-04 03:17:09 +00:00
|
|
|
SS.GW.ClearSelection();
|
2009-12-15 12:26:22 +00:00
|
|
|
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.");
|
|
|
|
}
|
2010-01-04 03:17:09 +00:00
|
|
|
SS.GW.ClearSelection();
|
2009-12-15 12:26:22 +00:00
|
|
|
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();
|
2010-01-04 03:17:09 +00:00
|
|
|
SS.GW.ClearSelection();
|
2009-12-15 12:26:22 +00:00
|
|
|
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);
|
2015-03-18 17:02:11 +00:00
|
|
|
SS.ScheduleShowTW();
|
2009-12-15 12:26:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextWindow::ShowPasteTransformed(void) {
|
|
|
|
Printf(true, "%FtPASTE TRANSFORMED%E");
|
2010-05-09 18:25:23 +00:00
|
|
|
Printf(true, "%Ba %Ftrepeat%E %d time%s %Fl%Lt%f[change]%E",
|
2009-12-15 12:26:22 +00:00
|
|
|
shown.paste.times, (shown.paste.times == 1) ? "" : "s",
|
|
|
|
&ScreenChangePasteTransformed);
|
2010-05-09 18:25:23 +00:00
|
|
|
Printf(false, "%Bd %Ftrotate%E %@ degrees %Fl%Lr%f[change]%E",
|
2009-12-15 12:26:22 +00:00
|
|
|
shown.paste.theta*180/PI,
|
|
|
|
&ScreenChangePasteTransformed);
|
2010-05-09 18:25:23 +00:00
|
|
|
Printf(false, "%Ba %Ftabout pt%E (%s, %s, %s) %Fl%Lo%f[use selected]%E",
|
2015-11-06 08:40:12 +00:00
|
|
|
SS.MmToString(shown.paste.origin.x).c_str(),
|
|
|
|
SS.MmToString(shown.paste.origin.y).c_str(),
|
|
|
|
SS.MmToString(shown.paste.origin.z).c_str(),
|
2009-12-15 12:26:22 +00:00
|
|
|
&ScreenPasteTransformed);
|
2010-05-09 18:25:23 +00:00
|
|
|
Printf(false, "%Bd %Fttranslate%E (%s, %s, %s) %Fl%Lt%f[use selected]%E",
|
2015-11-06 08:40:12 +00:00
|
|
|
SS.MmToString(shown.paste.trans.x).c_str(),
|
|
|
|
SS.MmToString(shown.paste.trans.y).c_str(),
|
|
|
|
SS.MmToString(shown.paste.trans.z).c_str(),
|
2009-12-15 12:26:22 +00:00
|
|
|
&ScreenPasteTransformed);
|
2010-05-09 18:25:23 +00:00
|
|
|
Printf(false, "%Ba %Ftscale%E %@ %Fl%Ls%f[change]%E",
|
2009-12-15 12:26:22 +00:00
|
|
|
shown.paste.scale,
|
|
|
|
&ScreenChangePasteTransformed);
|
|
|
|
|
|
|
|
Printf(true, " %Fl%Lg%fpaste transformed now%E", &ScreenPasteTransformed);
|
|
|
|
|
|
|
|
Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
|
|
|
|
}
|
|
|
|
|