FreeCAD

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

23
#include "PreCompiled.h"
24

25
#ifndef _PreComp_
26
# include <sstream>
27
# include <Bnd_Box.hxx>
28
# include <BRepBndLib.hxx>
29
# include <BRepBuilderAPI_Copy.hxx>
30
# include <BRepTools.hxx>
31
# include <BRepTools_ShapeSet.hxx>
32
# include <OSD_OpenFile.hxx>
33
# include <Standard_Failure.hxx>
34
# include <Standard_Version.hxx>
35
# include <TopoDS.hxx>
36
#endif // _PreComp_
37

38
#include <App/Application.h>
39
#include <App/Document.h>
40
#include <App/DocumentObject.h>
41
#include <App/ObjectIdentifier.h>
42
#include <Base/Console.h>
43
#include <Base/Exception.h>
44
#include <Base/FileInfo.h>
45
#include <Base/Reader.h>
46
#include <Base/Stream.h>
47
#include <Base/Writer.h>
48

49
#include "PartFeature.h"
50
#include "PartPyCXX.h"
51
#include "PropertyTopoShape.h"
52
#include "TopoShapePy.h"
53
#include "PartFeature.h"
54

55
FC_LOG_LEVEL_INIT("App", true, true)
56

57
namespace sp = std::placeholders;
58
using namespace Part;
59

60
TYPESYSTEM_SOURCE(Part::PropertyPartShape , App::PropertyComplexGeoData)
61

62
PropertyPartShape::PropertyPartShape() = default;
63

64
PropertyPartShape::~PropertyPartShape() = default;
65

66
void PropertyPartShape::setValue(const TopoShape& sh)
67
{
68
    aboutToSetValue();
69
    _Shape = sh;
70
    auto obj = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
71
    if(obj) {
72
        auto tag = obj->getID();
73
        if(_Shape.Tag && tag!=_Shape.Tag) {
74
            auto hasher = _Shape.Hasher?_Shape.Hasher:obj->getDocument()->getStringHasher();
75
            _Shape.reTagElementMap(tag,hasher);
76
        } else
77
            _Shape.Tag = obj->getID();
78
        if (!_Shape.Hasher && _Shape.hasChildElementMap()) {
79
            _Shape.Hasher = obj->getDocument()->getStringHasher();
80
            _Shape.hashChildMaps();
81
        }
82
    }
83
    hasSetValue();
84
    _Ver.clear();
85
}
86

87
void PropertyPartShape::setValue(const TopoDS_Shape& sh, bool resetElementMap)
88
{
89
    aboutToSetValue();
90
    auto obj = dynamic_cast<App::DocumentObject*>(getContainer());
91
    if(obj)
92
        _Shape.Tag = obj->getID();
93
    _Shape.setShape(sh,resetElementMap);
94
    hasSetValue();
95
    _Ver.clear();
96
}
97

98
const TopoDS_Shape& PropertyPartShape::getValue() const
99
{
100
    return _Shape.getShape();
101
}
102

103
const TopoShape& PropertyPartShape::getShape() const
104
{
105
    _Shape.initCache(-1);
106
    // March, 2024 Toponaming project:  There was originally an unused feature to disable
107
    // elementMapping that has not been kept:
108
    //    if (Feature::isElementMappingDisabled(getContainer()))
109
    //        res.Tag = -1;
110
    //    else if (!res.Tag) {
111
    if (!_Shape.Tag) {
112
        if (auto parent = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer())) {
113
            _Shape.Tag = parent->getID();
114
        }
115
    }
116
    return _Shape;
117
}
118

119
const Data::ComplexGeoData* PropertyPartShape::getComplexData() const
120
{
121
    _Shape.initCache(-1);
122
    return &(this->_Shape);
123
}
124

125
Base::BoundBox3d PropertyPartShape::getBoundingBox() const
126
{
127
    Base::BoundBox3d box;
128
    if (_Shape.getShape().IsNull())
129
        return box;
130
    try {
131
        // If the shape is empty an exception may be thrown
132
        Bnd_Box bounds;
133
        BRepBndLib::Add(_Shape.getShape(), bounds);
134
        bounds.SetGap(0.0);
135
        Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
136
        bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
137

138
        box.MinX = xMin;
139
        box.MaxX = xMax;
140
        box.MinY = yMin;
141
        box.MaxY = yMax;
142
        box.MinZ = zMin;
143
        box.MaxZ = zMax;
144
    }
145
    catch (Standard_Failure&) {
146
    }
147

148
    return box;
149
}
150

151
void PropertyPartShape::setTransform(const Base::Matrix4D &rclTrf)
152
{
153
    _Shape.setTransform(rclTrf);
154
}
155

