FreeCAD

Форк
0
/
GeoFeatureGroupExtension.cpp 
499 строк · 18.0 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2014 Jürgen Riegel <juergen.riegel@web.de>              *
3
 *   Copyright (c) 2015 Alexander Golubev (Fat-Zer) <fatzer2@gmail.com>    *
4
 *                                                                         *
5
 *   This file is part of the FreeCAD CAx development system.              *
6
 *                                                                         *
7
 *   This library is free software; you can redistribute it and/or         *
8
 *   modify it under the terms of the GNU Library General Public           *
9
 *   License as published by the Free Software Foundation; either          *
10
 *   version 2 of the License, or (at your option) any later version.      *
11
 *                                                                         *
12
 *   This library  is distributed in the hope that it will be useful,      *
13
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15
 *   GNU Library General Public License for more details.                  *
16
 *                                                                         *
17
 *   You should have received a copy of the GNU Library General Public     *
18
 *   License along with this library; see the file COPYING.LIB. If not,    *
19
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
20
 *   Suite 330, Boston, MA  02111-1307, USA                                *
21
 *                                                                         *
22
 ***************************************************************************/
23

24

25
#include "PreCompiled.h"
26

27
#include <App/Document.h>
28
#include <Base/Tools.h>
29

30
#include "GeoFeatureGroupExtension.h"
31
#include "Link.h"
32
#include "Origin.h"
33
#include "OriginFeature.h"
34
#include "OriginGroupExtension.h"
35

36

37
using namespace App;
38

39

40
EXTENSION_PROPERTY_SOURCE(App::GeoFeatureGroupExtension, App::GroupExtension)
41

42

43
//===========================================================================
44
// Feature
45
//===========================================================================
46

47
GeoFeatureGroupExtension::GeoFeatureGroupExtension()
48
{
49
    initExtensionType(GeoFeatureGroupExtension::getExtensionClassTypeId());
50
    Group.setScope(LinkScope::Child);
51
}
52

53
GeoFeatureGroupExtension::~GeoFeatureGroupExtension() = default;
54

55
void GeoFeatureGroupExtension::initExtension(ExtensionContainer* obj) {
56

57
    if(!obj->isDerivedFrom(App::GeoFeature::getClassTypeId()))
58
        throw Base::RuntimeError("GeoFeatureGroupExtension can only be applied to GeoFeatures");
59

60
    App::GroupExtension::initExtension(obj);
61
}
62

63
PropertyPlacement& GeoFeatureGroupExtension::placement() {
64

65
    if(!getExtendedContainer())
66
        throw Base::RuntimeError("GeoFeatureGroupExtension was not applied to GeoFeature");
67

68
    return static_cast<App::GeoFeature*>(getExtendedContainer())->Placement;
69
}
70

71

72
void GeoFeatureGroupExtension::transformPlacement(const Base::Placement &transform)
73
{
74
    // NOTE: Keep in sync with APP::GeoFeature
75
    Base::Placement plm = this->placement().getValue();
76
    plm = transform * plm;
77
    this->placement().setValue(plm);
78
}
79

80
DocumentObject* GeoFeatureGroupExtension::getGroupOfObject(const DocumentObject* obj)
81
{
82
    if(!obj)
83
        return nullptr;
84

85
    //we will find origins, but not origin features
86
    if(obj->isDerivedFrom(App::OriginFeature::getClassTypeId()))
87
        return OriginGroupExtension::getGroupOfObject(obj);
88

89
    //compared to GroupExtension we do return here all GeoFeatureGroups including all extensions derived from it
90
    //like OriginGroup. That is needed as we use this function to get all local coordinate systems. Also there
91
    //is no reason to distinguish between GeoFeatuerGroups, there is only between group/geofeaturegroup
92
    auto list = obj->getInList();
93
    for (auto inObj : list) {
94

95
        //There is a chance that a derived geofeaturegroup links with a local link and hence is not
96
        //the parent group even though it links to the object. We use hasObject as one and only truth
97
        //if it has the object within the group
98
        auto group = inObj->getExtensionByType<GeoFeatureGroupExtension>(true);
99
        if(group && group->hasObject(obj))
100
            return inObj;
101
    }
102

103
    return nullptr;
104
}
105

106
Base::Placement GeoFeatureGroupExtension::globalGroupPlacement()
107
{
108
    if (getExtendedObject()->isRecomputing()) {
109
        throw Base::RuntimeError("Global placement cannot be calculated on recompute");
110
    }
111

112
    std::unordered_set<GeoFeatureGroupExtension*> history;
113
    history.insert(this);
114
    return recursiveGroupPlacement(this, history);
115
}
116

