Solvespace

Форк
0
/
clipboard.cpp 
519 строк · 18.2 Кб
1
//-----------------------------------------------------------------------------
2
// The clipboard that gets manipulated when the user selects Edit -> Cut,
3
// Copy, Paste, etc.; may contain entities only, not constraints.
4
//
5
// Copyright 2008-2013 Jonathan Westhues.
6
//-----------------------------------------------------------------------------
7
#include "solvespace.h"
8

9
void SolveSpaceUI::Clipboard::Clear() {
10
    c.Clear();
11
    r.Clear();
12
}
13

14
bool SolveSpaceUI::Clipboard::ContainsEntity(hEntity he) {
15
    if(he == Entity::NO_ENTITY)
16
        return true;
17

18
    ClipboardRequest *cr;
19
    for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
20
        if(cr->oldEnt == he)
21
            return true;
22

23
        for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
24
            if(cr->oldPointEnt[i] == he)
25
                return true;
26
        }
27
    }
28
    return false;
29
}
30

31
hEntity SolveSpaceUI::Clipboard::NewEntityFor(hEntity he) {
32
    if(he == Entity::NO_ENTITY)
33
        return Entity::NO_ENTITY;
34

35
    ClipboardRequest *cr;
36
    for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
37
        if(cr->oldEnt == he)
38
            return cr->newReq.entity(0);
39

40
        for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
41
            if(cr->oldPointEnt[i] == he)
42
                return cr->newReq.entity(1+i);
43
        }
44
    }
45

46
    ssassert(false, "Expected to find entity in some clipboard request");
47
}
48

49
void GraphicsWindow::DeleteSelection() {
50
    SK.request.ClearTags();
51
    SK.constraint.ClearTags();
52
    List<Selection> *ls = &(selection);
53
    for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
54
        hRequest r = { 0 };
55
        if(s->entity.v && s->entity.isFromRequest()) {
56
            r = s->entity.request();
57
        }
58
        if(r.v && !r.IsFromReferences()) {
59
            SK.request.Tag(r, 1);
60
        }
61
        if(s->constraint.v) {
62
            SK.constraint.Tag(s->constraint, 1);
63
        }
64
    }
65

66
    SK.constraint.RemoveTagged();
67
    // Note that this regenerates and clears the selection, to avoid
68
    // lingering references to the just-deleted items.
69
    DeleteTaggedRequests();
70
}
71

72
void GraphicsWindow::CopySelection() {
73
    SS.clipboard.Clear();
74

75
    Entity *wrkpl  = SK.GetEntity(ActiveWorkplane());
76
    Entity *wrkpln = SK.GetEntity(wrkpl->normal);
77
    Vector u = wrkpln->NormalU(),
78
           v = wrkpln->NormalV(),
79
           n = wrkpln->NormalN(),
80
           p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
81

82
    List<Selection> *ls = &(selection);
83
    for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
84
        if(!s->entity.v) continue;
85
        // Work only on entities that have requests that will generate them.
86
        Entity *e = SK.GetEntity(s->entity);
87
        bool hasDistance;
88
        Request::Type req;
89
        int pts;
90
        if(!EntReqTable::GetEntityInfo(e->type, e->extraPoints,
91
                &req, &pts, NULL, &hasDistance))
92
        {
93
            if(!e->h.isFromRequest()) continue;
94
            Request *r = SK.GetRequest(e->h.request());
95
            if(r->type != Request::Type::DATUM_POINT) continue;
96
            EntReqTable::GetEntityInfo((Entity::Type)0, e->extraPoints,
97
                &req, &pts, NULL, &hasDistance);
98
        }
99
        if(req == Request::Type::WORKPLANE) continue;
100

101
        ClipboardRequest cr = {};
102
        cr.type         = req;
103
        cr.extraPoints  = e->extraPoints;
104
        cr.style        = e->style;
105
        cr.str          = e->str;
106
        cr.font         = e->font;
107
        cr.file         = e->file;
108
        cr.construction = e->construction;
109
        {for(int i = 0; i < pts; i++) {
110
            Vector pt;
111
            if(req == Request::Type::DATUM_POINT) {
112
                pt = e->PointGetNum();
113
            } else {
114
                pt = SK.GetEntity(e->point[i])->PointGetNum();
115
            }
116
            pt = pt.Minus(p);
117
            pt = pt.DotInToCsys(u, v, n);
118
            cr.point[i] = pt;
119
        }}
120
        if(hasDistance) {
121
            cr.distance = SK.GetEntity(e->distance)->DistanceGetNum();
122
        }
123

124
        cr.oldEnt = e->h;
125
        for(int i = 0; i < pts; i++) {
126
            cr.oldPointEnt[i] = e->point[i];
127
        }
128

129
        SS.clipboard.r.Add(&cr);
130
    }