156
Base::Matrix4D PropertyPartShape::getTransform() const
157
{
158
    return _Shape.getTransform();
159
}
160

161
void PropertyPartShape::transformGeometry(const Base::Matrix4D &rclTrf)
162
{
163
    aboutToSetValue();
164
    _Shape.transformGeometry(rclTrf);
165
    hasSetValue();
166
}
167

168
PyObject *PropertyPartShape::getPyObject()
169
{
170
    Base::PyObjectBase* prop = static_cast<Base::PyObjectBase*>(_Shape.getPyObject());
171
    if (prop)
172
        prop->setConst();
173
    return prop;
174
}
175

176
void PropertyPartShape::setPyObject(PyObject *value)
177
{
178
    if (PyObject_TypeCheck(value, &(TopoShapePy::Type))) {
179
        auto shape = *static_cast<TopoShapePy*>(value)->getTopoShapePtr();
180
        auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
181
        if(owner && owner->getDocument()) {
182
            if(shape.Tag || shape.getElementMapSize()) {
183
                // We can't trust the meaning of the input shape tag, so we
184
                // remap anyway
185
                TopoShape res(owner->getID(),owner->getDocument()->getStringHasher(),shape.getShape());
186
                res.mapSubElement(shape);
187
                shape = res;
188
            }else{
189
                shape.Tag = owner->getID();
190
                if ( shape.Hasher ) {   // TODO: This null guard added during TNP transition
191
                    shape.Hasher->clear();
192
                }
193
            }
194
        }
195
        setValue(shape);
196
    }
197
    else {
198
        std::string error = std::string("type must be 'Shape', not ");
199
        error += value->ob_type->tp_name;
200
        throw Base::TypeError(error);
201
    }
202
}
203

204
App::Property *PropertyPartShape::Copy() const
205
{
206
    PropertyPartShape *prop = new PropertyPartShape();
207

208
    // March, 2024 Toponaming project:  There was originally a feature to enable making an element
209
    // copy ( new geometry and map ) that has not been kept:
210
//    if (PartParams::getShapePropertyCopy()) {
211
//        // makeElementCopy() consume too much memory for complex geometry.
212
//        prop->_Shape = this->_Shape.makeElementCopy();
213
//    } else
214
//        prop->_Shape = this->_Shape;
215
    prop->_Shape = this->_Shape;
216
    prop->_Ver = this->_Ver;
217
    return prop;
218
}
219

220
void PropertyPartShape::Paste(const App::Property &from)
221
{
222
    auto prop = Base::freecad_dynamic_cast<const PropertyPartShape>(&from);
223
    if(prop) {
224
        setValue(prop->_Shape);
225
        _Ver = prop->_Ver;
226
    }
227
}
228

229
unsigned int PropertyPartShape::getMemSize () const
230
{
231
    return _Shape.getMemSize();
232
}
233

234
void PropertyPartShape::getPaths(std::vector<App::ObjectIdentifier> &paths) const
235
{
236
    paths.push_back(App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::SimpleComponent(getName())
237
                    << App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String("ShapeType")));
238
    paths.push_back(App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::SimpleComponent(getName())
239
                    << App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String("Orientation")));
240
    paths.push_back(App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::SimpleComponent(getName())
241
                    << App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String("Length")));
242
    paths.push_back(App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::SimpleComponent(getName())
243
                    << App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String("Area")));
244
    paths.push_back(App::ObjectIdentifier(getContainer()) << App::ObjectIdentifier::Component::SimpleComponent(getName())
245
                    << App::ObjectIdentifier::Component::SimpleComponent(App::ObjectIdentifier::String("Volume")));
246
}
247