117

118
Base::Placement GeoFeatureGroupExtension::recursiveGroupPlacement(GeoFeatureGroupExtension* group,
119
                                                                  std::unordered_set<GeoFeatureGroupExtension*>& history)
120
{
121
    history.insert(this);
122

123
    auto inList = group->getExtendedObject()->getInList();
124
    for (auto* link : inList) {
125
        auto parent = link->getExtensionByType<GeoFeatureGroupExtension>(true);
126
        if (parent && parent->hasObject(group->getExtendedObject())) {
127
            // Cyclic dependencies detected
128
            if (history.count(parent) > 0) {
129
                break;
130
            }
131
            return recursiveGroupPlacement(parent, history) * group->placement().getValue();
132
        }
133
    }
134

135
    return group->placement().getValue();
136
}
137

138
std::vector<DocumentObject*> GeoFeatureGroupExtension::addObjects(std::vector<App::DocumentObject*> objects)  {
139

140
    std::vector<DocumentObject*> grp = Group.getValues();
141
    std::vector<DocumentObject*> ret;
142

143
    for(auto object : objects) {
144

145
        if(!allowObject(object))
146
            continue;
147

148
        //cross CoordinateSystem links are not allowed, so we need to move the whole link group
149
        std::vector<App::DocumentObject*> links = getCSRelevantLinks(object);
150
        links.push_back(object);
151

152
        for( auto obj : links) {
153
            //only one geofeaturegroup per object.
154
            auto *group = App::GeoFeatureGroupExtension::getGroupOfObject(obj);
155
            if(group && group != getExtendedObject())
156
                group->getExtensionByType<App::GroupExtension>()->removeObject(obj);
157

158
            if (!hasObject(obj)) {
159
                grp.push_back(obj);
160
                ret.push_back(obj);
161
            }
162
        }
163
    }
164

165
    Group.setValues(grp);
166
    return ret;
167
}
168

169
std::vector<DocumentObject*> GeoFeatureGroupExtension::removeObjects(std::vector<App::DocumentObject*> objects)  {
170

171
    std::vector<DocumentObject*> removed;
172
    std::vector<DocumentObject*> grp = Group.getValues();
173

174
    for(auto object : objects) {
175
        //cross CoordinateSystem links are not allowed, so we need to remove the whole link group
176
        std::vector< DocumentObject* > links = getCSRelevantLinks(object);
177
        links.push_back(object);
178

179
        //remove all links out of group
180
        for(auto link : links) {
181
            auto end = std::remove(grp.begin(), grp.end(), link);
182
            if(end != grp.end()) {
183
                grp.erase(end, grp.end());
184
                removed.push_back(link);
185
            }
186
        }
187
    }
188

189
    if(!removed.empty())
190
        Group.setValues(grp);
191

192
    return removed;
193
}
194

195
void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) {
196

197
    //objects are only allowed in a single GeoFeatureGroup
198
    if(p == &Group && !Group.testStatus(Property::User3)) {
199

200
        if((!getExtendedObject()->isRestoring()
201
                || getExtendedObject()->getDocument()->testStatus(Document::Importing))
202
            && !getExtendedObject()->getDocument()->isPerformingTransaction()) {
203

204
            bool error = false;
205
            auto corrected = Group.getValues();
206
            for(auto obj : Group.getValues()) {
207

208
                //we have already set the obj into the group, so in a case of multiple groups getGroupOfObject
209
                //would return anyone of it and hence it is possible that we miss an error. We need a custom check
210
                auto list = obj->getInList();
211
                for (auto in : list) {
212
                    if(in == getExtendedObject())
213
                        continue;
214
                    auto parent = in->getExtensionByType<GeoFeatureGroupExtension>(true);
215
                    if(parent && parent->hasObject(obj)) {
216
                        error = true;
217
                        corrected.erase(std::remove(corrected.begin(), corrected.end(), obj), corrected.end());
218
                    }
219
                }
220
            }
221

222
            //if an error was found we need to correct the values and inform the user
223
            if(error) {
224
                Base::ObjectStatusLocker<Property::Status, Property> guard(Property::User3, &Group);
225
                Group.setValues(corrected);
226
                throw Base::RuntimeError("Object can only be in a single GeoFeatureGroup");
227
            }
228
        }
229
    }
230

231
    App::GroupExtension::extensionOnChanged(p);
232
}
233

234

