<commit_before>#include <BALL/VIEW/WIDGETS/SDWidget.h>

#include <BALL/VIEW/KERNEL/iconLoader.h>

#include <BALL/KERNEL/bond.h>
#include <BALL/KERNEL/PTE.h>
#include <BALL/STRUCTURE/geometricProperties.h>
#include <BALL/STRUCTURE/sdGenerator.h>

#include <QtGui/QPainter>
#include <QtGui/QStyle>
#include <QtGui/QStyleOptionFocusRect>
#include <QtGui/QAction>

#include <QtGui/QFileDialog>
#include <QtGui/QImageWriter>

#include <set>

namespace BALL
{
	namespace VIEW
	{
		const char* SDWidget::Option::SHOW_HYDROGENS = "sd_widget_show_hydrogens";
		const bool  SDWidget::Default::SHOW_HYDROGENS = false;

		SDWidget::SDWidget(QWidget *parent, bool show_hydrogens)
			: QWidget(parent)
		{
			setup_();

			options[SDWidget::Option::SHOW_HYDROGENS] = show_hydrogens;
		}

		SDWidget::SDWidget(const System& system, QWidget *parent)
			: QWidget(parent)
		{
			setup_();

			plot(system);
		}

		void SDWidget::setup_()
		{
			setDefaultOptions();
			setBackgroundRole(QPalette::Base);

			//Todo: Add a nice icon
			QAction* export_image = new QAction(tr("Export image"), this);

			export_image->setIcon(IconLoader::instance().getIcon("actions/document-save"));

			addAction(export_image);
			connect(export_image, SIGNAL(triggered()), this, SLOT(exportImage_()));
		}

		SDWidget::~SDWidget()
		{}
		
		void SDWidget::plot(const System& system, bool create_sd)
		{
			system_ = system;

			if (create_sd)
			{
				SDGenerator sdg;
				sdg.generateSD(system_);
			}

			update();
		}

		void SDWidget::paintEvent(QPaintEvent *)
		{
			drawFrame_();
			renderSD_(this);
		}

		void SDWidget::drawFrame_()
		{
			QPainter p(this);

			QStyleOptionFrame opt;
			opt.initFrom(this);
			opt.state |= QStyle::State_Sunken;
			QRect erase_area = rect();

			int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this) + 1;
			erase_area.adjust(frameWidth, frameWidth, -frameWidth, -frameWidth);
			p.eraseRect(erase_area);
			style()->drawPrimitive(QStyle::PE_Frame, &opt, &p, this);

			p.end();
		}

