Skip to content

Commit 58deefa

Browse files
committed
refactoring of RS_DimAngular, fix LibreCAD#911 and LibreCAD#824/LibreCAD#896
1 parent da597e1 commit 58deefa

11 files changed

+700
-302
lines changed

librecad/src/actions/rs_actiondimangular.cpp

Lines changed: 200 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
**
2626
**********************************************************************/
2727

28+
#include <cmath>
2829
#include <QAction>
2930
#include <QMouseEvent>
3031
#include "rs_actiondimangular.h"
@@ -34,17 +35,15 @@
3435
#include "rs_graphicview.h"
3536
#include "rs_commandevent.h"
3637
#include "rs_information.h"
37-
#include "rs_line.h"
3838
#include "rs_coordinateevent.h"
3939
#include "rs_preview.h"
4040
#include "rs_debug.h"
41+
#include "rs_math.h"
4142

4243
RS_ActionDimAngular::RS_ActionDimAngular(RS_EntityContainer& container,
4344
RS_GraphicView& graphicView) :
44-
RS_ActionDimension("Draw Angular Dimensions", container, graphicView),
45-
center( new RS_Vector{})
45+
RS_ActionDimension( "Draw Angular Dimensions", container, graphicView)
4646
{
47-
actionType= RS2::ActionDimAngular;
4847
reset();
4948
}
5049

@@ -54,24 +53,22 @@ void RS_ActionDimAngular::reset()
5453
{
5554
RS_ActionDimension::reset();
5655

57-
edata.reset( new RS_DimAngularData( RS_Vector(false),
58-
RS_Vector(false),
59-
RS_Vector(false),
60-
RS_Vector(false)) );
61-
line1 = nullptr;
62-
line2 = nullptr;
63-
*center = {}; //default to invalid vector
56+
actionType = RS2::ActionDimAngular;
57+
edata.reset( new RS_DimAngularData( RS_Vector( false),
58+
RS_Vector( false),
59+
RS_Vector( false),
60+
RS_Vector( false)) );
6461
RS_DIALOGFACTORY->requestOptions( this, true, true);
6562
}
6663