235
std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLinks(const DocumentObject* obj, LinkScope scope) {
236

237
    if(!obj)
238
        return {};
239

240
    //we get all linked objects. We can't use outList() as this includes the links from expressions
241
    std::vector< App::DocumentObject* > result;
242
    std::vector<App::Property*> list;
243
    obj->getPropertyList(list);
244
    for(App::Property* prop : list) {
245
        auto vec = getScopedObjectsFromLink(prop, scope);
246
        result.insert(result.end(), vec.begin(), vec.end());
247
    }
248

249
    //clear all null objects and duplicates
250
    std::sort(result.begin(), result.end());
251
    result.erase(std::unique(result.begin(), result.end()), result.end());
252

253
    return result;
254
}
255

256
std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLink(App::Property* prop, LinkScope scope) {
257

258
    if(!prop)
259
        return {};
260

261
    std::vector< App::DocumentObject* > result;
262
    auto link = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
263
    if(link && link->getScope()==scope)
264
        link->getLinks(result);
265

266
    return result;
267
}
268

269
void GeoFeatureGroupExtension::getCSOutList(const App::DocumentObject* obj,
270
                                            std::vector< DocumentObject* >& vec) {
271

272
    if(!obj)
273
        return;
274

275
    //we get all relevant linked objects. We can't use outList() as this includes the links from expressions,
276
    //also we only want links with scope Local
277
    auto result = getScopedObjectsFromLinks(obj, LinkScope::Local);
278

279
    //we remove all links to origin features and origins, they belong to a CS too and can't be moved
280
    result.erase(std::remove_if(result.begin(), result.end(), [](App::DocumentObject* obj)->bool {
281
        return (obj->isDerivedFrom(App::OriginFeature::getClassTypeId()) ||
282
                obj->isDerivedFrom(App::Origin::getClassTypeId()));
283
    }), result.end());
284

285
    vec.insert(vec.end(), result.begin(), result.end());
286

287
    //post process the vector
288
    std::sort(vec.begin(), vec.end());
289
    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
290
}
291

292
void GeoFeatureGroupExtension::getCSInList(const DocumentObject* obj,
293
                                           std::vector< DocumentObject* >& vec) {
294

295
    if(!obj)
296
        return;
297

298
    //search the inlist for objects that have non-expression links to us
299
    for(App::DocumentObject* parent : obj->getInList()) {
300

301
        //not interested in other groups (and here we mean all groups, normal ones and geofeaturegroup)
302
        if(parent->hasExtension(App::GroupExtension::getExtensionClassTypeId()))
303
            continue;
304

305
        //check if the link is real Local scope one or if it is a expression one (could also be both, so it is not
306
        //enough to check the expressions)
307
        auto res = getScopedObjectsFromLinks(parent, LinkScope::Local);
308
        if(std::find(res.begin(), res.end(), obj) != res.end())
309
            vec.push_back(parent);
310
    }
311

312
    //clear all duplicates
313
    std::sort(vec.begin(), vec.end());
314
    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
315
}
316

317
std::vector< DocumentObject* > GeoFeatureGroupExtension::getCSRelevantLinks(const DocumentObject* obj) {
318

319
    if(!obj)
320
        return {};
321

322
    //get all out links
323
    std::vector<DocumentObject*> vec;
324

325
    recursiveCSRelevantLinks(obj, vec);
326

327
    //post process the list after we added many things
328
    std::sort(vec.begin(), vec.end());
329
    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
330
    vec.erase(std::remove(vec.begin(), vec.end(), obj), vec.end());
331

332
    return vec;
333
}
334

335
void GeoFeatureGroupExtension::recursiveCSRelevantLinks(const DocumentObject* obj,
336
                                                        std::vector< DocumentObject* >& vec) {
337

338
    if(!obj)
339
        return;
340

341
    std::vector< DocumentObject* > links;
342
    getCSOutList(obj, links);
343
    getCSInList(obj, links);
344

345
    //go on traversing the graph in all directions!
346
    for(auto o : links) {
347
        if(!o || o == obj ||  std::find(vec.begin(), vec.end(), o) != vec.end())
348
            continue;
349

350
        vec.push_back(o);
351
        recursiveCSRelevantLinks(o, vec);
352
    }
353
}
354

355
bool GeoFeatureGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
356
        PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