248
void PropertyPartShape::beforeSave() const
249
{
250
    _HasherIndex = 0;
251
    _SaveHasher = false;
252
    auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
253
    if(owner && !_Shape.isNull() && _Shape.getElementMapSize()>0) {
254
        auto ret = owner->getDocument()->addStringHasher(_Shape.Hasher);
255
        _HasherIndex = ret.second;
256
        _SaveHasher = ret.first;
257
        _Shape.beforeSave();
258
    }
259
}
260
void PropertyPartShape::Save (Base::Writer &writer) const
261
{
262
    //See SaveDocFile(), RestoreDocFile()
263
    writer.Stream() << writer.ind() << "<Part";
264
    auto owner = dynamic_cast<App::DocumentObject*>(getContainer());
265
    if(owner && !_Shape.isNull()
266
        && _Shape.getElementMapSize()>0
267
        && !_Shape.Hasher.isNull()) {
268
        writer.Stream() << " HasherIndex=\"" << _HasherIndex << '"';
269
        if(_SaveHasher)
270
            writer.Stream() << " SaveHasher=\"1\"";
271
    }
272
    std::string version;
273
    // If exporting, do not export mapped element name, but still make a mark
274
    if(owner) {
275
        if(!owner->isExporting())
276
            version = _Ver.size()?_Ver:owner->getElementMapVersion(this);
277
    }else
278
        version = _Ver.size()?_Ver:_Shape.getElementMapVersion();
279
    writer.Stream() << " ElementMap=\"" << version << '"';
280

281
    bool binary = writer.getMode("BinaryBrep");
282
    bool toXML = writer.isForceXML();
283
    if(!toXML) {
284
        writer.Stream() << " file=\""
285
                        << writer.addFile(getFileName(binary?".bin":".brp").c_str(), this)
286
                        << "\"/>\n";
287
    } else if(binary) {
288
        writer.Stream() << " binary=\"1\">\n";
289
        _Shape.exportBinary(writer.beginCharStream(Base::CharStreamFormat::Base64Encoded));
290
        writer.endCharStream() <<  writer.ind() << "</Part>\n";
291
    } else {
292
        writer.Stream() << " brep=\"1\">\n";
293
        _Shape.exportBrep(writer.beginCharStream(Base::CharStreamFormat::Raw)<<'\n');
294
        writer.endCharStream() << '\n' << writer.ind() << "</Part>\n";
295
    }
296

297
    if(_SaveHasher) {
298
        if(!toXML)
299
            _Shape.Hasher->setPersistenceFileName(getFileName(".Table").c_str());
300
        else
301
            _Shape.Hasher->setPersistenceFileName(0);
302
        _Shape.Hasher->Save(writer);
303
    }
304
    if(version.size()) {
305
        if(!toXML)
306
            _Shape.setPersistenceFileName(getFileName(".Map").c_str());
307
        else
308
            _Shape.setPersistenceFileName(0);
309
        _Shape.Save(writer);
310
    }
311
}
312

313
std::string PropertyPartShape::getElementMapVersion(bool restored) const {
314
    if(restored)
315
        return _Ver;
316
    return PropertyComplexGeoData::getElementMapVersion(false);
317
}
318

319
void PropertyPartShape::Restore(Base::XMLReader &reader)
320
{
321
    reader.readElement("Part");
322

323
    auto owner = Base::freecad_dynamic_cast<App::DocumentObject>(getContainer());
324
    _Ver = "?";
325
    bool has_ver = reader.hasAttribute("ElementMap");
326
    if (has_ver)
327
        _Ver = reader.getAttribute("ElementMap");
328

329
    int hasher_idx = static_cast<int>(reader.getAttributeAsInteger("HasherIndex", "-1"));
330
    int save_hasher = static_cast<int>(reader.getAttributeAsInteger("SaveHasher", "0"));
331

332
    TopoShape shape;
333

334
    if (reader.hasAttribute("file")) {
335
        std::string file = reader.getAttribute("file");
336
        if (!file.empty()) {
337
            // initiate a file read
338
            reader.addFile(file.c_str(), this);
339
        }
340
    }
341
    else if (reader.hasAttribute(("binary")) && reader.getAttributeAsInteger("binary")) {
342
        TopoShape shape;
343
        shape.importBinary(reader.beginCharStream());
344
        shape = shape.getShape();
345
    }
346
    else if (reader.hasAttribute("brep") && reader.getAttributeAsInteger("brep")) {
347
        shape.importBrep(reader.beginCharStream(Base::CharStreamFormat::Raw));
348
    }
349

350
    reader.readEndElement("Part");
351

352
    if(owner && hasher_idx>=0) {
353
        _Shape.Hasher = owner->getDocument()->getStringHasher(hasher_idx);
354
        if(save_hasher)
355
            _Shape.Hasher->Restore(reader);
356
    }
357

358
    if(has_ver) {
359
        // The file name here is not used for restore, but just a way to get
360
        // more useful error message if something wrong when restoring
361
        _Shape.setPersistenceFileName(getFileName().c_str());
362
        if(owner && owner->getDocument()->testStatus(App::Document::PartialDoc))
363
            _Shape.Restore(reader);
364
        else if(_Ver == "?" || _Ver.empty()) {
365
            // This indicate the shape is saved by legacy version without
366
            // element map info.
367
            if(owner) {
368
                // This will ask user for recompute after import
369
                owner->getDocument()->addRecomputeObject(owner);
370
            }
371
        }else{
372
            _Shape.Restore(reader);
373
            if (owner ? owner->checkElementMapVersion(this, _Ver.c_str())
374
                      : _Shape.checkElementMapVersion(_Ver.c_str())) {
375
                auto ver = owner?owner->getElementMapVersion(this):_Shape.getElementMapVersion();
376
                if(!owner || !owner->getNameInDocument() || !_Shape.getElementMapSize()) {
377
                    _Ver = ver;
378
                } else {
379
                    // version mismatch, signal for regenerating.
380
                    static const char *warnedDoc=0;
381
                    if(warnedDoc != owner->getDocument()->getName()) {
382
                        warnedDoc = owner->getDocument()->getName();
383
                        FC_WARN("Recomputation required for document '" << warnedDoc
384
                                                                        << "' on geo element version change in " << getFullName()
385
                                                                        << ": " << _Ver << " -> " << ver);
386
                    }
387
                    owner->getDocument()->addRecomputeObject(owner);
388
                }
389
            }
390
        }
391
    } else if(owner && !owner->getDocument()->testStatus(App::Document::PartialDoc)) {
392
        // Toponaming 09/2024:  Original code has an infrastructure for document parameters we aren't bringing in:
393
        // if(App::DocumentParams::getWarnRecomputeOnRestore()) {
394
        // However, this warning appeared on all files without element maps, and is now superseded by a user dialog
395
        // after loading that is triggered by any call to addRecomputeObject()
396
        // FC_WARN("Pending recompute for generating element map: " << owner->getFullName());
397
        owner->getDocument()->addRecomputeObject(owner);
398
    }
399

400
    if (!shape.isNull() || !_Shape.isNull()) {
401
        aboutToSetValue();
402
        _Shape.setShape(shape.getShape(),false);
403
        hasSetValue();
404
    }
405
}
406