131

132
    for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
133
        if(!s->constraint.v) continue;
134

135
        Constraint *c = SK.GetConstraint(s->constraint);
136
        if(c->type == Constraint::Type::COMMENT) {
137
            SS.clipboard.c.Add(c);
138
        }
139
    }
140

141
    for(Constraint &c : SK.constraint) {
142
        if(!SS.clipboard.ContainsEntity(c.ptA) ||
143
           !SS.clipboard.ContainsEntity(c.ptB) ||
144
           !SS.clipboard.ContainsEntity(c.entityA) ||
145
           !SS.clipboard.ContainsEntity(c.entityB) ||
146
           !SS.clipboard.ContainsEntity(c.entityC) ||
147
           !SS.clipboard.ContainsEntity(c.entityD) ||
148
           c.type == Constraint::Type::COMMENT) {
149
            continue;
150
        }
151
        SS.clipboard.c.Add(&c);
152
    }
153
}
154

155
void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
156
    Entity *wrkpl  = SK.GetEntity(ActiveWorkplane());
157
    Entity *wrkpln = SK.GetEntity(wrkpl->normal);
158
    Vector u = wrkpln->NormalU(),
159
           v = wrkpln->NormalV(),
160
           n = wrkpln->NormalN(),
161
           p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
162

163
    // For arcs, reflection involves swapping the endpoints, or otherwise
164
    // the arc gets inverted.
165
    auto mapPoint = [scale](hEntity he) {
166
        if(he.v == 0) return he;
167

168
        if(scale < 0) {
169
            hRequest hr = he.request();
170
            Request *r = SK.GetRequest(hr);
171
            if(r->type == Request::Type::ARC_OF_CIRCLE) {
172
                if(he == hr.entity(2)) {
173
                    return hr.entity(3);
174
                } else if(he == hr.entity(3)) {
175
                    return hr.entity(2);
176
                }
177
            }
178
        }
179
        return he;
180
    };
181

182
    ClipboardRequest *cr;
183
    for(cr = SS.clipboard.r.First(); cr; cr = SS.clipboard.r.NextAfter(cr)) {
184
        hRequest hr = AddRequest(cr->type, /*rememberForUndo=*/false);
185
        Request *r = SK.GetRequest(hr);
186
        r->extraPoints  = cr->extraPoints;
187
        r->style        = cr->style;
188
        r->str          = cr->str;
189
        r->font         = cr->font;
190
        r->file         = cr->file;
191
        r->construction = cr->construction;
192
        // Need to regen to get the right number of points, if extraPoints
193
        // changed.
194
        SS.GenerateAll(SolveSpaceUI::Generate::REGEN);
195
        SS.MarkGroupDirty(r->group);
196
        bool hasDistance;
197
        int i, pts;
198
        EntReqTable::GetRequestInfo(r->type, r->extraPoints,
199
            NULL, &pts, NULL, &hasDistance);
200
        for(i = 0; i < pts; i++) {
201
            Vector pt = cr->point[i];
202
            // We need the reflection to occur within the workplane; it may
203
            // otherwise correspond to just a rotation as projected.
204
            if(scale < 0) {
205
                pt.x *= -1;
206
            }
207
            // Likewise the scale, which could otherwise take us out of the
208
            // workplane.
209
            pt = pt.ScaledBy(fabs(scale));
210
            pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0));
211
            pt = pt.Plus(p);
212
            pt = pt.RotatedAbout(n, theta);
213
            pt = pt.Plus(trans);
214
            int j = (r->type == Request::Type::DATUM_POINT) ? i : i + 1;
215
            SK.GetEntity(mapPoint(hr.entity(j)))->PointForceTo(pt);
216
        }
217
        if(hasDistance) {
218
            SK.GetEntity(hr.entity(64))->DistanceForceTo(
219
                                            cr->distance*fabs(scale));
220
        }
221

222
        cr->newReq = hr;
223
        MakeSelected(hr.entity(0));
224
        for(i = 0; i < pts; i++) {
225
            int j = (r->type == Request::Type::DATUM_POINT) ? i : i + 1;
226
            MakeSelected(hr.entity(j));
227
        }
