1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
7 ** Copyright (C) 2016 ravas (github.com/r-a-v-a-s)
8 **
9 ** This file may be distributed and/or modified under the terms of the
10 ** GNU General Public License version 2 as published by the Free Software
11 ** Foundation and appearing in the file gpl-2.0.txt included in the
12 ** packaging of this file.
13 **
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; if not, write to the Free Software
21 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 **
23 ** This copyright notice MUST APPEAR in all copies of the script!
24 **
25 **********************************************************************/
26
27 #include <iostream>
28 #include <cmath>
29 #include <QDir>
30 //#include <QDebug>
31
32 #include "rs_graphic.h"
33 #include "rs_dialogfactory.h"
34
35 #include "rs_debug.h"
36 #include "rs_fileio.h"
37 #include "rs_math.h"
38 #include "rs_units.h"
39 #include "rs_settings.h"
40 #include "rs_layer.h"
41 #include "rs_block.h"
42
43
44 /**
45 * Default constructor.
46 */
RS_Graphic(RS_EntityContainer * parent)47 RS_Graphic::RS_Graphic(RS_EntityContainer* parent)
48 : RS_Document(parent),
49 layerList(),
50 blockList(true),
51 paperScaleFixed(false),
52 marginLeft(0.0),
53 marginTop(0.0),
54 marginRight(0.0),
55 marginBottom(0.0),
56 pagesNumH(1),
57 pagesNumV(1)
58 {
59
60 RS_SETTINGS->beginGroup("/Defaults");
61 setUnit(RS_Units::stringToUnit(RS_SETTINGS->readEntry("/Unit", "None")));
62 RS_SETTINGS->endGroup();
63 RS_SETTINGS->beginGroup("/Appearance");
64 //$ISOMETRICGRID == $SNAPSTYLE
65 addVariable("$SNAPSTYLE",static_cast<int>(RS_SETTINGS->readNumEntry("/IsometricGrid", 0)),70);
66 crosshairType=static_cast<RS2::CrosshairType>(RS_SETTINGS->readNumEntry("/CrosshairType",0));
67 RS_SETTINGS->endGroup();
68 RS2::Unit unit = getUnit();
69
70 if (unit==RS2::Inch) {
71 addVariable("$DIMASZ", 0.1, 40);
72 addVariable("$DIMEXE", 0.05, 40);
73 addVariable("$DIMEXO", 0.025, 40);
74 addVariable("$DIMGAP", 0.025, 40);
75 addVariable("$DIMTXT", 0.1, 40);
76 } else {
77 addVariable("$DIMASZ",
78 RS_Units::convert(2.5, RS2::Millimeter, unit), 40);
79 addVariable("$DIMEXE",
80 RS_Units::convert(1.25, RS2::Millimeter, unit), 40);
81 addVariable("$DIMEXO",
82 RS_Units::convert(0.625, RS2::Millimeter, unit), 40);
83 addVariable("$DIMGAP",
84 RS_Units::convert(0.625, RS2::Millimeter, unit), 40);
85 addVariable("$DIMTXT",
86 RS_Units::convert(2.5, RS2::Millimeter, unit), 40);
87 }
88 addVariable("$DIMTIH", 0, 70);
89 //initialize printer vars bug #3602444
90 setPaperScale(getPaperScale());
91 setPaperInsertionBase(getPaperInsertionBase());
92
93 setModified(false);
94 }
95
96
97
98 /**
99 * Destructor.
100 */
101 RS_Graphic::~RS_Graphic() = default;
102
103
104
105 /**
106 * Counts the entities on the given layer.
107 */
countLayerEntities(RS_Layer * layer)108 unsigned long int RS_Graphic::countLayerEntities(RS_Layer* layer) {
109
110 int c=0;
111
112 if (layer) {
113 for(auto t: entities){
114
115 if (t->getLayer() &&
116 t->getLayer()->getName()==layer->getName()) {
117 c+=t->countDeep();
118 }
119 }
120 }
121
122 return c;
123 }
124
125
126
127 /**
128 * Removes the given layer and undoes all entities on it.
129 */
removeLayer(RS_Layer * layer)130 void RS_Graphic::removeLayer(RS_Layer* layer) {
131
132 if (layer && layer->getName()!="0") {
133
134 std::vector<RS_Entity*> toRemove;
135 //find entities on layer
136 for(auto e: entities){
137 if (e->getLayer() &&
138 e->getLayer()->getName()==layer->getName()) {
139 toRemove.push_back(e);
140 }
141 }
142 // remove all entities on that layer:
143 if(toRemove.size()){
144 startUndoCycle();
145 for(auto e: toRemove){
146 e->setUndoState(true);
147 e->setLayer("0");
148 addUndoable(e);
149 }
150 endUndoCycle();
151 }
152
153 toRemove.clear();
154 // remove all entities in blocks that are on that layer:
155 for(RS_Block* blk: blockList){
156 if(!blk) continue;
157 for(auto e: *blk){
158
159 if (e->getLayer() &&
160 e->getLayer()->getName()==layer->getName()) {
161 toRemove.push_back(e);
162 }
163 }
164 }
165
166 for(auto e: toRemove){
167 e->setUndoState(true);
168 e->setLayer("0");
169 }
170
171 layerList.remove(layer);
172 }
173 }
174
175
176 /**
177 * Clears all layers, blocks and entities of this graphic.
178 * A default layer (0) is created.
179 */
newDoc()180 void RS_Graphic::newDoc() {
181
182 RS_DEBUG->print("RS_Graphic::newDoc");
183
184 clear();
185
186 clearLayers();
187 clearBlocks();
188
189 addLayer(new RS_Layer("0"));
190 //addLayer(new RS_Layer("ByBlock"));
191
192 setModified(false);
193 }
194
195
196
197 /*
198 * Description: Create/update the drawing backup file, if necessary.
199 * Author(s): Claude Sylvain
200 * Created: 13 July 2011
201 * Last modified:
202 *
203 * Parameters: const QString &filename:
204 * Name of the drawing file to backup.
205 *
206 * Returns: bool:
207 * false : Operation failed.
208 * true : Operation successful.
209 */
210
BackupDrawingFile(const QString & filename)211 bool RS_Graphic::BackupDrawingFile(const QString &filename)
212 {
213 static const char *msg_err =
214 "RS_Graphic::BackupDrawingFile: Can't create object!";
215
216 bool ret = false; /* Operation failed, by default. */
217
218
219 /* - Create backup only if drawing file name exist.
220 * - Remark: Not really necessary to check if the drawing file
221 * name have been defined.
222 * ----------------------------------------------------------- */
223 if (filename.length() > 0)
224 {
225 /* Built Backup File Name.
226 * */
227 QString *qs_backup_fn = new QString(filename + '~');
228
229 /* Create "Drawing File" object.
230 * */
231 QFile *qf_df = new QFile(filename);
232
233 /* If able to create the objects, process...
234 * ----------------------------------------- */
235 if ((qs_backup_fn != NULL) && (qf_df != NULL))
236 {
237 /* Create backup file only if drawing file already exist.
238 * ------------------------------------------------------ */
239 if (qf_df->exists() == true)
240 {
241 /* Create "Drawing File Backup" object.
242 * */
243 QFile *qf_dfb = new QFile(*qs_backup_fn);
244
245 /* If able to create the object, process...
246 * ---------------------------------------- */
247 if (qf_dfb != NULL)
248 {
249 /* If a backup file already exist, remove it!
250 * ------------------------------------------ */
251 if (qf_dfb->exists() == true)
252 qf_dfb->remove();
253
254 qf_df->copy(*qs_backup_fn); /* Create backup file. */
255 ret = true; /* Operation successful. */
256 delete qf_dfb;
257 }
258 /* Can't create object.
259 * -------------------- */
260 else
261 {
262 RS_DEBUG->print("%s", msg_err);
263 }
264 }
265
266 }
267 /* Can't create object(s).
268 * ----------------------- */
269 else
270 {
271 RS_DEBUG->print("%s", msg_err);
272 }
273
274 delete qs_backup_fn;
275 delete qf_df;
276 }
277
278 return ret;
279 }
280
281
282
283 /*
284 * Description: Saves this graphic with the current filename and settings.
285 * Author(s): ..., Claude Sylvain
286 * Last modified: 13 July 2011
287 * Parameters:
288 *
289 * Returns: bool:
290 * false: Operation failed.
291 * true: Operation successful.
292 *
293 * Notes: - If this is not an AutoSave, backup the drawing file
294 * (if necessary).
295 * - Drawing is saved only when it has been modified.
296 * This prevent lost of backup file when file
297 * is saved more than one time without being modified.
298 */
299
save(bool isAutoSave)300 bool RS_Graphic::save(bool isAutoSave)
301 {
302 bool ret = false;
303
304 RS_DEBUG->print("RS_Graphic::save: Entering...");
305
306 /* - Save drawing file only if it has been modified.
307 * - Notes: Potentially dangerous in case of an internal
308 * coding error that make LibreCAD not aware of modification
309 * when some kind of drawing modification is done.
310 * ----------------------------------------------------------- */
311 if (isModified())
312 {
313 QString actualName;
314 RS2::FormatType actualType;
315
316 actualType = formatType;
317
318 if (isAutoSave)
319 {
320 actualName = autosaveFilename;
321
322 if (formatType == RS2::FormatUnknown)
323 actualType = RS2::FormatDXFRW;
324 } else {
325 // - This is not an AutoSave operation. This is a manual
326 // save operation. So, ...
327 // - Set working file name to the drawing file name.
328 // - Backup drawing file (if necessary).
329 // ------------------------------------------------------
330 QFileInfo finfo(filename);
331 QDateTime m=finfo.lastModified();
332 //bug#3414993
333 //modifiedTime should only be used for the same filename
334 // DEBUG_HEADER
335 // qDebug()<<"currentFileName= "<<currentFileName;
336 // qDebug()<<"Checking file: filename= "<<filename;
337 // qDebug()<<"Checking file: "<<filename;
338 // qDebug()<<"modifiedTime.isValid()="<<modifiedTime.isValid();
339 // qDebug()<<"Previous timestamp: "<<modifiedTime;
340 // qDebug()<<"Current timestamp: "<<m;
341 if ( currentFileName == QString(filename)
342 && modifiedTime.isValid() && m != modifiedTime ) {
343 //file modified by others
344 // qDebug()<<"detected on disk change";
345 RS_DIALOGFACTORY->commandMessage(QObject::tr("File on disk modified. Please save to another file to avoid data loss! File modified: %1").arg(filename));
346 return false;
347 }
348
349 actualName = filename;
350 if (RS_SETTINGS->readNumEntry("/AutoBackupDocument", 1)!=0)
351 BackupDrawingFile(filename);
352 }
353
354 /* Save drawing file if able to created associated object.
355 * ------------------------------------------------------- */
356 if (!actualName.isEmpty())
357 {
358 RS_DEBUG->print("RS_Graphic::save: File: %s", actualName.toLatin1().data());
359 RS_DEBUG->print("RS_Graphic::save: Format: %d", (int) actualType);
360 RS_DEBUG->print("RS_Graphic::save: Export...");
361
362 ret = RS_FileIO::instance()->fileExport(*this, actualName, actualType);
363 QFileInfo finfo(actualName);
364 modifiedTime=finfo.lastModified();
365 currentFileName=actualName;
366 } else {
367 RS_DEBUG->print("RS_Graphic::save: Can't create object!");
368 RS_DEBUG->print("RS_Graphic::save: File not saved!");
369 }
370
371 /* Remove AutoSave file after user has successfully saved file.
372 * ------------------------------------------------------------ */
373 if (ret && !isAutoSave)
374 {
375 /* Autosave file object.
376 * */
377 QFile qf_file(autosaveFilename);
378
379 /* Tell that drawing file is no more modified.
380 * ------------------------------------------- */
381 setModified(false);
382 layerList.setModified(false);
383 blockList.setModified(false);
384
385 /* - Remove autosave file, if able to create associated object,
386 * and if autosave file exist.
387 * ------------------------------------------------------------ */
388 if (qf_file.exists())
389 {
390 RS_DEBUG->print( "RS_Graphic::save: Removing old autosave file %s",
391 autosaveFilename.toLatin1().data());
392 qf_file.remove();
393 }
394
395 }
396
397 RS_DEBUG->print("RS_Graphic::save: Done!");
398 } else {
399 RS_DEBUG->print("RS_Graphic::save: File not modified, not saved");
400 ret = true;
401 }
402
403 RS_DEBUG->print("RS_Graphic::save: Exiting...");
404
405 return ret;
406 }
407
408
409
410 /*
411 * Description: - Saves this graphic with the given filename and current
412 * settings.
413 *
414 * Author(s): ..., Claude Sylvain
415 * Created: ?
416 * Last modified: 13 July 2011
417 * Parameters: QString: name to save
418 * RS2::FormatType: format to save
419 * bool:
420 * false: do not save if not needed
421 * true: force to save (when called for save as...
422 *
423 * Returns: bool:
424 * false: Operation failed.
425 * true: Operation successful.
426 *
427 * Notes: Backup the drawing file (if necessary).
428 */
429
saveAs(const QString & filename,RS2::FormatType type,bool force)430 bool RS_Graphic::saveAs(const QString &filename, RS2::FormatType type, bool force)
431 {
432 RS_DEBUG->print("RS_Graphic::saveAs: Entering...");
433
434 // Set to "failed" by default.
435 bool ret = false;
436
437 // Check/memorize if file name we want to use as new file
438 // name is the same as the actual file name.
439 bool fn_is_same = filename == this->filename;
440 auto const filenameSaved=this->filename;
441 auto const autosaveFilenameSaved=this->autosaveFilename;
442 auto const formatTypeSaved=this->formatType;
443
444 this->filename = filename;
445 this->formatType = type;
446
447 // QString const oldAutosaveName = this->autosaveFilename;
448 QFileInfo finfo(filename);
449
450 // Construct new autosave filename by prepending # to the filename
451 // part, using the same directory as the destination file.
452 this->autosaveFilename = finfo.path() + "/#" + finfo.fileName();
453
454 // When drawing is saved using a different name than the actual
455 // drawing file name, make LibreCAD think that drawing file
456 // has been modified, to make sure the drawing file saved.
457 if (!fn_is_same || force)
458 setModified(true);
459
460 ret = save(); // Save file.
461
462 if (ret) {
463 // Save was successful, remove old autosave file.
464 QFile qf_file(autosaveFilenameSaved);
465
466 if (qf_file.exists()) {
467 RS_DEBUG->print("RS_Graphic::saveAs: Removing old autosave file %s",
468 autosaveFilenameSaved.toLatin1().data());
469 qf_file.remove();
470 }
471
472 }else{
473 //do not modify filenames:
474 this->filename=filenameSaved;
475 this->autosaveFilename=autosaveFilenameSaved;
476 this->formatType=formatTypeSaved;
477 }
478
479 return ret;
480 }
481
482
483 /**
484 * Loads the given file into this graphic.
485 */
loadTemplate(const QString & filename,RS2::FormatType type)486 bool RS_Graphic::loadTemplate(const QString &filename, RS2::FormatType type) {
487 RS_DEBUG->print("RS_Graphic::loadTemplate(%s)", filename.toLatin1().data());
488
489 bool ret = false;
490
491 // Construct new autosave filename by prepending # to the filename part,
492 // using system temporary dir.
493 this->autosaveFilename = QDir::tempPath () + "/#" + "Unnamed.dxf";
494
495 // clean all:
496 newDoc();
497
498 // import template file:
499 ret = RS_FileIO::instance()->fileImport(*this, filename, type);
500
501 setModified(false);
502 layerList.setModified(false);
503 blockList.setModified(false);
504 QFileInfo finfo;
505 modifiedTime = finfo.lastModified();
506
507 RS_DEBUG->print("RS_Graphic::loadTemplate(%s): OK", filename.toLatin1().data());
508
509 return ret;
510 }
511
512 /**
513 * Loads the given file into this graphic.
514 */
open(const QString & filename,RS2::FormatType type)515 bool RS_Graphic::open(const QString &filename, RS2::FormatType type) {
516 RS_DEBUG->print("RS_Graphic::open(%s)", filename.toLatin1().data());
517
518 bool ret = false;
519
520 this->filename = filename;
521 QFileInfo finfo(filename);
522 // Construct new autosave filename by prepending # to the filename
523 // part, using the same directory as the destination file.
524 this->autosaveFilename = finfo.path() + "/#" + finfo.fileName();
525
526 // clean all:
527 newDoc();
528
529 // import file:
530 ret = RS_FileIO::instance()->fileImport(*this, filename, type);
531
532 if( ret) {
533 setModified(false);
534 layerList.setModified(false);
535 blockList.setModified(false);
536 modifiedTime = finfo.lastModified();
537 currentFileName=QString(filename);
538
539 //cout << *((RS_Graphic*)graphic);
540 //calculateBorders();
541
542 RS_DEBUG->print("RS_Graphic::open(%s): OK", filename.toLatin1().data());
543 }
544
545 return ret;
546 }
547
548
549
550 /**
551 * @return true if the grid is switched on (visible).
552 */
isGridOn()553 bool RS_Graphic::isGridOn() {
554 int on = getVariableInt("$GRIDMODE", 1);
555 return on!=0;
556 }
557
558
559
560 /**
561 * Enables / disables the grid.
562 */
setGridOn(bool on)563 void RS_Graphic::setGridOn(bool on) {
564 addVariable("$GRIDMODE", (int)on, 70);
565 }
566
567 /**
568 * @return true if the isometric grid is switched on (visible).
569 */
isIsometricGrid()570 bool RS_Graphic::isIsometricGrid() {
571 //$ISOMETRICGRID == $SNAPSTYLE
572 int on = getVariableInt("$SNAPSTYLE", 0);
573 return on!=0;
574 }
575
576
577
578 /**
579 * Enables / disables isometric grid.
580 */
setIsometricGrid(bool on)581 void RS_Graphic::setIsometricGrid(bool on) {
582 //$ISOMETRICGRID == $SNAPSTYLE
583 addVariable("$SNAPSTYLE", (int)on, 70);
584 }
585
setCrosshairType(RS2::CrosshairType chType)586 void RS_Graphic::setCrosshairType(RS2::CrosshairType chType){
587 crosshairType=chType;
588 }
589
getCrosshairType()590 RS2::CrosshairType RS_Graphic::getCrosshairType(){
591 return crosshairType;
592 }
593
594 /**
595 * Sets the unit of this graphic to 'u'
596 */
setUnit(RS2::Unit u)597 void RS_Graphic::setUnit(RS2::Unit u) {
598
599 setPaperSize(RS_Units::convert(getPaperSize(), getUnit(), u));
600
601 addVariable("$INSUNITS", (int)u, 70);
602
603 //unit = u;
604 }
605
606
607
608 /**
609 * Gets the unit of this graphic
610 */
getUnit()611 RS2::Unit RS_Graphic::getUnit() {
612 return (RS2::Unit)getVariableInt("$INSUNITS", 0);
613 //return unit;
614 }
615
616
617
618 /**
619 * @return The linear format type for this document.
620 * This is determined by the variable "$LUNITS".
621 */
getLinearFormat()622 RS2::LinearFormat RS_Graphic::getLinearFormat() {
623 int lunits = getVariableInt("$LUNITS", 2);
624 return getLinearFormat(lunits);
625 /* changed by RS2::LinearFormat getLinearFormat(int f)
626 switch (lunits) {
627 default:
628 case 2:
629 return RS2::Decimal;
630 break;
631
632 case 1:
633 return RS2::Scientific;
634 break;
635
636 case 3:
637 return RS2::Engineering;
638 break;
639
640 case 4:
641 return RS2::Architectural;
642 break;
643
644 case 5:
645 return RS2::Fractional;
646 break;
647 }
648
649 return RS2::Decimal;*/
650 }
651
652 /**
653 * @return The linear format type used by the variable "$LUNITS" & "$DIMLUNIT".
654 */
getLinearFormat(int f)655 RS2::LinearFormat RS_Graphic::getLinearFormat(int f){
656 switch (f) {
657 default:
658 case 2:
659 return RS2::Decimal;
660 break;
661
662 case 1:
663 return RS2::Scientific;
664 break;
665
666 case 3:
667 return RS2::Engineering;
668 break;
669
670 case 4:
671 return RS2::Architectural;
672 break;
673
674 case 5:
675 return RS2::Fractional;
676 break;
677
678 case 6:
679 return RS2::ArchitecturalMetric;
680 break;
681 }
682
683 return RS2::Decimal;
684 }
685
686
687 /**
688 * @return The linear precision for this document.
689 * This is determined by the variable "$LUPREC".
690 */
getLinearPrecision()691 int RS_Graphic::getLinearPrecision() {
692 return getVariableInt("$LUPREC", 4);
693 }
694
695
696
697 /**
698 * @return The angle format type for this document.
699 * This is determined by the variable "$AUNITS".
700 */
getAngleFormat()701 RS2::AngleFormat RS_Graphic::getAngleFormat() {
702 int aunits = getVariableInt("$AUNITS", 0);
703
704 switch (aunits) {
705 default:
706 case 0:
707 return RS2::DegreesDecimal;
708 break;
709
710 case 1:
711 return RS2::DegreesMinutesSeconds;
712 break;
713
714 case 2:
715 return RS2::Gradians;
716 break;
717
718 case 3:
719 return RS2::Radians;
720 break;
721
722 case 4:
723 return RS2::Surveyors;
724 break;
725 }
726
727 return RS2::DegreesDecimal;
728 }
729
730
731
732 /**
733 * @return The linear precision for this document.
734 * This is determined by the variable "$LUPREC".
735 */
getAnglePrecision()736 int RS_Graphic::getAnglePrecision() {
737 return getVariableInt("$AUPREC", 4);
738 }
739
740
741
742 /**
743 * @return The insertion point of the drawing into the paper space.
744 * This is the distance from the lower left paper edge to the zero
745 * point of the drawing. DXF: $PINSBASE.
746 */
getPaperInsertionBase()747 RS_Vector RS_Graphic::getPaperInsertionBase() {
748 return getVariableVector("$PINSBASE", RS_Vector(0.0,0.0));
749 }
750
751
752 /**
753 * Sets the PINSBASE variable.
754 */
setPaperInsertionBase(const RS_Vector & p)755 void RS_Graphic::setPaperInsertionBase(const RS_Vector& p) {
756 addVariable("$PINSBASE", p, 10);
757 }
758
759
760 /**
761 * @return Paper size in graphic units.
762 */
getPaperSize()763 RS_Vector RS_Graphic::getPaperSize() {
764 RS_SETTINGS->beginGroup("/Print");
765 bool okX,okY;
766 double sX = RS_SETTINGS->readEntry("/PaperSizeX", "0.0").toDouble(&okX);
767 double sY = RS_SETTINGS->readEntry("/PaperSizeY", "0.0").toDouble(&okY);
768 RS_SETTINGS->endGroup();
769 RS_Vector def ;
770 if(okX&&okY && sX>RS_TOLERANCE && sY>RS_TOLERANCE) {
771 def=RS_Units::convert(RS_Vector(sX,sY),
772 RS2::Millimeter, getUnit());
773 }else{
774 def= RS_Units::convert(RS_Vector(210.0,297.0),
775 RS2::Millimeter, getUnit());
776 }
777
778 RS_Vector v1 = getVariableVector("$PLIMMIN", RS_Vector(0.0,0.0));
779 RS_Vector v2 = getVariableVector("$PLIMMAX", def);
780
781 return v2-v1;
782 }
783
784
785 /**
786 * Sets a new paper size.
787 */
setPaperSize(const RS_Vector & s)788 void RS_Graphic::setPaperSize(const RS_Vector& s) {
789 addVariable("$PLIMMIN", RS_Vector(0.0,0.0), 10);
790 addVariable("$PLIMMAX", s, 10);
791 //set default paper size
792 RS_Vector def = RS_Units::convert(s,
793 getUnit(), RS2::Millimeter);
794 RS_SETTINGS->beginGroup("/Print");
795 RS_SETTINGS->writeEntry("/PaperSizeX", def.x);
796 RS_SETTINGS->writeEntry("/PaperSizeY", def.y);
797 RS_SETTINGS->endGroup();
798
799 }
800
801
802 /**
803 * @return Print Area size in graphic units.
804 */
getPrintAreaSize(bool total)805 RS_Vector RS_Graphic::getPrintAreaSize(bool total) {
806 RS_Vector printArea = getPaperSize();
807 printArea.x -= RS_Units::convert(marginLeft+marginRight, RS2::Millimeter, getUnit());
808 printArea.y -= RS_Units::convert(marginTop+marginBottom, RS2::Millimeter, getUnit());
809 if (total) {
810 printArea.x *= pagesNumH;
811 printArea.y *= pagesNumV;
812 }
813 return printArea;
814 }
815
816
817
818 /**
819 * @return Paper format.
820 * This is determined by the variables "$PLIMMIN" and "$PLIMMAX".
821 *
822 * @param landscape will be set to true for landscape and false for portrait if not NULL.
823 */
getPaperFormat(bool * landscape)824 RS2::PaperFormat RS_Graphic::getPaperFormat(bool* landscape) {
825 RS_Vector size = RS_Units::convert(getPaperSize(),
826 getUnit(), RS2::Millimeter);
827
828 if (landscape) {
829 *landscape = (size.x>size.y);
830 }
831
832 return RS_Units::paperSizeToFormat(size);
833 }
834
835
836
837 /**
838 * Sets the paper format to the given format.
839 */
setPaperFormat(RS2::PaperFormat f,bool landscape)840 void RS_Graphic::setPaperFormat(RS2::PaperFormat f, bool landscape) {
841 RS_Vector size = RS_Units::paperFormatToSize(f);
842
843 if (landscape ^ (size.x > size.y)) {
844 std::swap(size.x, size.y);
845 }
846
847 setPaperSize(RS_Units::convert(size, RS2::Millimeter, getUnit()));
848 }
849
850
851
852 /**
853 * @return Paper space scaling (DXF: $PSVPSCALE).
854 */
getPaperScale()855 double RS_Graphic::getPaperScale() {
856 double ret;
857
858 ret = getVariableDouble("$PSVPSCALE", 1.0);
859 // if (ret<1.0e-6) {
860 // ret = 1.0;
861 // }
862
863 return ret;
864 }
865
866
867
868 /**
869 * Sets a new scale factor for the paper space.
870 */
setPaperScale(double s)871 void RS_Graphic::setPaperScale(double s) {
872 if(paperScaleFixed==false) addVariable("$PSVPSCALE", s, 40);
873 }
874
875
876
877 /**
878 * Centers drawing on page. Affects DXF variable $PINSBASE.
879 */
centerToPage()880 void RS_Graphic::centerToPage() {
881 RS_Vector size = getPrintAreaSize();
882 double scale = getPaperScale();
883 auto s=getSize();
884 auto sMin=getMin();
885 /** avoid zero size, bug#3573158 */
886 if(fabs(s.x)<RS_TOLERANCE) {
887 s.x=10.;
888 sMin.x=-5.;
889 }
890 if(fabs(s.y)<RS_TOLERANCE) {
891 s.y=10.;
892 sMin.y=-5.;
893 }
894
895 RS_Vector pinsbase = (size-s*scale)/2.0 - sMin*scale;
896 pinsbase.x += RS_Units::convert(marginLeft, RS2::Millimeter, getUnit());
897 pinsbase.y += RS_Units::convert(marginBottom, RS2::Millimeter, getUnit());
898
899 setPaperInsertionBase(pinsbase);
900 }
901
902
903
904 /**
905 * Fits drawing on page. Affects DXF variable $PINSBASE.
906 */
fitToPage()907 bool RS_Graphic::fitToPage() {
908 bool ret(true);
909 RS_Vector ps = getPrintAreaSize();
910 RS_Vector s = getSize();
911 /** avoid zero size, bug#3573158 */
912 if(fabs(s.x)<RS_TOLERANCE) s.x=10.;
913 if(fabs(s.y)<RS_TOLERANCE) s.y=10.;
914 double fx = RS_MAXDOUBLE;
915 double fy = RS_MAXDOUBLE;
916 double fxy;
917 //ps = RS_Units::convert(ps, getUnit(), RS2::Millimeter);
918
919 // tin-pot 2011-12-30: TODO: can s.x < 0.0 (==> fx < 0.0) happen?
920 if (fabs(s.x) > RS_TOLERANCE) {
921 fx = ps.x / s.x;
922 // ret=false;
923 }
924 if (fabs(s.y) > RS_TOLERANCE) {
925 fy = ps.y / s.y;
926 // ret=false;
927 }
928
929 fxy = std::min(fx, fy);
930 if (fxy >= RS_MAXDOUBLE || fxy <= 1.0e-10) {
931 setPaperSize(
932 RS_Units::convert(RS_Vector(210.,297.)
933 , RS2::Millimeter
934 , getUnit()
935 )
936 );
937 ret=false;
938 }
939 setPaperScale(fxy);
940 centerToPage();
941 return ret;
942 }
943
944
isBiggerThanPaper()945 bool RS_Graphic::isBiggerThanPaper() {
946 RS_Vector ps = getPrintAreaSize();
947 RS_Vector s = getSize() * getPaperScale();
948 return !s.isInWindow(RS_Vector(0.0, 0.0), ps);
949 }
950
951
addEntity(RS_Entity * entity)952 void RS_Graphic::addEntity(RS_Entity* entity)
953 {
954 RS_EntityContainer::addEntity(entity);
955 if( entity->rtti() == RS2::EntityBlock ||
956 entity->rtti() == RS2::EntityContainer){
957 RS_EntityContainer* e=static_cast<RS_EntityContainer*>(entity);
958 for(auto e1: *e){
959 addEntity(e1);
960 }
961 }
962 }
963
964
965 /**
966 * Dumps the entities to stdout.
967 */
operator <<(std::ostream & os,RS_Graphic & g)968 std::ostream& operator << (std::ostream& os, RS_Graphic& g) {
969 os << "--- Graphic: \n";
970 os << "---" << *g.getLayerList() << "\n";
971 os << "---" << *g.getBlockList() << "\n";
972 os << "---" << (RS_Undo&)g << "\n";
973 os << "---" << (RS_EntityContainer&)g << "\n";
974
975 return os;
976 }
977
978 /**
979 * Removes invalid objects.
980 * @return how many objects were removed
981 */
clean()982 int RS_Graphic::clean()
983 {
984 // author: ravas
985
986 int how_many = 0;
987
988 foreach (RS_Entity* e, entities)
989 {
990 if (e->getMin().x > e->getMax().x
991 || e->getMin().y > e->getMax().y
992 || e->getMin().x > RS_MAXDOUBLE
993 || e->getMax().x > RS_MAXDOUBLE
994 || e->getMin().x < RS_MINDOUBLE
995 || e->getMax().x < RS_MINDOUBLE
996 || e->getMin().y > RS_MAXDOUBLE
997 || e->getMax().y > RS_MAXDOUBLE
998 || e->getMin().y < RS_MINDOUBLE
999 || e->getMax().y < RS_MINDOUBLE)
1000 {
1001 removeEntity(e);
1002 how_many += 1;
1003 }
1004 }
1005 return how_many;
1006 }
1007
1008 /**
1009 * Paper margins in graphic units
1010 */
setMarginsInUnits(double left,double top,double right,double bottom)1011 void RS_Graphic::setMarginsInUnits(double left, double top, double right, double bottom) {
1012 setMargins(
1013 RS_Units::convert(left, getUnit(), RS2::Millimeter),
1014 RS_Units::convert(top, getUnit(), RS2::Millimeter),
1015 RS_Units::convert(right, getUnit(), RS2::Millimeter),
1016 RS_Units::convert(bottom, getUnit(), RS2::Millimeter));
1017 }
getMarginLeftInUnits()1018 double RS_Graphic::getMarginLeftInUnits() {
1019 return RS_Units::convert(marginLeft, RS2::Millimeter, getUnit());
1020 }
getMarginTopInUnits()1021 double RS_Graphic::getMarginTopInUnits() {
1022 return RS_Units::convert(marginTop, RS2::Millimeter, getUnit());
1023 }
getMarginRightInUnits()1024 double RS_Graphic::getMarginRightInUnits() {
1025 return RS_Units::convert(marginRight, RS2::Millimeter, getUnit());
1026 }
getMarginBottomInUnits()1027 double RS_Graphic::getMarginBottomInUnits() {
1028 return RS_Units::convert(marginBottom, RS2::Millimeter, getUnit());
1029 }
1030
setPagesNum(int horiz,int vert)1031 void RS_Graphic::setPagesNum(int horiz, int vert) {
1032 if (horiz > 0)
1033 pagesNumH = horiz;
1034 if (vert > 0)
1035 pagesNumV = vert;
1036 }
setPagesNum(const QString & horizXvert)1037 void RS_Graphic::setPagesNum(const QString &horizXvert) {
1038 if (horizXvert.contains('x')) {
1039 bool ok1 = false;
1040 bool ok2 = false;
1041 int i = horizXvert.indexOf('x');
1042 int h = (int)RS_Math::eval(horizXvert.left(i), &ok1);
1043 int v = (int)RS_Math::eval(horizXvert.mid(i+1), &ok2);
1044 if (ok1 && ok2)
1045 setPagesNum(h, v);
1046 }
1047 }
1048