407
void PropertyPartShape::afterRestore()
408
{
409
    if (_Shape.isRestoreFailed()) {
410
        // this cause GeoFeature::updateElementReference() to call
411
        // PropertyLinkBase::updateElementReferences() with reverse = true, in
412
        // order to try to regenerate the element map
413
        _Ver = "?";
414
    }
415
    else if (_Shape.getElementMapSize() == 0) {
416
        if (_Shape.Hasher)
417
            _Shape.Hasher->clear();
418
    }
419
    PropertyComplexGeoData::afterRestore();
420
}
421

422
// The following function is copied from OCCT BRepTools.cxx and modified
423
// to disable saving of triangulation
424
//
425

426
static Standard_Boolean  BRepTools_Write(const TopoDS_Shape& Sh, const Standard_CString File)
427
{
428
  std::ofstream os;
429
  OSD_OpenStream(os, File, std::ios::out);
430

431
  if (!os.rdbuf()->is_open())
432
      return Standard_False;
433

434
  Standard_Boolean isGood = (os.good() && !os.eof());
435
  if(!isGood)
436
    return isGood;
437

438
  // See TopTools_FormatVersion of OCCT 7.6
439
  enum {
440
      VERSION_1 = 1,
441
      VERSION_2 = 2,
442
      VERSION_3 = 3
443
  };
444

445
  BRepTools_ShapeSet SS(Standard_False);
446
  SS.SetFormatNb(VERSION_1);
447
  // SS.SetProgress(PR);
448
  SS.Add(Sh);
449

450
  os << "DBRep_DrawableShape\n";  // for easy Draw read
451
  SS.Write(os);
452
  isGood = os.good();
453
  if(isGood )
454
    SS.Write(Sh,os);
455
  os.flush();
456
  isGood = os.good();
457

458
  errno = 0;
459
  os.close();
460
  isGood = os.good() && isGood && !errno;
461

462
  return isGood;
463
}
464

465
void PropertyPartShape::saveToFile(Base::Writer &writer) const
466
{
467
    // create a temporary file and copy the content to the zip stream
468
    // once the tmp. filename is known use always the same because otherwise
469
    // we may run into some problems on the Linux platform
470
    static Base::FileInfo fi(App::Application::getTempFileName());
471

472
    TopoDS_Shape myShape = _Shape.getShape();
473
    if (!BRepTools_Write(myShape,static_cast<Standard_CString>(fi.filePath().c_str()))) {
474
        // Note: Do NOT throw an exception here because if the tmp. file could
475
        // not be created we should not abort.
476
        // We only print an error message but continue writing the next files to the
477
        // stream...
478
        App::PropertyContainer* father = this->getContainer();
479
        if (father && father->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
480
            App::DocumentObject* obj = static_cast<App::DocumentObject*>(father);
481
            Base::Console().Error("Shape of '%s' cannot be written to BRep file '%s'\n",
482
                obj->Label.getValue(),fi.filePath().c_str());
483
        }
484
        else {
485
            Base::Console().Error("Cannot save BRep file '%s'\n", fi.filePath().c_str());
486
        }
487

488
        std::stringstream ss;
489
        ss << "Cannot save BRep file '" << fi.filePath() << "'";
490
        writer.addError(ss.str());
491
    }
492

493
    Base::ifstream file(fi, std::ios::in | std::ios::binary);
494
    if (file) {
495
        std::streambuf* buf = file.rdbuf();
496
        writer.Stream() << buf;
497
    }
498

499
    file.close();
500
    // remove temp file
501
    fi.deleteFile();
502
}
503