228
    }
229
    Constraint *cc;
230
    for(cc = SS.clipboard.c.First(); cc; cc = SS.clipboard.c.NextAfter(cc)) {
231
        Constraint c = {};
232
        c.group = SS.GW.activeGroup;
233
        c.workplane = SS.GW.ActiveWorkplane();
234
        c.type = cc->type;
235
        c.valA = cc->valA;
236
        c.ptA = SS.clipboard.NewEntityFor(mapPoint(cc->ptA));
237
        c.ptB = SS.clipboard.NewEntityFor(mapPoint(cc->ptB));
238
        c.entityA = SS.clipboard.NewEntityFor(cc->entityA);
239
        c.entityB = SS.clipboard.NewEntityFor(cc->entityB);
240
        c.entityC = SS.clipboard.NewEntityFor(cc->entityC);
241
        c.entityD = SS.clipboard.NewEntityFor(cc->entityD);
242
        c.other = cc->other;
243
        c.other2 = cc->other2;
244
        c.reference = cc->reference;
245
        c.disp = cc->disp;
246
        c.comment = cc->comment;
247
        bool dontAddConstraint = false;
248
        switch(c.type) {
249
            case Constraint::Type::COMMENT:
250
                c.disp.offset = c.disp.offset.Plus(trans);
251
                break;
252
            case Constraint::Type::PT_LINE_DISTANCE:
253
                c.valA *= scale;
254
                break;
255
            case Constraint::Type::PT_PT_DISTANCE:
256
            case Constraint::Type::PROJ_PT_DISTANCE:
257
            case Constraint::Type::DIAMETER:
258
                c.valA *= fabs(scale);
259
                break;
260
            case Constraint::Type::ARC_LINE_TANGENT: {
261
                Entity *line = SK.GetEntity(c.entityB),
262
                       *arc  = SK.GetEntity(c.entityA);
263
                if(line->type == Entity::Type::ARC_OF_CIRCLE) {
264
                    swap(line, arc);
265
                }
266
                Constraint::ConstrainArcLineTangent(&c, line, arc);
267
                break;
268
            }
269
            case Constraint::Type::CUBIC_LINE_TANGENT: {
270
                Entity *line  = SK.GetEntity(c.entityB),
271
                       *cubic = SK.GetEntity(c.entityA);
272
                if(line->type == Entity::Type::CUBIC) {
273
                    swap(line, cubic);
274
                }
275
                Constraint::ConstrainCubicLineTangent(&c, line, cubic);
276
                break;
277
            }
278
            case Constraint::Type::CURVE_CURVE_TANGENT: {
279
                Entity *eA = SK.GetEntity(c.entityA),
280
                       *eB = SK.GetEntity(c.entityB);
281
                Constraint::ConstrainCurveCurveTangent(&c, eA, eB);
282
                break;
283
            }
284
            case Constraint::Type::HORIZONTAL:
285
            case Constraint::Type::VERTICAL:
286
                // When rotating 90 or 270 degrees, swap the vertical / horizontal constraints
287
                if (EXACT(fmod(theta + (PI/2), PI) == 0)) {
288
                    if(c.type == Constraint::Type::HORIZONTAL) {
289
                        c.type = Constraint::Type::VERTICAL;
290
                    } else {
291
                        c.type = Constraint::Type::HORIZONTAL;
292
                    }
293
                } else if (fmod(theta, PI/2) != 0) {
294
                    dontAddConstraint = true;
295
                }
296
                break;
297
            default:
298
                break;
299
        }
300
        if (!dontAddConstraint) {
301
            hConstraint hc = Constraint::AddConstraint(&c, /*rememberForUndo=*/false);
302
            if(c.type == Constraint::Type::COMMENT) {
303
                MakeSelected(hc);
304
            }
305
        }
306
    }
307
}
308