		void SDWidget::renderSD_(QPaintDevice* pd)
		{
			if(!pd)
			{
				return;
			}

			BoundingBoxProcessor bp;
			system_.apply(bp);
			Vector3 upper = bp.getUpper() + Vector3(5.,5.,5.);
			Vector3 lower = bp.getLower() - Vector3(5.,5.,5.);

			GeometricCenterProcessor gcp;
			system_.apply(gcp);
			Vector3 center = gcp.getCenter();

			float xscale = pd->width()  / (upper.x - lower.x);
			float yscale = pd->height() / (upper.y - lower.y);

			xscale = yscale = std::min(xscale, yscale);

			QPainter painter(pd);

			QPen pen(Qt::black, 3, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin);
			painter.setPen(pen);
			painter.setRenderHint(QPainter::Antialiasing, true);
			painter.translate(pd->width()/2, pd->height()/2);

		  QFont newFont = font();
 		  newFont.setPixelSize(12);
			painter.setFont(newFont);
 
			QFontMetrics fontMetrics(newFont);
			
			// check if hydrogen atoms should be shown!
			bool show_H = options.getBool(Option::SHOW_HYDROGENS);

			AtomIterator at_it = system_.beginAtom();
			std::set<Atom*> already_seen;
			for (; +at_it; ++at_it)
			{
				if ((at_it->getElement() == PTE[Element::H]) && !show_H) continue;
				already_seen.insert(&(*at_it));	

				Atom::BondIterator b_it = at_it->beginBond();

				Vector3 from3d = at_it->getPosition() - center;
				QPointF from2d(from3d.x*xscale, from3d.y*yscale);

				for (; +b_it; ++b_it)
				{
					Atom* partner = b_it->getPartner(*at_it);
					
					if (
								 // treat each bond only once
								 (std::find(already_seen.begin(), already_seen.end(), partner) == already_seen.end()) 
								 // we don't draw hydrogens
							&& !((partner->getElement() == PTE[Element::H]) && !show_H)
						 )
					{
						Vector3 to3d = partner->getPosition() - center;
						QPointF to2d(to3d.x*xscale, to3d.y*yscale);
						QPointF shifted_from = from2d;

						// do we need to draw a character for the "to" atom?
						if (partner->getElement() != PTE[Element::C])
						{
							QRect br = fontMetrics.boundingRect(partner->getElement().getSymbol().c_str());
							QPointF label_pos(to2d.x() - br.width()/2, to2d.y() + br.height()/2 - 4);

							painter.drawText(label_pos, partner->getElement().getSymbol().c_str());	

							QRectF bounding_box(to2d.x() - br.width()/2  - 3, 
																	to2d.y() - br.height()/2 - 4, 
																	br.width() + 6, br.height() + 6);
							to2d = getEndpoint_(bounding_box, to2d, from2d, false);

						}

						// and do we need to draw a character for the "from" atom?
						if (at_it->getElement() != PTE[Element::C])
						{
							QRect br = fontMetrics.boundingRect(at_it->getElement().getSymbol().c_str());

							QRectF bounding_box(from2d.x() - br.width()/2  - 3, 
																	from2d.y() - br.height()/2 - 4, 
																	br.width() + 6, br.height() + 6);
							shifted_from = getEndpoint_(bounding_box, to2d, from2d, true);
						}<commit_msg>Safeguard SDWidget against lines of width 0<commit_after>#include <BALL/VIEW/WIDGETS/SDWidget.h>

#include <BALL/VIEW/KERNEL/iconLoader.h>

#include <BALL/KERNEL/bond.h>
#include <BALL/KERNEL/PTE.h>
#include <BALL/STRUCTURE/geometricProperties.h>
#include <BALL/STRUCTURE/sdGenerator.h>

#include <QtGui/QPainter>
#include <QtGui/QStyle>
#include <QtGui/QStyleOptionFocusRect>
#include <QtGui/QAction>

#include <QtGui/QFileDialog>
#include <QtGui/QImageWriter>

#include <set>

namespace BALL
{
	namespace VIEW
	{
		const char* SDWidget::Option::SHOW_HYDROGENS = "sd_widget_show_hydrogens";
		const bool  SDWidget::Default::SHOW_HYDROGENS = false;

		SDWidget::SDWidget(QWidget *parent, bool show_hydrogens)
			: QWidget(parent)
		{
			setup_();

			options[SDWidget::Option::SHOW_HYDROGENS] = show_hydrogens;
		}

		SDWidget::SDWidget(const System& system, QWidget *parent)
			: QWidget(parent)
		{
			setup_();

			plot(system);
		}

		void SDWidget::setup_()
		{
			setDefaultOptions();
			setBackgroundRole(QPalette::Base);

			//Todo: Add a nice icon
			QAction* export_image = new QAction(tr("Export image"), this);

			export_image->setIcon(IconLoader::instance().getIcon("actions/document-save"));

			addAction(export_image);
			connect(export_image, SIGNAL(triggered()), this, SLOT(exportImage_()));
		}

		SDWidget::~SDWidget()
		{}
		
		void SDWidget::plot(const System& system, bool create_sd)
		{
			system_ = system;

			if (create_sd)
			{
				SDGenerator sdg;
				sdg.generateSD(system_);
			}

			update();
		}

		void SDWidget::paintEvent(QPaintEvent *)
		{
			drawFrame_();
			renderSD_(this);
		}

		void SDWidget::drawFrame_()
		{
			QPainter p(this);

			QStyleOptionFrame opt;
			opt.initFrom(this);
			opt.state |= QStyle::State_Sunken;
			QRect erase_area = rect();

			int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this) + 1;
			erase_area.adjust(frameWidth, frameWidth, -frameWidth, -frameWidth);
			p.eraseRect(erase_area);
			style()->drawPrimitive(QStyle::PE_Frame, &opt, &p, this);

			p.end();
		}

		void SDWidget::renderSD_(QPaintDevice* pd)
		{
			if(!pd)
			{
				return;
			}

			BoundingBoxProcessor bp;
			system_.apply(bp);
			Vector3 upper = bp.getUpper() + Vector3(5.,5.,5.);
			Vector3 lower = bp.getLower() - Vector3(5.,5.,5.);

			GeometricCenterProcessor gcp;
			system_.apply(gcp);
			Vector3 center = gcp.getCenter();

			float xscale = pd->width()  / (upper.x - lower.x);
			float yscale = pd->height() / (upper.y - lower.y);

			xscale = yscale = std::min(xscale, yscale);

			QPainter painter(pd);

			QPen pen(Qt::black, 3, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin);
			painter.setPen(pen);
			painter.setRenderHint(QPainter::Antialiasing, true);
			painter.translate(pd->width()/2, pd->height()/2);

		  QFont newFont = font();
 		  newFont.setPixelSize(12);
			painter.setFont(newFont);
 
			QFontMetrics fontMetrics(newFont);
			
			// check if hydrogen atoms should be shown!
			bool show_H = options.getBool(Option::SHOW_HYDROGENS);

			AtomIterator at_it = system_.beginAtom();
			std::set<Atom*> already_seen;
			for (; +at_it; ++at_it)
			{
				if ((at_it->getElement() == PTE[Element::H]) && !show_H) continue;
				already_seen.insert(&(*at_it));	

				Atom::BondIterator b_it = at_it->beginBond();

				Vector3 from3d = at_it->getPosition() - center;
				QPointF from2d(from3d.x*xscale, from3d.y*yscale);

				for (; +b_it; ++b_it)
				{
					Atom* partner = b_it->getPartner(*at_it);
					
					if (
								 // treat each bond only once
								 (std::find(already_seen.begin(), already_seen.end(), partner) == already_seen.end()) 
								 // we don't draw hydrogens
							&& !((partner->getElement() == PTE[Element::H]) && !show_H)
						 )
					{
						Vector3 to3d = partner->getPosition() - center;
						QPointF to2d(to3d.x*xscale, to3d.y*yscale);
						QPointF shifted_from = from2d;

						// do we need to draw a character for the "to" atom?
						if (partner->getElement() != PTE[Element::C])
						{
							QRect br = fontMetrics.boundingRect(partner->getElement().getSymbol().c_str());
							QPointF label_pos(to2d.x() - br.width()/2, to2d.y() + br.height()/2 - 4);

							painter.drawText(label_pos, partner->getElement().getSymbol().c_str());	

							QRectF bounding_box(to2d.x() - br.width()/2  - 3, 
																	to2d.y() - br.height()/2 - 4, 
																	br.width() + 6, br.height() + 6);
							to2d = getEndpoint_(bounding_box, to2d, from2d, false);

						}

						// and do we need to draw a character for the "from" atom?
						if (at_it->getElement() != PTE[Element::C])
						{
							QRect br = fontMetrics.boundingRect(at_it->getElement().getSymbol().c_str());

							QRectF bounding_box(from2d.x() - br.width()/2  - 3, 
																	from2d.y() - br.height()/2 - 4, 
																	br.width() + 6, br.height() + 6);
							shifted_from = getEndpoint_(bounding_box, to2d, from2d, true);
						}

						// don't draw if the points are too close to each other (Qt tends to crash if this happens...)
						if ((to2d - shifted_from).toPoint().manhattanLength() == 0)
							continue;
{'fraction_non_alphanumeric': 0.15780548628428928, 'fraction_numerical': 0.011072319201995013, 'mean_word_length': 3.2718363868768643, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '100464', 'n_tokens_mistral': 4565, 'n_tokens_neox': 3614, 'n_words': 669}