504
void PropertyPartShape::loadFromFile(Base::Reader &reader)
505
{
506
    BRep_Builder builder;
507
    // create a temporary file and copy the content from the zip stream
508
    Base::FileInfo fi(App::Application::getTempFileName());
509

510
    // read in the ASCII file and write back to the file stream
511
    Base::ofstream file(fi, std::ios::out | std::ios::binary);
512
    unsigned long ulSize = 0;
513
    if (reader) {
514
        std::streambuf* buf = file.rdbuf();
515
        reader >> buf;
516
        file.flush();
517
        ulSize = buf->pubseekoff(0, std::ios::cur, std::ios::in);
518
    }
519
    file.close();
520

521
    // Read the shape from the temp file, if the file is empty the stored shape was already empty.
522
    // If it's still empty after reading the (non-empty) file there must occurred an error.
523
    TopoDS_Shape shape;
524
    if (ulSize > 0) {
525
        if (!BRepTools::Read(shape, static_cast<Standard_CString>(fi.filePath().c_str()), builder)) {
526
            // Note: Do NOT throw an exception here because if the tmp. created file could
527
            // not be read it's NOT an indication for an invalid input stream 'reader'.
528
            // We only print an error message but continue reading the next files from the
529
            // stream...
530
            App::PropertyContainer* father = this->getContainer();
531
            if (father && father->isDerivedFrom(App::DocumentObject::getClassTypeId())) {
532
                App::DocumentObject* obj = static_cast<App::DocumentObject*>(father);
533
                Base::Console().Error("BRep file '%s' with shape of '%s' seems to be empty\n",
534
                    fi.filePath().c_str(),obj->Label.getValue());
535
            }
536
            else {
537
                Base::Console().Warning("Loaded BRep file '%s' seems to be empty\n", fi.filePath().c_str());
538
            }
539
        }
540
    }
541

542
    // delete the temp file
543
    fi.deleteFile();
544
    setValue(shape);
545
}
546

547
void PropertyPartShape::loadFromStream(Base::Reader &reader)
548
{
549
    try {
550
        reader.exceptions(std::istream::failbit | std::istream::badbit);
551
        BRep_Builder builder;
552
        TopoDS_Shape shape;
553
        BRepTools::Read(shape, reader, builder);
554
        setValue(shape);
555
    }
556
    catch (const std::exception&) {
557
        if (!reader.eof())
558
            Base::Console().Warning("Failed to load BRep file %s\n", reader.getFileName().c_str());
559
    }
560
}
561

562
void PropertyPartShape::SaveDocFile (Base::Writer &writer) const
563
{
564
    // If the shape is empty we simply store nothing. The file size will be 0 which
565
    // can be checked when reading in the data.
566
    if (_Shape.getShape().IsNull())
567
        return;
568
    TopoDS_Shape myShape = _Shape.getShape();
569
    if (writer.getMode("BinaryBrep")) {
570
        TopoShape shape;
571
        shape.setShape(myShape);
572
        shape.exportBinary(writer.Stream());
573
    }
574
    else {
575
        bool direct = App::GetApplication().GetParameterGroupByPath
576
            ("User parameter:BaseApp/Preferences/Mod/Part/General")->GetBool("DirectAccess", true);
577
        if (!direct) {
578
            saveToFile(writer);
579
        }
580
        else {
581
            TopoShape shape;
582
            shape.setShape(myShape);
583
            shape.exportBrep(writer.Stream());
584
        }
585
    }
586
}
587

588
void PropertyPartShape::RestoreDocFile(Base::Reader &reader)
589
{
590
    Base::FileInfo brep(reader.getFileName());
591
    if (brep.hasExtension("bin")) {
592
        TopoShape shape;
593
        shape.importBinary(reader);
594
        setValue(shape);
595
    }
596
    else {
597
        bool direct = App::GetApplication().GetParameterGroupByPath
598
            ("User parameter:BaseApp/Preferences/Mod/Part/General")->GetBool("DirectAccess", true);
599
        if (!direct) {
600
            loadFromFile(reader);
601
        }
602
        else {
603
            auto iostate = reader.exceptions();
604
            loadFromStream(reader);
605
            reader.exceptions(iostate);
606
        }
607
    }
608
}
609

610
// -------------------------------------------------------------------------
611

612
ShapeHistory::ShapeHistory(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type,
613
                           const TopoDS_Shape& newS, const TopoDS_Shape& oldS)