309
void GraphicsWindow::MenuClipboard(Command id) {
310
    if(id != Command::DELETE && !SS.GW.LockedInWorkplane()) {
311
        Error(_("Cut, paste, and copy work only in a workplane.\n\n"
312
                "Activate one with Sketch -> In Workplane."));
313
        return;
314
    }
315

316
    switch(id) {
317
        case Command::PASTE: {
318
            SS.UndoRemember();
319
            Vector trans = SS.GW.projRight.ScaledBy(80/SS.GW.scale).Plus(
320
                           SS.GW.projUp   .ScaledBy(40/SS.GW.scale));
321
            SS.GW.ClearSelection();
322
            SS.GW.PasteClipboard(trans, 0, 1);
323
            break;
324
        }
325

326
        case Command::PASTE_TRANSFORM: {
327
            if(SS.clipboard.r.IsEmpty()) {
328
                Error(_("Clipboard is empty; nothing to paste."));
329
                break;
330
            }
331

332
            Entity *wrkpl  = SK.GetEntity(SS.GW.ActiveWorkplane());
333
            Vector p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
334
            SS.TW.shown.paste.times  = 1;
335
            SS.TW.shown.paste.trans  = Vector::From(0, 0, 0);
336
            SS.TW.shown.paste.theta  = 0;
337
            SS.TW.shown.paste.origin = p;
338
            SS.TW.shown.paste.scale  = 1;
339
            SS.TW.GoToScreen(TextWindow::Screen::PASTE_TRANSFORMED);
340
            SS.GW.ForceTextWindowShown();
341
            SS.ScheduleShowTW();
342
            break;
343
        }
344

345
        case Command::COPY:
346
            SS.GW.CopySelection();
347
            SS.GW.ClearSelection();
348
            break;
349

350
        case Command::CUT:
351
            SS.UndoRemember();
352
            SS.GW.CopySelection();
353
            SS.GW.DeleteSelection();
354
            break;
355

356
        case Command::DELETE:
357
            SS.UndoRemember();
358
            SS.GW.DeleteSelection();
359
            break;
360

361
        default: ssassert(false, "Unexpected menu ID");
362
    }
363
}
364

365
bool TextWindow::EditControlDoneForPaste(const std::string &s) {
366
    Expr *e;
367
    switch(edit.meaning) {
368
        case Edit::PASTE_TIMES_REPEATED: {
369
            e = Expr::From(s, /*popUpError=*/true);
370
            if(!e) break;
371
            int v = (int)e->Eval();
372
            if(v > 0) {
373
                shown.paste.times = v;
374
            } else {
375
                Error(_("Number of copies to paste must be at least one."));
376
            }
377
            break;
378
        }
379
        case Edit::PASTE_ANGLE:
380
            e = Expr::From(s, /*popUpError=*/true);
381
            if(!e) break;
382
            shown.paste.theta = WRAP_SYMMETRIC((e->Eval())*PI/180, 2*PI);
383
            break;
384

385
        case Edit::PASTE_SCALE: {
386
            e = Expr::From(s, /*popUpError=*/true);
387
            double v = e->Eval();
388
            if(fabs(v) > 1e-6) {
389
                shown.paste.scale = shown.paste.scale < 0 ? -v : v;
390
            } else {
391
                Error(_("Scale cannot be zero."));
392
            }
393
            break;
394
        }
395

396
        default:
397
            return false;
398
    }
399
    return true;
400
}
401

402
void TextWindow::ScreenChangePasteTransformed(int link, uint32_t v) {
403
    switch(link) {
404
        case 't':
405
            SS.TW.ShowEditControl(13, ssprintf("%d", SS.TW.shown.paste.times));
406
            SS.TW.edit.meaning = Edit::PASTE_TIMES_REPEATED;
407
            break;
408

409
        case 'r':
410
            SS.TW.ShowEditControl(13, ssprintf("%.3f", SS.TW.shown.paste.theta*180/PI));
411
            SS.TW.edit.meaning = Edit::PASTE_ANGLE;
412
            break;
413

414
        case 's':
415
            SS.TW.ShowEditControl(13, ssprintf("%.3f", fabs(SS.TW.shown.paste.scale)));
416
            SS.TW.edit.meaning = Edit::PASTE_SCALE;
417
            break;
418

419
        case 'f':
420
            SS.TW.shown.paste.scale *= -1;
421
            break;
422
    }
423
}
424