6764
void RS_ActionDimAngular::trigger()
6865
{
6966
RS_PreviewActionInterface::trigger();
7067

71-
if (line1 && line2) {
72-
RS_DimAngular* newEntity {new RS_DimAngular(container,
73-
*data,
74-
*edata)};
68+
if (line1.getStartpoint().valid && line2.getStartpoint().valid) {
69+
RS_DimAngular* newEntity {new RS_DimAngular( container,
70+
*data,
71+
*edata)};
7572

7673
newEntity->setLayerToActive();
7774
newEntity->setPenToActive();
@@ -98,14 +95,12 @@ void RS_ActionDimAngular::trigger()
9895

9996
void RS_ActionDimAngular::mouseMoveEvent(QMouseEvent* e)
10097
{
101-
RS_DEBUG->print("RS_ActionDimAngular::mouseMoveEvent begin");
98+
RS_DEBUG->print( "RS_ActionDimAngular::mouseMoveEvent begin");
10299

103100
switch (getStatus()) {
104101
case SetPos:
105-
if (line1 && line2 && center->valid) {
106-
edata->definitionPoint4 = snapPoint(e);
107-
108-
RS_DimAngular* d {new RS_DimAngular(preview.get(), *data, *edata)};
102+
if( setData( snapPoint(e))) {
103+
RS_DimAngular *d {new RS_DimAngular( preview.get(), *data, *edata)};
109104

110105
deletePreview();
111106
preview->addEntity(d);
@@ -126,44 +121,22 @@ void RS_ActionDimAngular::mouseReleaseEvent(QMouseEvent* e)
126121
if (Qt::LeftButton == e->button()) {
127122
switch (getStatus()) {
128123
case SetLine1: {
129-
RS_Entity* en {catchEntity( e, RS2::ResolveAll)};
124+
RS_Entity *en {catchEntity( e, RS2::ResolveAll)};
130125
if (en && RS2::EntityLine == en->rtti()) {
131-
line1 = dynamic_cast<RS_Line*>(en);
132-
setStatus( SetLine2);
126+
line1 = *dynamic_cast<RS_Line*>(en);
127+
click1 = line1.getNearestPointOnEntity( graphicView->toGraph( e->x(), e->y()));
128+
setStatus(SetLine2);
133129
}
134130
break; }
135131

136132
case SetLine2: {
137-
RS_Entity* en {catchEntity( e, RS2::ResolveAll)};
138-
if (en && RS2::EntityLine == en->rtti()) {
139-
line2 = dynamic_cast<RS_Line*>(en);
140-
141-
RS_VectorSolutions sol {RS_Information::getIntersectionLineLine( line1, line2)};
142-
143-
if (sol.get( 0).valid) {
144-
*center = sol.get( 0);
145-
146-
if (center->distanceTo(line1->getStartpoint())
147-
< center->distanceTo(line1->getEndpoint())) {
148-
edata->definitionPoint1 = line1->getStartpoint();
149-
edata->definitionPoint2 = line1->getEndpoint();
150-
}
151-
else {
152-
edata->definitionPoint1 = line1->getEndpoint();
153-
edata->definitionPoint2 = line1->getStartpoint();
154-
}
155-
156-
if (center->distanceTo(line2->getStartpoint())
157-
< center->distanceTo(line2->getEndpoint())) {
158-
edata->definitionPoint3 = line2->getStartpoint();
159-
data->definitionPoint = line2->getEndpoint();
160-
}
161-
else {
162-
edata->definitionPoint3 = line2->getEndpoint();
163-
data->definitionPoint = line2->getStartpoint();
164-
}
165-
graphicView->moveRelativeZero( *center);
166-
setStatus( SetPos);
133+
RS_Entity *en{catchEntity(e, RS2::ResolveAll)};
134+
if (en && en->rtti()==RS2::EntityLine) {
135+
line2 = *dynamic_cast<RS_Line*>(en);
136+
click2 = line2.getNearestPointOnEntity( graphicView->toGraph( e->x(), e->y()));
137+
if( setData( click2, true)) {
138+
graphicView->moveRelativeZero( center);
139+
setStatus(SetPos);
167140
}
168141
}
169142
break; }
@@ -182,16 +155,17 @@ void RS_ActionDimAngular::mouseReleaseEvent(QMouseEvent* e)
182155

183156
void RS_ActionDimAngular::coordinateEvent(RS_CoordinateEvent* e)
184157
{
185-
if (!e) {
158+
if ( ! e) {
186159
return;
187160
}
188161

189162
switch (getStatus()) {
190163
case SetPos:
191-
edata->definitionPoint4 = e->getCoordinate();
192-
trigger();
193-
reset();
194-
setStatus( SetLine1);
164+
if( setData( e->getCoordinate())) {
165+
trigger();
166+
reset();
167+
setStatus( SetLine1);
168+
}
195169
break;
196170

197171
default:
@@ -286,4 +260,172 @@ void RS_ActionDimAngular::updateMouseButtonHints()
286260
}
287261
}
288262

263+
/**
264+
* Justify one of the angle lines to ensure that the starting point
265+
* of the line has the same angle from the intersection point as the
266+
* selection click point and it is further away than the line end point
267+
*
268+
* @param line A selected line for the dimension
269+
* @param click The click pos which selected the line
270+
* @param center The intersection of the 2 lines to dimension
271+
*/
272+
void RS_ActionDimAngular::justify(RS_Line &line, const RS_Vector &click)
273+
{
274+
RS_Vector vStartPoint( line.getStartpoint());
275+
276+
if( ! RS_Math::equal( vStartPoint.angleTo(center), click.angleTo( center))
277+
|| vStartPoint.distanceTo( center) < click.distanceTo( center)) {
278+
line.reverse();
279+
}
280+
}
281+
282+
/**
283+
* Create a sorted array with angles from the lines intersection point
284+
* to the starting points and their revers angles.
285+
* Ensure, that line1 and line2 are in CCW order.
286+
* Compute an offset for quadrant() methode.
287+
*
288+
* @param line A selected line for the dimension
289+
* @param click The click pos which selected the line
290+
* @param center The intersection of the 2 lines to dimension
291+
*/
292+
void RS_ActionDimAngular::lineOrder(const RS_Vector &dimPos)
293+
{
294+
if( ! center.valid) {
295+
return;
296+
}
297+
298+
// starting point angles and selection point angle from intersection point
299+
double a0 {(dimPos - center).angle()};
300+
double a1 {(line1.getStartpoint() - center).angle()};
301+
double a2 {(line2.getStartpoint() - center).angle()};
302+
303+
// swap lines if necessary to ensure CCW order
304+
if( RS_Math::correctAngle2( a1 - a0) > RS_Math::correctAngle2( a2 - a0)) {
305+
RS_Line swapLines( line1);
306+
line1 = line2;
307+
line2 = swapLines;
308+
double swapAngle {a1};
309+
a1 = a2;
310+
a2 = swapAngle;
311+
}
312+
313+
// sorted array with starting point and reverse angles
314+
angles.clear();
315+
angles.push_back( a1);
316+
angles.push_back( RS_Math::correctAngle( a1 + M_PI));
317+
angles.push_back( a2);
318+
angles.push_back( RS_Math::correctAngle( a2 + M_PI));
319+
std::sort( angles.begin(), angles.end());
320+
321+
// find starting quadrant and compute the offset for quadrant() method
322+
int startQuadrant = 0;
323+
for( auto angle : angles) {
324+
if( RS_Math::equal( a1, angle)) {
325+
break;
326+
}
327+
++startQuadrant;
328+
}
329+
quadrantOffset = 0x03 & (4 - startQuadrant);
330+
}
331+
332+
/**
333+
* Find the quadrant of \p angle relative to 1st quadrant.
334+
* When the angle lines are selected, the starting quadrant
335+
* is shifted to become 0 by \p quadrantOffset.
336+
* This is the criterion how the angles dimension is drawn.
337+
*
338+
* @param angle The angle, e.g. mouse or coordinate position
339+
* @return The quadrant of \p angle, relative to the 1st selection quadrant
340+
*/
341+
int RS_ActionDimAngular::quadrant(const double angle)
342+
{
343+
if( 1 > angles.size()) {
344+
return 0;
345+
}
346+
347+
double a1 {RS_Math::correctAngle2( angles.at(0) - angle)};
348+
double a2 {RS_Math::correctAngle2( angles.at(1) - angle)};
349+
350+
int angleQuadrant {0};
351+
if( 0.0 < a1 && 0.0 < a2) {
352+
angleQuadrant = 3;
353+
}
354+
else if( 0.0 >= a1 && 0.0 >= a2) {
355+
angleQuadrant = 1;
356+
}
357+
else if( 0.0 < a1 && 0.0 >= a2) {
358+
angleQuadrant = 2;
359+
}
360+
361+
return (0x03 & (angleQuadrant + quadrantOffset));
362+
}
363+
364+
/**
365+
* On \p mouseMoveEvent, \p mouseReleaseEvent and \p coordinateEvent
366+
* this methode sets the dimension data appropriate to the mouse
367+
* cursor/coordinate in \p dimPos.
368+
* When \p calcCenter is true, the intersection point and other static
369+
* values are computed. This is only necessary, when line selection changes,
370+
* e.g. on \p mouseReleaseEvent. For \p mouseMoveEvent calcCenter is false.
371+
*
372+
* @param dimPos The mouse/coordinate position
373+
* @param calcCenter If true, the center and corresponding values are calculated
374+
* @return true If the dimension data were set, false is a parameter is invalid
375+
*/
376+
bool RS_ActionDimAngular::setData(const RS_Vector &dimPos, const bool calcCenter /*= false*/)
377+
{
378+
if( ! line1.getStartpoint().valid || ! line2.getStartpoint().valid) {
379+
return false;
380+
}
381+
382+
if ( ! center.valid || calcCenter) {
383+
RS_VectorSolutions sol = RS_Information::getIntersectionLineLine( &line1, &line2);
384+
center = sol.get(0);
385+
}
386+
if ( ! center.valid) {
387+
return false;
388+
}
389+
390+
if( calcCenter) {
391+
justify( line1, click1);
392+
justify( line2, click2);
393+
lineOrder( dimPos);
394+
}
395+
396+
edata->definitionPoint4 = dimPos;
397+
switch( quadrant( (dimPos - center).angle())) {
398+
default:
399+
case 0:
400+
edata->definitionPoint1 = line1.getEndpoint();
401+
edata->definitionPoint2 = line1.getStartpoint();
402+
edata->definitionPoint3 = line2.getEndpoint();
403+
data->definitionPoint = line2.getStartpoint();
404+
break;
405+
406+
case 1:
407+
edata->definitionPoint1 = line2.getEndpoint();
408+
edata->definitionPoint2 = line2.getStartpoint();
409+
edata->definitionPoint3 = line1.getStartpoint();
410+
data->definitionPoint = line1.getEndpoint();
411+
break;
412+
413+
case 2:
414+
edata->definitionPoint1 = line2.getEndpoint();
415+
edata->definitionPoint2 = line2.getStartpoint();
416+
edata->definitionPoint3 = line1.getEndpoint();
417+
data->definitionPoint = line1.getStartpoint();
418+
break;
419+
420+
case 3:
421+
edata->definitionPoint1 = line2.getStartpoint();
422+
edata->definitionPoint2 = line2.getEndpoint();
423+
edata->definitionPoint3 = line1.getEndpoint();
424+
data->definitionPoint = line1.getStartpoint();
425+
break;
426+
}
427+
428+
return true;
429+
}
430+
289431
// EOF

librecad/src/actions/rs_actiondimangular.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
#define RS_ACTIONDIMANGULAR_H
3030

3131
#include "rs_actiondimension.h"
32+
#include "rs_line.h"
3233

3334
struct RS_DimAngularData;
34-
class RS_Line;
3535

3636
/**
3737
* This action class can handle user events to draw angular dimensions.
@@ -72,11 +72,20 @@ class RS_ActionDimAngular : public RS_ActionDimension
7272
void updateMouseButtonHints() override;
7373

7474
private:
75-
RS_Line* line1 {nullptr}; ///< 1st chosen line
76-
RS_Line* line2 {nullptr}; ///< 2nd chosen line
77-
std::unique_ptr<RS_Vector> center; ///< Center of arc
75+
RS_Line line1; ///< 1st chosen line
76+
RS_Line line2; ///< 2nd chosen line
77+
RS_Vector click1; ///< 1st click pos
78+
RS_Vector click2; ///< 2nd click pos
79+
RS_Vector center; ///< Center of arc
7880
std::unique_ptr<RS_DimAngularData> edata; ///< Data of new dimension
79-
Status lastStatus {SetLine1}; ///< Last status before entering text.
81+
Status lastStatus; ///< Last status before entering text
82+
std::vector<double> angles; ///< Array to sort line angles
83+
int quadrantOffset {0}; ///< Offset on starting quadrant
84+
85+
void justify( RS_Line &line, const RS_Vector &click);
86+
void lineOrder(const RS_Vector &dimPos);
87+
int quadrant(const double angle);
88+
bool setData(const RS_Vector& dimPos, const bool calcCenter = false);
8089
};
8190

8291
#endif

0 commit comments

Comments
 (0)