614
{
615
    reset(mkShape,type,newS,oldS);
616
}
617

618
void ShapeHistory::reset(BRepBuilderAPI_MakeShape& mkShape, TopAbs_ShapeEnum type,
619
                         const TopoDS_Shape& newS, const TopoDS_Shape& oldS)
620
{
621
    shapeMap.clear();
622
    this->type = type;
623

624
    TopTools_IndexedMapOfShape newM, oldM;
625
    TopExp::MapShapes(newS, type, newM); // map containing all old objects of type "type"
626
    TopExp::MapShapes(oldS, type, oldM); // map containing all new objects of type "type"
627

628
    // Look at all objects in the old shape and try to find the modified object in the new shape
629
    for (int i=1; i<=oldM.Extent(); i++) {
630
        bool found = false;
631
        TopTools_ListIteratorOfListOfShape it;
632
        // Find all new objects that are a modification of the old object (e.g. a face was resized)
633
        for (it.Initialize(mkShape.Modified(oldM(i))); it.More(); it.Next()) {
634
            found = true;
635
            for (int j=1; j<=newM.Extent(); j++) { // one old object might create several new ones!
636
                if (newM(j).IsPartner(it.Value())) {
637
                    shapeMap[i-1].push_back(j-1); // adjust indices to start at zero
638
                    break;
639
                }
640
            }
641
        }
642

643
        // Find all new objects that were generated from an old object (e.g. a face generated from an edge)
644
        for (it.Initialize(mkShape.Generated(oldM(i))); it.More(); it.Next()) {
645
            found = true;
646
            for (int j=1; j<=newM.Extent(); j++) {
647
                if (newM(j).IsPartner(it.Value())) {
648
                    shapeMap[i-1].push_back(j-1);
649
                    break;
650
                }
651
            }
652
        }
653

654
        if (!found) {
655
            // Find all old objects that don't exist any more (e.g. a face was completely cut away)
656
            if (mkShape.IsDeleted(oldM(i))) {
657
                shapeMap[i-1] = std::vector<int>();
658
            }
659
            else {
660
                // Mop up the rest (will this ever be reached?)
661
                for (int j=1; j<=newM.Extent(); j++) {
662
                    if (newM(j).IsPartner(oldM(i))) {
663
                        shapeMap[i-1].push_back(j-1);
664
                        break;
665
                    }
666
                }
667
            }
668
        }
669
    }
670
}
671

672
void ShapeHistory::join(const ShapeHistory& newH)
673
{
674
    ShapeHistory join;
675

676
    for (ShapeHistory::MapList::const_iterator it = shapeMap.begin(); it != shapeMap.end(); ++it) {
677
        int old_shape_index = it->first;
678
        if (it->second.empty())
679
            join.shapeMap[old_shape_index] = ShapeHistory::List();
680
        for (ShapeHistory::List::const_iterator jt = it->second.begin(); jt != it->second.end(); ++jt) {
681
            ShapeHistory::MapList::const_iterator kt = newH.shapeMap.find(*jt);
682
            if (kt != newH.shapeMap.end()) {
683
                ShapeHistory::List& ary = join.shapeMap[old_shape_index];
684
                ary.insert(ary.end(), kt->second.begin(), kt->second.end());
685
            }
686
        }
687
    }
688

689
    shapeMap.swap(join.shapeMap);
690
}
691

692
// -------------------------------------------------------------------------
693

694
TYPESYSTEM_SOURCE(Part::PropertyShapeHistory , App::PropertyLists)
695

696
PropertyShapeHistory::PropertyShapeHistory() = default;
697

698
PropertyShapeHistory::~PropertyShapeHistory() = default;
699

700
void PropertyShapeHistory::setValue(const ShapeHistory& sh)
701
{
702
    aboutToSetValue();
703
    _lValueList.resize(1);
704
    _lValueList[0] = sh;
705
    hasSetValue();
706
}
707

708
void PropertyShapeHistory::setValues(const std::vector<ShapeHistory>& values)
709
{
710
    aboutToSetValue();
711
    _lValueList = values;
712
    hasSetValue();
713
}
714

715
PyObject *PropertyShapeHistory::getPyObject()
716
{
717
    return Py::new_reference_to(Py::None());
718
}
719

720
void PropertyShapeHistory::setPyObject(PyObject *)
721
{
722
}
723

724
void PropertyShapeHistory::Save (Base::Writer &) const
725
{
726
}
727

728
void PropertyShapeHistory::Restore(Base::XMLReader &)
729
{
730
}
731

732
void PropertyShapeHistory::SaveDocFile (Base::Writer &) const
733
{
734
}
735