425
void TextWindow::ScreenPasteTransformed(int link, uint32_t v) {
426
    SS.GW.GroupSelection();
427
    switch(link) {
428
        case 'o':
429
            if(SS.GW.gs.points == 1 && SS.GW.gs.n == 1) {
430
                Entity *e = SK.GetEntity(SS.GW.gs.point[0]);
431
                SS.TW.shown.paste.origin = e->PointGetNum();
432
            } else {
433
                Error(_("Select one point to define origin of rotation."));
434
            }
435
            SS.GW.ClearSelection();
436
            break;
437

438
        case 't':
439
            if(SS.GW.gs.points == 2 && SS.GW.gs.n == 2) {
440
                Entity *pa = SK.GetEntity(SS.GW.gs.point[0]),
441
                       *pb = SK.GetEntity(SS.GW.gs.point[1]);
442
                SS.TW.shown.paste.trans =
443
                    (pb->PointGetNum()).Minus(pa->PointGetNum());
444
            } else {
445
                Error(_("Select two points to define translation vector."));
446
            }
447
            SS.GW.ClearSelection();
448
            break;
449

450
        case 'g': {
451
            if(fabs(SS.TW.shown.paste.theta) < LENGTH_EPS &&
452
               SS.TW.shown.paste.trans.Magnitude() < LENGTH_EPS &&
453
               SS.TW.shown.paste.times != 1)
454
            {
455
                Message(_("Transformation is identity. So all copies will be "
456
                          "exactly on top of each other."));
457
            }
458
            if(SS.TW.shown.paste.times*SS.clipboard.r.n > 100) {
459
                Error(_("Too many items to paste; split this into smaller "
460
                        "pastes."));
461
                break;
462
            }
463
            if(!SS.GW.LockedInWorkplane()) {
464
                Error(_("No workplane active."));
465
                break;
466
            }
467
            Entity *wrkpl  = SK.GetEntity(SS.GW.ActiveWorkplane());
468
            Entity *wrkpln = SK.GetEntity(wrkpl->normal);
469
            Vector wn = wrkpln->NormalN();
470
            SS.UndoRemember();
471
            SS.GW.ClearSelection();
472
            for(int i = 0; i < SS.TW.shown.paste.times; i++) {
473
                Vector trans  = SS.TW.shown.paste.trans.ScaledBy(i+1),
474
                       origin = SS.TW.shown.paste.origin;
475
                double theta = SS.TW.shown.paste.theta*(i+1);
476
                // desired transformation is Q*(p - o) + o + t =
477
                // Q*p - Q*o + o + t = Q*p + (t + o - Q*o)
478
                Vector t = trans.Plus(
479
                           origin).Minus(
480
                           origin.RotatedAbout(wn, theta));
481

482
                SS.GW.PasteClipboard(t, theta, SS.TW.shown.paste.scale);
483
            }
484
            SS.TW.GoToScreen(Screen::LIST_OF_GROUPS);
485
            SS.ScheduleShowTW();
486
            break;
487
        }
488
    }
489
}
490

491
void TextWindow::ShowPasteTransformed() {
492
    Printf(true, "%FtPASTE TRANSFORMED%E");
493
    Printf(true,  "%Ba   %Ftrepeat%E    %d time%s %Fl%Lt%f[change]%E",
494
        shown.paste.times, (shown.paste.times == 1) ? "" : "s",
495
        &ScreenChangePasteTransformed);
496
    Printf(false, "%Bd   %Ftrotate%E    %@ degrees %Fl%Lr%f[change]%E",
497
        shown.paste.theta*180/PI,
498
        &ScreenChangePasteTransformed);
499
    Printf(false, "%Ba   %Ftabout pt%E  (%s, %s, %s) %Fl%Lo%f[use selected]%E",
500
            SS.MmToString(shown.paste.origin.x).c_str(),
501
            SS.MmToString(shown.paste.origin.y).c_str(),
502
            SS.MmToString(shown.paste.origin.z).c_str(),
503
        &ScreenPasteTransformed);
504
    Printf(false, "%Bd   %Fttranslate%E (%s, %s, %s) %Fl%Lt%f[use selected]%E",
505
            SS.MmToString(shown.paste.trans.x).c_str(),
506
            SS.MmToString(shown.paste.trans.y).c_str(),
507
            SS.MmToString(shown.paste.trans.z).c_str(),
508
        &ScreenPasteTransformed);
509
    Printf(false, "%Ba   %Ftscale%E     %@ %Fl%Ls%f[change]%E",
510
        fabs(shown.paste.scale),
511
        &ScreenChangePasteTransformed);
512
    Printf(false, "%Ba   %Ftmirror%E    %Fd%Lf%f%s  flip%E",
513
        &ScreenChangePasteTransformed,
514
        shown.paste.scale < 0 ? CHECK_TRUE : CHECK_FALSE);
515

516
    Printf(true, " %Fl%Lg%fpaste transformed now%E", &ScreenPasteTransformed);
517

518
    Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
519
}
520

521

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.