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