736
void PropertyShapeHistory::RestoreDocFile(Base::Reader &)
737
{
738
}
739

740
App::Property *PropertyShapeHistory::Copy() const
741
{
742
    PropertyShapeHistory *p= new PropertyShapeHistory();
743
    p->_lValueList = _lValueList;
744
    return p;
745
}
746

747
void PropertyShapeHistory::Paste(const Property &from)
748
{
749
    aboutToSetValue();
750
    _lValueList = dynamic_cast<const PropertyShapeHistory&>(from)._lValueList;
751
    hasSetValue();
752
}
753

754
// -------------------------------------------------------------------------
755

756
TYPESYSTEM_SOURCE(Part::PropertyFilletEdges , App::PropertyLists)
757

758
PropertyFilletEdges::PropertyFilletEdges() = default;
759

760
PropertyFilletEdges::~PropertyFilletEdges() = default;
761

762
void PropertyFilletEdges::setValue(int id, double r1, double r2)
763
{
764
    aboutToSetValue();
765
    _lValueList.resize(1);
766
    _lValueList[0].edgeid = id;
767
    _lValueList[0].radius1 = r1;
768
    _lValueList[0].radius2 = r2;
769
    hasSetValue();
770
}
771

772
void PropertyFilletEdges::setValues(const std::vector<FilletElement>& values)
773
{
774
    aboutToSetValue();
775
    _lValueList = values;
776
    hasSetValue();
777
}
778

779
PyObject *PropertyFilletEdges::getPyObject()
780
{
781
    Py::List list(getSize());
782
    std::vector<FilletElement>::const_iterator it;
783
    int index = 0;
784
    for (it = _lValueList.begin(); it != _lValueList.end(); ++it) {
785
        Py::Tuple ent(3);
786
        ent.setItem(0, Py::Long(it->edgeid));
787
        ent.setItem(1, Py::Float(it->radius1));
788
        ent.setItem(2, Py::Float(it->radius2));
789
        list[index++] = ent;
790
    }
791

792
    return Py::new_reference_to(list);
793
}
794

795
void PropertyFilletEdges::setPyObject(PyObject *value)
796
{
797
    Py::Sequence list(value);
798
    std::vector<FilletElement> values;
799
    values.reserve(list.size());
800
    for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
801
        FilletElement fe;
802
        Py::Tuple ent(*it);
803
        fe.edgeid = (int)Py::Long(ent.getItem(0));
804
        fe.radius1 = (double)Py::Float(ent.getItem(1));
805
        fe.radius2 = (double)Py::Float(ent.getItem(2));
806
        values.push_back(fe);
807
    }
808

809
    setValues(values);
810
}
811

812
void PropertyFilletEdges::Save (Base::Writer &writer) const
813
{
814
    if (!writer.isForceXML()) {
815
        writer.Stream() << writer.ind() << "<FilletEdges file=\"" << writer.addFile(getName(), this) << "\"/>" << std::endl;
816
    }
817
}
818

819
void PropertyFilletEdges::Restore(Base::XMLReader &reader)
820
{
821
    reader.readElement("FilletEdges");
822
    std::string file (reader.getAttribute("file") );
823

824
    if (!file.empty()) {
825
        // initiate a file read
826
        reader.addFile(file.c_str(),this);
827
    }
828
}
829

830
void PropertyFilletEdges::SaveDocFile (Base::Writer &writer) const
831
{
832
    Base::OutputStream str(writer.Stream());
833
    uint32_t uCt = (uint32_t)getSize();
834
    str << uCt;
835
    for (const auto & it : _lValueList) {
836
        str << it.edgeid << it.radius1 << it.radius2;
837
    }
838
}
839

840
void PropertyFilletEdges::RestoreDocFile(Base::Reader &reader)
841
{
842
    Base::InputStream str(reader);
843
    uint32_t uCt=0;
844
    str >> uCt;
845
    std::vector<FilletElement> values(uCt);
846
    for (auto & it : values) {
847
        str >> it.edgeid >> it.radius1 >> it.radius2;
848
    }
849
    setValues(values);
850
}
851

852
App::Property *PropertyFilletEdges::Copy() const
853
{
854
    PropertyFilletEdges *p= new PropertyFilletEdges();
855
    p->_lValueList = _lValueList;
856
    return p;
857
}
858

859
void PropertyFilletEdges::Paste(const Property &from)
860
{
861
    aboutToSetValue();
862
    _lValueList = dynamic_cast<const PropertyFilletEdges&>(from)._lValueList;
863
    hasSetValue();
864
}
865

866
// -------------------------------------------------------------------------
867

868
TYPESYSTEM_SOURCE(Part::PropertyShapeCache, App::Property);
869

870
App::Property *PropertyShapeCache::Copy(void) const {
871
    return new PropertyShapeCache();
872
}
873

