1 /*
2 	Copyright 2006-2019 The QElectroTech Team
3 	This file is part of QElectroTech.
4 
5 	QElectroTech is free software: you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation, either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	QElectroTech is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "partline.h"
19 #include <cmath>
20 #include "elementscene.h"
21 #include "QPropertyUndoCommand/qpropertyundocommand.h"
22 #include "QetGraphicsItemModeler/qetgraphicshandleritem.h"
23 
24 
25 /**
26  * @brief PartLine::PartLine
27  * Constructor
28  * @param editor : QETElementEditor of this part
29  * @param parent : parent item
30  */
PartLine(QETElementEditor * editor,QGraphicsItem * parent)31 PartLine::PartLine(QETElementEditor *editor, QGraphicsItem *parent) :
32 	CustomElementGraphicPart(editor, parent),
33 	first_end(Qet::None),
34 	first_length(1.5),
35 	second_end(Qet::None),
36 	second_length(1.5),
37 	m_undo_command(nullptr)
38 {}
39 
40 /// Destructeur
~PartLine()41 PartLine::~PartLine()
42 {
43 	if(m_undo_command)
44 		delete m_undo_command;
45 
46 	removeHandler();
47 }
48 
49 /**
50  * @brief PartLine::requiredLengthForEndType
51  * @param end_type
52  * @return the number of "length" needed to draw a extremity of type Qet::EndType.
53  */
requiredLengthForEndType(const Qet::EndType & end_type)54 uint PartLine::requiredLengthForEndType(const Qet::EndType &end_type)
55 {
56 	uint length_count_required = 0;
57 
58 	if (end_type == Qet::Circle || end_type == Qet::Diamond)
59 		length_count_required = 2;
60 	else if (end_type == Qet::Simple || end_type == Qet::Triangle)
61 		length_count_required = 1;
62 
63 	return(length_count_required);
64 }
65 
66 /**
67  * @brief PartLine::paint
68  * Draw this line
69  * @param painter
70  * @param options
71  * @param widget
72  */
paint(QPainter * painter,const QStyleOptionGraphicsItem * options,QWidget * widget)73 void PartLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *options, QWidget *widget)
74 {
75 	Q_UNUSED(widget);
76 	if (isUseless()) return;
77 
78 	painter->save();
79 	applyStylesToQPainter(*painter);
80 	QPen t = painter -> pen();
81 	t.setJoinStyle(Qt::MiterJoin);
82 	t.setCosmetic(options && options -> levelOfDetail < 1.0);
83 
84 	if (isSelected()) t.setColor(Qt::red);
85 
86 	painter -> setPen(t);
87 
88 	if (first_end || second_end)
89 		painter -> drawPath(path());
90 	else
91 		painter -> drawLine(m_line);
92 
93 	if (m_hovered)
94 		drawShadowShape(painter);
95 
96 	painter->restore();
97 }
98 
99 /**
100  * @brief PartLine::toXml
101  * Export this line in xml
102  * @param xml_document : Xml document to use for create the xml element.
103  * @return an xml element that describe this line
104  */
toXml(QDomDocument & xml_document) const105 const QDomElement PartLine::toXml(QDomDocument &xml_document) const
106 {
107 	QPointF p1(sceneP1());
108 	QPointF p2(sceneP2());
109 
110 	QDomElement xml_element = xml_document.createElement("line");
111 	xml_element.setAttribute("x1", QString("%1").arg(p1.x()));
112 	xml_element.setAttribute("y1", QString("%1").arg(p1.y()));
113 	xml_element.setAttribute("x2", QString("%1").arg(p2.x()));
114 	xml_element.setAttribute("y2", QString("%1").arg(p2.y()));
115 	xml_element.setAttribute("end1", Qet::endTypeToString(first_end));
116 	xml_element.setAttribute("length1", QString("%1").arg(first_length));
117 	xml_element.setAttribute("end2", Qet::endTypeToString(second_end));
118 	xml_element.setAttribute("length2", QString("%1").arg(second_length));
119 
120 	stylesToXml(xml_element);
121 	return(xml_element);
122 }
123 
124 /**
125  * @brief PartLine::fromXml
126  * Import the properties of this line from a xml element.
127  * @param qde : Xml document to use
128  */
fromXml(const QDomElement & qde)129 void PartLine::fromXml(const QDomElement &qde) {
130 	stylesFromXml(qde);
131 	m_line = QLineF(mapFromScene(qde.attribute("x1", "0").toDouble(),
132 								 qde.attribute("y1", "0").toDouble()),
133 					mapFromScene(qde.attribute("x2", "0").toDouble(),
134 								 qde.attribute("y2", "0").toDouble()));
135 
136 	first_end     = Qet::endTypeFromString(qde.attribute("end1"));
137 	first_length  = qde.attribute("length1", "1.5").toDouble();
138 	second_end    = Qet::endTypeFromString(qde.attribute("end2"));
139 	second_length = qde.attribute("length2", "1.5").toDouble();
140 }
141 
142 /**
143  * @brief PartLine::itemChange
144  * @param change
145  * @param value
146  * @return
147  */
itemChange(QGraphicsItem::GraphicsItemChange change,const QVariant & value)148 QVariant PartLine::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
149 {
150 	if (change == ItemSelectedHasChanged && scene())
151 	{
152 		if (value.toBool() == true)
153 		{
154 				//When item is selected, he must to be up to date whene the selection in the scene change, for display or not the handler,
155 				//according to the number of selected items.
156 			connect(scene(), &QGraphicsScene::selectionChanged, this, &PartLine::sceneSelectionChanged);
157 
158 			if (scene()->selectedItems().size() == 1)
159 				addHandler();
160 		}
161 		else
162 		{
163 			disconnect(scene(), &QGraphicsScene::selectionChanged, this, &PartLine::sceneSelectionChanged);
164 			removeHandler();
165 		}
166 	}
167 	else if (change == ItemPositionHasChanged)
168 	{
169 		adjusteHandlerPos();
170 	}
171 	else if (change == ItemSceneChange)
172 	{
173 		if(scene())
174 			disconnect(scene(), &QGraphicsScene::selectionChanged, this, &PartLine::sceneSelectionChanged);
175 
176 		setSelected(false); //This is item removed from scene, then we deselect this, and so, the handlers is also removed.
177 	}
178 
179 	return QGraphicsItem::itemChange(change, value);
180 }
181 
182 /**
183  * @brief PartLine::sceneEventFilter
184  * @param watched
185  * @param event
186  * @return
187  */
sceneEventFilter(QGraphicsItem * watched,QEvent * event)188 bool PartLine::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
189 {
190 	//Watched must be an handler
191 	if(watched->type() == QetGraphicsHandlerItem::Type)
192 	{
193 		QetGraphicsHandlerItem *qghi = qgraphicsitem_cast<QetGraphicsHandlerItem *>(watched);
194 
195 		if(m_handler_vector.contains(qghi)) //Handler must be in m_vector_index, then we can start resize
196 		{
197 			m_vector_index = m_handler_vector.indexOf(qghi);
198 			if (m_vector_index != -1)
199 			{
200 				if(event->type() == QEvent::GraphicsSceneMousePress) //Click
201 				{
202 					handlerMousePressEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
203 					return true;
204 				}
205 				else if(event->type() == QEvent::GraphicsSceneMouseMove) //Move
206 				{
207 					handlerMouseMoveEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
208 					return true;
209 				}
210 				else if (event->type() == QEvent::GraphicsSceneMouseRelease) //Release
211 				{
212 					handlerMouseReleaseEvent(qghi, static_cast<QGraphicsSceneMouseEvent *>(event));
213 					return true;
214 				}
215 			}
216 		}
217 	}
218 
219 	return false;
220 }
221 
222 /**
223  * @brief PartLine::adjusteHandlerPos
224  * Adjust the position of the handler item
225  */
adjusteHandlerPos()226 void PartLine::adjusteHandlerPos()
227 {
228 	if(m_handler_vector.isEmpty())
229 		return;
230 
231 	QVector<QPointF> points_vector;
232 	points_vector << m_line.p1() << m_line.p2();
233 
234 	if (m_handler_vector.size() == points_vector.size())
235 	{
236 		points_vector = mapToScene(points_vector);
237 		for (int i = 0 ; i < points_vector.size() ; ++i)
238 			m_handler_vector.at(i)->setPos(points_vector.at(i));
239 	}
240 }
241 
242 /**
243  * @brief PartLine::handlerMousePressEvent
244  * @param qghi
245  * @param event
246  */
handlerMousePressEvent(QetGraphicsHandlerItem * qghi,QGraphicsSceneMouseEvent * event)247 void PartLine::handlerMousePressEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
248 {
249 	Q_UNUSED(qghi);
250 	Q_UNUSED(event);
251 
252 	m_undo_command = new QPropertyUndoCommand(this, "line", QVariant(m_line));
253 	m_undo_command->setText(tr("Modifier une ligne"));
254 	m_undo_command->enableAnimation();
255 	return;
256 }
257 
258 /**
259  * @brief PartLine::handlerMouseMoveEvent
260  * @param qghi
261  * @param event
262  */
handlerMouseMoveEvent(QetGraphicsHandlerItem * qghi,QGraphicsSceneMouseEvent * event)263 void PartLine::handlerMouseMoveEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
264 {
265 	Q_UNUSED(qghi);
266 
267 	QPointF new_pos = event->scenePos();
268 	if (event->modifiers() != Qt::ControlModifier)
269 		new_pos = elementScene()->snapToGrid(event->scenePos());
270 	new_pos = mapFromScene(new_pos);
271 
272 	prepareGeometryChange();
273 	if (m_vector_index == 0)
274 		m_line.setP1(new_pos);
275 	else
276 		m_line.setP2(new_pos);
277 
278 	adjusteHandlerPos();
279 }
280 
281 /**
282  * @brief PartLine::handlerMouseReleaseEvent
283  * @param qghi
284  * @param event
285  */
handlerMouseReleaseEvent(QetGraphicsHandlerItem * qghi,QGraphicsSceneMouseEvent * event)286 void PartLine::handlerMouseReleaseEvent(QetGraphicsHandlerItem *qghi, QGraphicsSceneMouseEvent *event)
287 {
288 	Q_UNUSED(qghi);
289 	Q_UNUSED(event);
290 
291 	m_undo_command->setNewValue(QVariant(m_line));
292 	elementScene()->undoStack().push(m_undo_command);
293 	m_undo_command = nullptr;
294 	m_vector_index = -1;
295 }
296 
297 /**
298  * @brief PartLine::sceneSelectionChanged
299  * When the scene selection change, if there are several primitive selected, we remove the handler of this item
300  */
sceneSelectionChanged()301 void PartLine::sceneSelectionChanged()
302 {
303 	if (this->isSelected() && scene()->selectedItems().size() == 1)
304 		addHandler();
305 	else
306 		removeHandler();
307 }
308 
309 /**
310  * @brief PartLine::addHandler
311  * Add handlers for this item
312  */
addHandler()313 void PartLine::addHandler()
314 {
315 	if (m_handler_vector.isEmpty() && scene())
316 	{
317 		QVector<QPointF> points_vector;
318 		points_vector << m_line.p1() << m_line.p2();
319 
320 		m_handler_vector = QetGraphicsHandlerItem::handlerForPoint(mapToScene(points_vector));
321 
322 		for(QetGraphicsHandlerItem *handler : m_handler_vector)
323 		{
324 			handler->setColor(Qt::blue);
325 			scene()->addItem(handler);
326 			handler->installSceneEventFilter(this);
327 			handler->setZValue(this->zValue()+1);
328 		}
329 	}
330 }
331 
332 /**
333  * @brief PartLine::removeHandler
334  * Remove the handlers of this item
335  */
removeHandler()336 void PartLine::removeHandler()
337 {
338 	if (!m_handler_vector.isEmpty())
339 	{
340 		qDeleteAll(m_handler_vector);
341 		m_handler_vector.clear();
342 	}
343 }
344 
345 /**
346  * @brief PartLine::sceneP1
347  * @return the point p1 in scene coordinate
348  */
sceneP1() const349 QPointF PartLine::sceneP1() const {
350 	return(mapToScene(m_line.p1()));
351 }
352 
353 /**
354  * @brief PartLine::sceneP2
355  * @return the point p2 in scen coordinate
356  */
sceneP2() const357 QPointF PartLine::sceneP2() const {
358 	return(mapToScene(m_line.p2()));
359 }
360 
361 /**
362  * @brief PartLine::shape
363  * @return the shape of this item
364  */
shape() const365 QPainterPath PartLine::shape() const
366 {
367 	QPainterPath shape;
368 
369 		//We calcul path only if there is an end type
370 		//Else we just draw a line
371 	if (first_end || second_end)
372 		shape.addPath(path());
373 	else
374 	{
375 		shape.moveTo(m_line.p1());
376 		shape.lineTo(m_line.p2());
377 	}
378 
379 	QPainterPathStroker pps;
380 	pps.setWidth(m_hovered? penWeight()+SHADOWS_HEIGHT : penWeight());
381 	shape = pps.createStroke(shape);
382 
383 	return shape;
384 }
385 
shadowShape() const386 QPainterPath PartLine::shadowShape() const
387 {
388 	QPainterPath shape;
389 
390 		//We calcul path only if there is an end type
391 		//Else we just draw a line
392 	if (first_end || second_end)
393 		shape.addPath(path());
394 	else
395 	{
396 		shape.moveTo(m_line.p1());
397 		shape.lineTo(m_line.p2());
398 	}
399 
400 	QPainterPathStroker pps;
401 	pps.setWidth(penWeight());
402 
403 	return (pps.createStroke(shape));
404 }
405 
406 /**
407  * @brief PartLine::fourShapePoints
408  * @return a list with the two points that delimite the line
409  * + the four points surrounding these two points
410  */
fourShapePoints() const411 QList<QPointF> PartLine::fourShapePoints() const
412 {
413 	const qreal marge = 2.0;
414 
415 	QPointF a = m_line.p1();
416 	QPointF b = m_line.p2();
417 
418 	QList<QPointF> result;
419 
420 		//Special case, the line is defined by one point
421 	if (a == b)
422 	{
423 		result << QPointF(a.x() - marge, a.y() - marge);
424 		result << QPointF(a.x() - marge, a.y() + marge);
425 		result << QPointF(a.x() + marge, a.y() + marge);
426 		result << QPointF(a.x() + marge, a.y() - marge);
427 	}
428 	else
429 	{
430 			//We calcule the vector AB : (xb-xa, yb-ya)
431 		QPointF v_ab = b - a;
432 
433 			//And the distance AB: root of the coordinates of the vector squared
434 		qreal ab = sqrt(pow(v_ab.x(), 2) + pow(v_ab.y(), 2));
435 
436 			//Next, we define the vector u(a, b) wich is equal to the vector AB divided
437 			//by is length and multiplied by the length of marge.
438 		QPointF u = v_ab / ab * marge;
439 
440 			//We define the vector v(-b, a) wich is perpendicular to AB
441 		QPointF v(-u.y(), u.x());
442 		QPointF m = -u + v; // we have vector M = -u + v
443 		QPointF n = -u - v; // and vector N=-u-v
444 		QPointF h =  a + m; // H = A + M
445 		QPointF k =  a + n; // K = A + N
446 		QPointF i =  b - n; // I = B - N
447 		QPointF j =  b - m; // J = B - M
448 
449 		result << h << i << j << k;
450 	}
451 	return(result);
452 }
453 
454 /**
455  * @brief PartLine::firstEndCircleRect
456  * @return the rectangle bordering the entirety of the first extremity
457  */
firstEndCircleRect() const458 QRectF PartLine::firstEndCircleRect() const
459 {
460 	QList<QPointF> interesting_points = fourEndPoints(m_line.p1(),
461 													  m_line.p2(),
462 													  first_length);
463 
464 	QRectF end_rect(
465 		interesting_points[0] - QPointF(first_length, first_length),
466 		QSizeF(2.0 * first_length, 2.0 * first_length)
467 	);
468 
469 	return(end_rect);
470 }
471 
472 /**
473  * @brief PartLine::secondEndCircleRect
474  * @return the rectangle bordering the entirety of the second extremity
475  */
secondEndCircleRect() const476 QRectF PartLine::secondEndCircleRect() const {
477 	QList<QPointF> interesting_points = fourEndPoints(m_line.p2(),
478 													  m_line.p1(),
479 													  second_length);
480 
481 	QRectF end_rect(
482 		interesting_points[0] - QPointF(second_length, second_length),
483 		QSizeF(2.0 * second_length, 2.0 * second_length)
484 	);
485 
486 	return(end_rect);
487 }
488 
489 /**
490  * @brief PartLine::debugPaint
491  * Display several composante of the drawing
492  * -the bounding rect
493  * -special points at each extremity
494  * -the quadrature of the circle at each extremity, even if itself is an other type
495  * @param painter
496  */
debugPaint(QPainter * painter)497 void PartLine::debugPaint(QPainter *painter)
498 {
499 	painter -> save();
500 	painter -> setPen(Qt::gray);
501 	painter -> drawRect(boundingRect());
502 
503 	painter -> setPen(Qt::green);
504 	painter -> drawRect(firstEndCircleRect());
505 	painter -> drawRect(secondEndCircleRect());
506 
507 	painter -> setPen(Qt::red);
508 
509 	foreach(QPointF pointy, fourEndPoints(m_line.p1(), m_line.p2(), first_length))
510 		painter -> drawEllipse(pointy, 0.1, 0.1);
511 
512 	foreach(QPointF pointy, fourEndPoints(m_line.p2(), m_line.p1(), second_length))
513 		painter -> drawEllipse(pointy, 0.1, 0.1);
514 
515 	painter -> restore();
516 }
517 
518 /**
519  * @brief PartLine::boundingRect
520  * @return the bounding rect of this part
521  */
boundingRect() const522 QRectF PartLine::boundingRect() const
523 {
524 	QRectF bound;
525 	if (first_end || second_end)
526 		bound = path().boundingRect();
527 	else
528 		bound = QRectF (m_line.p1(), m_line.p2());
529 
530 	qreal adjust = (SHADOWS_HEIGHT + penWeight()) / 2;
531 		//We add 0.5 because CustomElementGraphicPart::drawShadowShape
532 		//draw a shape bigger of 0.5 when pen weight is to 0.
533 	if (penWeight() == 0) adjust += 0.5;
534 
535 	bound = bound.normalized();
536 	bound.adjust(-adjust, -adjust, adjust, adjust);
537 
538 	return bound;
539 }
540 
541 /**
542  * @brief PartLine::isUseless
543  * @return true if this part is irrelevant and does not deserve to be Retained / registered.
544  * A line is relevant when is two point is different
545  */
isUseless() const546 bool PartLine::isUseless() const {
547 	return(m_line.p1() == m_line.p2());
548 }
549 
550 /**
551  * @brief PartLine::sceneGeometricRect
552  * @return the minimum, margin-less rectangle this part can fit into, in scene
553  * coordinates. It is different from boundingRect() because it is not supposed
554  * to imply any margin, and it is different from shape because it is a regular
555  * rectangle, not a complex shape.
556  */
sceneGeometricRect() const557 QRectF PartLine::sceneGeometricRect() const {
558 	return(QRectF(sceneP1(), sceneP2()));
559 }
560 
561 /**
562  * @brief PartLine::startUserTransformation
563  * Start the user-induced transformation, provided this primitive is contained
564  * within the \a initial_selection_rect bounding rectangle.
565  * @param initial_selection_rect
566  */
startUserTransformation(const QRectF & initial_selection_rect)567 void PartLine::startUserTransformation(const QRectF &initial_selection_rect)
568 {
569 	Q_UNUSED(initial_selection_rect)
570 	saved_points_.clear();
571 	saved_points_ << sceneP1() << sceneP2();
572 }
573 
574 /**
575  * @brief PartLine::handleUserTransformation
576  * Handle the user-induced transformation from \a initial_selection_rect to \a new_selection_rect
577  * @param initial_selection_rect
578  * @param new_selection_rect
579  */
handleUserTransformation(const QRectF & initial_selection_rect,const QRectF & new_selection_rect)580 void PartLine::handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect)
581 {
582 	QList<QPointF> mapped_points = mapPoints(initial_selection_rect, new_selection_rect, saved_points_);
583 	prepareGeometryChange();
584 	m_line = QLineF(mapFromScene(mapped_points.at(0)), mapFromScene(mapped_points.at(1)));
585 }
586 
587 /**
588  * @brief PartLine::fourEndPoints
589  * Return the four interesting point needed to draw the shape
590  * at extremity of line (circle, diamond, arrow, triangle)
591  * This points are in order :
592  *		O : point on the line, at a distance 'length' of the extremity
593  *		A : point on the line at a 'length' of 2x the extremity length
594  *		B : point at a distance of length O - O is the projection of B on the line
595  *		C : point at a distance of length O - O is the projection of C on the line
596  * @param end_point : The concerned extremity
597  * @param other_point : other needed point to define the line
598  * @param length : length to use between the extremity and the point O
599  * @return
600  */
fourEndPoints(const QPointF & end_point,const QPointF & other_point,const qreal & length)601 QList<QPointF> PartLine::fourEndPoints(const QPointF &end_point, const QPointF &other_point, const qreal &length)
602 {
603 		//Vector and length of the line
604 	QPointF line_vector = end_point - other_point;
605 	qreal line_length = sqrt(pow(line_vector.x(), 2) + pow(line_vector.y(), 2));
606 
607 		//Unitary vector and perpendicular vector
608 	QPointF u(line_vector / line_length * length);
609 	QPointF v(-u.y(), u.x());
610 
611 		// points O, A, B, C
612 	QPointF o(end_point - u);
613 	QPointF a(o - u);
614 	QPointF b(o + v);
615 	QPointF c(o - v);
616 
617 	return(QList<QPointF>() << o << a << b << c);
618 }
619 
line() const620 QLineF PartLine::line() const {
621 	return m_line;
622 }
623 
setLine(const QLineF & line)624 void PartLine::setLine(const QLineF &line)
625 {
626 	if (m_line == line) return;
627 	prepareGeometryChange();
628 	m_line = line;
629 	adjusteHandlerPos();
630 	emit lineChanged();
631 }
632 
setFirstEndType(const Qet::EndType & et)633 void PartLine::setFirstEndType(const Qet::EndType &et)
634 {
635 	if (first_end == et) return;
636 	prepareGeometryChange();
637 	first_end = et;
638 	emit firstEndTypeChanged();
639 }
640 
setSecondEndType(const Qet::EndType & et)641 void PartLine::setSecondEndType(const Qet::EndType &et)
642 {
643 	if (second_end == et) return;
644 	prepareGeometryChange();
645 	second_end = et;
646 	emit secondEndTypeChanged();
647 }
648 
setFirstEndLength(const qreal & l)649 void PartLine::setFirstEndLength(const qreal &l)
650 {
651 	qreal length = qMin(qAbs(l), m_line.length());
652 	if (first_length == length) return;
653 	prepareGeometryChange();
654 	first_length = length;
655 	emit firstEndLengthChanged();
656 }
657 
setSecondEndLength(const qreal & l)658 void PartLine::setSecondEndLength(const qreal &l)
659 {
660 	qreal length = qMin(qAbs(l), m_line.length());
661 	if (second_length == length) return;
662 	prepareGeometryChange();
663 	second_length = length;
664 	emit secondEndLengthChanged();
665 }
666 
667 /**
668  * @brief PartLine::path
669  * @return this line has a QPainterPath.
670  * It's notably use when this line have an end type (circle, triangle etc....),
671  * because return a QPainterPath with end already draw.
672  * Else if there isn't an end type get P1 and P2 of line is better (faster).
673  */
path() const674 QPainterPath PartLine::path() const
675 {
676 	QPainterPath path;
677 
678 	QPointF point1(m_line.p1());
679 	QPointF point2(m_line.p2());
680 
681 	qreal line_length(m_line.length());
682 	qreal pen_width = penWeight();
683 
684 	qreal length1 = first_length;
685 	qreal length2 = second_length;
686 
687 	//debugPaint(painter);
688 
689 		//Determine if we must to draw extremity
690 	qreal reduced_line_length = line_length - (length1 * requiredLengthForEndType(first_end));
691 	bool draw_1st_end = first_end && reduced_line_length >= 0;
692 
693 	if (draw_1st_end)
694 		reduced_line_length -= (length2 * requiredLengthForEndType(second_end));
695 	else
696 		reduced_line_length = line_length - (length2 * requiredLengthForEndType(second_end));
697 
698 
699 		//Draw the first extremity
700 	QPointF start_point;
701 	if (draw_1st_end)
702 	{
703 		QList<QPointF> four_points1(fourEndPoints(point1, point2, length1));
704 
705 		if (first_end == Qet::Circle)
706 		{
707 			path.addEllipse(QRectF(four_points1[0] - QPointF(length1, length1), QSizeF(length1 * 2.0, length1 * 2.0)));
708 			start_point = four_points1[1];
709 		}
710 		else if (first_end == Qet::Diamond)
711 		{
712 			path.addPolygon(QPolygonF() << four_points1[1] << four_points1[2] << point1 << four_points1[3] << four_points1[1]);
713 			start_point = four_points1[1];
714 		}
715 		else if (first_end == Qet::Simple)
716 		{
717 			path.addPolygon(QPolygonF() << four_points1[3] << point1 << four_points1[2]);
718 			start_point = point1;
719 
720 		}
721 		else if (first_end == Qet::Triangle)
722 		{
723 			path.addPolygon(QPolygonF() << four_points1[0] << four_points1[2] << point1 << four_points1[3] << four_points1[0]);
724 			start_point = four_points1[0];
725 		}
726 
727 			//Adjust the start point according to the pen width
728 		if (pen_width && (first_end == Qet::Simple || first_end == Qet::Circle))
729 			start_point = QLineF(start_point, point2).pointAt(pen_width / 2.0 / line_length);
730 	}
731 	else
732 	{
733 		start_point = point1;
734 	}
735 
736 		//Draw the second extremity
737 	QPointF stop_point;
738 	bool draw_2nd_end = second_end && reduced_line_length >= 0;
739 	if (draw_2nd_end)
740 	{
741 		QList<QPointF> four_points2(fourEndPoints(point2, point1, length2));
742 
743 		if (second_end == Qet::Circle)
744 		{
745 			path.addEllipse(QRectF(four_points2[0] - QPointF(length2, length2), QSizeF(length2 * 2.0, length2 * 2.0)));
746 			stop_point = four_points2[1];
747 		}
748 		else if (second_end == Qet::Diamond)
749 		{
750 			path.addPolygon(QPolygonF() << four_points2[2] << point2 << four_points2[3] << four_points2[1] << four_points2[2]);
751 			stop_point = four_points2[1];
752 		}
753 		else if (second_end == Qet::Simple)
754 		{
755 			path.addPolygon(QPolygonF() << four_points2[3] << point2 << four_points2[2]);
756 			stop_point = point2;
757 		}
758 		else if (second_end == Qet::Triangle)
759 		{/**
760 	@return true si cette partie n'est pas pertinente et ne merite pas d'etre
761 	conservee / enregistree.
762 	Une ligne est pertinente des lors que ses deux points sont differents
763 */
764 			path.addPolygon(QPolygonF() << four_points2[0] << four_points2[2] << point2 << four_points2[3] << four_points2[0]);
765 			stop_point = four_points2[0];
766 		}
767 
768 			//Adjust the end point accordint to the pen width
769 		if (pen_width && (second_end == Qet::Simple || second_end == Qet::Circle))
770 			stop_point = QLineF(point1, stop_point).pointAt((line_length - (pen_width / 2.0)) / line_length);
771 	}
772 	else
773 	{
774 		stop_point = point2;
775 	}
776 
777 	path.moveTo(start_point);
778 	path.lineTo(stop_point);
779 
780 	return path;
781 }
782