357
{
358
    ret = nullptr;
359
    const char *dot;
360
    if(!subname || *subname==0) {
361
        auto obj = dynamic_cast<const DocumentObject*>(getExtendedContainer());
362
        ret = const_cast<DocumentObject*>(obj);
363
        if(mat && transform)
364
            *mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
365
    }else if((dot=strchr(subname,'.'))) {
366
        if(subname[0]!='$')
367
            ret = Group.findUsingMap(std::string(subname,dot));
368
        else{
369
            std::string name = std::string(subname+1,dot);
370
            for(auto child : Group.getValues()) {
371
                if(name == child->Label.getStrValue()){
372
                    ret = child;
373
                    break;
374
                }
375
            }
376
        }
377
        if(ret) {
378
            if(dot) ++dot;
379
            if(dot && *dot
380
                    && !ret->hasExtension(App::LinkBaseExtension::getExtensionClassTypeId())
381
                    && !ret->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()))
382
            {
383
                // Consider this
384
                // Body
385
                //  | -- Pad
386
                //        | -- Sketch
387
                //
388
                // Suppose we want to getSubObject(Body,"Pad.Sketch.")
389
                // Because of the special property of geo feature group, both
390
                // Pad and Sketch are children of Body, so we can't call
391
                // getSubObject(Pad,"Sketch.") as this will transform Sketch
392
                // using Pad's placement.
393
                //
394
                const char *next = strchr(dot,'.');
395
                if(next) {
396
                    App::DocumentObject *nret=nullptr;
397
                    extensionGetSubObject(nret,dot,pyObj,mat,transform,depth+1);
398
                    if(nret) {
399
                        ret = nret;
400
                        return true;
401
                    }
402
                }
403
            }
404
            if(mat && transform)
405
                *mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
406
            ret = ret->getSubObject(dot?dot:"",pyObj,mat,true,depth+1);
407
        }
408
    }
409
    return true;
410
}
411

412
bool GeoFeatureGroupExtension::areLinksValid(const DocumentObject* obj) {
413

414
    if(!obj)
415
        return true;
416

417
    std::vector<App::Property*> list;
418
    obj->getPropertyList(list);
419
    for(App::Property* prop : list) {
420
        if(!isLinkValid(prop)) {
421
            return false;
422
        }
423
    }
424

425
    return true;
426
}
427

428
bool GeoFeatureGroupExtension::isLinkValid(App::Property* prop) {
429

430
    if(!prop)
431
        return true;
432

433
    //get the object that holds the property
434
    if(!prop->getContainer()->isDerivedFrom(App::DocumentObject::getClassTypeId()))
435
        return true; //this link comes not from a document object, scopes are meaningless
436
    auto obj = static_cast<App::DocumentObject*>(prop->getContainer());
437

438
    //no cross CS link for local links.
439
    auto result = getScopedObjectsFromLink(prop, LinkScope::Local);
440
    auto group = getGroupOfObject(obj);
441
    for(auto link : result) {
442
        if(getGroupOfObject(link) != group)
443
            return false;
444
    }
445

446
    //for links with scope SubGroup we need to check if all features are part of subgroups
447
    if(obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) {
448
        result = getScopedObjectsFromLink(prop, LinkScope::Child);
449
        auto groupExt = obj->getExtensionByType<App::GeoFeatureGroupExtension>();
450
        for(auto link : result) {
451
            if(!groupExt->hasObject(link, true))
452
                return false;
453
        }
454
    }
455

456
    return true;
457
}
458

459
void GeoFeatureGroupExtension::getInvalidLinkObjects(const DocumentObject* obj, std::vector< DocumentObject* >& vec) {
460

461
    if(!obj)
462
        return;
463

464
    //no cross CS link for local links.
465
    auto result = getScopedObjectsFromLinks(obj, LinkScope::Local);
466
    auto group = obj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) ? obj : getGroupOfObject(obj);
467
    for(auto link : result) {
468
        if(getGroupOfObject(link) != group)
469
            vec.push_back(link);
470
    }
471

472
    //for links with scope SubGroup we need to check if all features are part of subgroups
473
    if(group) {
474
        result = getScopedObjectsFromLinks(obj, LinkScope::Child);
475
        auto groupExt = group->getExtensionByType<App::GeoFeatureGroupExtension>();
476
        for(auto link : result) {
477
            if(!groupExt->hasObject(link, true))
478
                vec.push_back(link);
479
        }
480
    }
481
}
482

483
bool GeoFeatureGroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
484
    for(auto obj : Group.getValues()) {
485
        if(obj && obj->isAttachedToDocument() && !obj->testStatus(ObjectStatus::GeoExcluded))
486
            ret.push_back(std::string(obj->getNameInDocument())+'.');
487
    }
488
    return true;
489
}
490

491

492
// Python feature ---------------------------------------------------------
493

494
namespace App {
495
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GeoFeatureGroupExtensionPython, App::GeoFeatureGroupExtension)
496

497
// explicit template instantiation
498
template class AppExport ExtensionPythonT<GroupExtensionPythonT<GeoFeatureGroupExtension>>;
499
}
500

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

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

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

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