874
void PropertyShapeCache::Paste(const App::Property &) {
875
    cache.clear();
876
}
877

878
void PropertyShapeCache::Save (Base::Writer &) const
879
{
880
}
881

882
void PropertyShapeCache::Restore(Base::XMLReader &)
883
{
884
}
885

886
/**
887
 * Make a new python List with a tuple for each cache entry containing the key and the shape
888
 * @return the python list
889
 */
890
PyObject *PropertyShapeCache::getPyObject() {
891
    Py::List res;
892
    for(auto &v : cache)
893
        res.append(Py::TupleN(Py::String(v.first),shape2pyshape(v.second)));
894
    return Py::new_reference_to(res);
895
}
896

897
/**
898
 * Remove the cache entries for every element in the list
899
 * @param value A python list of entry names
900
 */
901
void PropertyShapeCache::setPyObject(PyObject *value) {
902
    if(!value)
903
        return;
904
    if(value == Py_None) {
905
        cache.clear();
906
        return;
907
    }
908
    App::PropertyStringList prop;
909
    prop.setPyObject(value);
910
    for(const auto &sub : prop.getValues())
911
        cache.erase(sub);
912
}
913

914
#define SHAPE_CACHE_NAME "_Part_ShapeCache"
915
/**
916
 * Find or create the shape cache for a document object
917
 * @param obj The document object
918
 * @param create True if we should create the cache if it doesn't exist
919
 * @return The shape cache, or null if we aren't creating and it doesn't exist
920
 */
921
PropertyShapeCache *PropertyShapeCache::get(const App::DocumentObject *obj, bool create) {
922
    auto prop = Base::freecad_dynamic_cast<PropertyShapeCache>(
923
        obj->getDynamicPropertyByName(SHAPE_CACHE_NAME));
924
    if(prop && prop->getContainer()==obj)
925
        return prop;
926
    if(!create)
927
        return 0;
928

929
    prop = static_cast<PropertyShapeCache*>(
930
        const_cast<App::DocumentObject*>(obj)->addDynamicProperty("Part::PropertyShapeCache",
931
                                                                  SHAPE_CACHE_NAME,"Part","Shape cache",
932
                                                                  App::Prop_NoPersist|App::Prop_Output|App::Prop_Hidden));
933
    if(!prop)
934
        FC_ERR("Failed to add shape cache for " << obj->getFullName());
935
    else
936
        prop->connChanged = const_cast<App::DocumentObject*>(obj)->signalEarlyChanged.connect(
937
            std::bind(&PropertyShapeCache::slotChanged,prop,sp::_1,sp::_2));
938
    return prop;
939
}
940

941
/**
942
 * Look up and return a shape in the cache
943
 * @param obj The document object to look in
944
 * @param shape The found shape is returned here
945
 * @param subname The key to look up
946
 * @return True if the name was found
947
 */
948
bool PropertyShapeCache::getShape(const App::DocumentObject *obj, TopoShape &shape, const char *subname) {
949
// March, 2024 Toponaming project:  There was originally a feature to disable shape cache
950
// that has not been kept:
951
//    if (PartParams::getDisableShapeCache())
952
//        return false;
953
    auto prop = get(obj,false);
954
    if(!prop)
955
        return false;
956
    if(!subname) subname = "";
957
    auto it = prop->cache.find(subname);
958
    if(it!=prop->cache.end()) {
959
        shape = it->second;
960
        return !shape.isNull();
961
    }
962
    return false;
963
}
964

965
/**
966
 * Find or create the property shape cache in a document object and then add an entry
967
 * @param obj The Object
968
 * @param shape The shape to cache
969
 * @param subname The key to point at that shape
970
 */
971
void PropertyShapeCache::setShape(
972
    const App::DocumentObject *obj, const TopoShape &shape, const char *subname)
973
{
974
// March, 2024 Toponaming project:  There was originally a feature to disable shape cache
975
// that has not been kept:
976
//    if (PartParams::getDisableShapeCache())
977
//        return;
978
    auto prop = get(obj,true);
979
    if(!prop)
980
        return;
981
    if(!subname) subname = "";
982
    prop->cache[subname] = shape;
983
}
984

985
void PropertyShapeCache::slotChanged(const App::DocumentObject &, const App::Property &prop) {
986
    auto propName = prop.getName();
987
    if(!propName) return;
988
    if(strcmp(propName,"Group")==0 ||
989
        strcmp(propName,"Shape")==0 ||
990
        strstr(propName,"Touched")!=0)
991
    {
992
        FC_LOG("clear shape cache on changed " << prop.getFullName());
993
        cache.clear();
994
    }
995
}
996

997

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

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

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

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