Files
newspark110/lib/engine/rs_dimangular.cpp
Chenwenxuan edac2715f0 init
2024-03-06 14:54:30 +08:00

581 lines
19 KiB
C++

/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2018 A. Stebich (librecad@mail.lordofbikes.de)
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file gpl-2.0.txt included in the
** packaging of this file.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**
** This copyright notice MUST APPEAR in all copies of the script!
**
**********************************************************************/
#include<iostream>
#include<cmath>
#include "rs_dimangular.h"
#include "rs_math.h"
#include "rs_constructionline.h"
#include "rs_arc.h"
#include "rs_line.h"
#include "rs_graphic.h"
#include "rs_information.h"
#include "rs_solid.h"
#include "rs_mtext.h"
#include "rs_debug.h"
RS_DimAngularData::RS_DimAngularData():
definitionPoint1( false),
definitionPoint2( false),
definitionPoint3( false),
definitionPoint4( false)
{
}
RS_DimAngularData::RS_DimAngularData(const RS_DimAngularData &ed):
definitionPoint1( ed.definitionPoint1),
definitionPoint2( ed.definitionPoint2),
definitionPoint3( ed.definitionPoint3),
definitionPoint4( ed.definitionPoint4)
{
}
/**
* Constructor with initialisation.
*
* @param definitionPoint Definition point of the angular dimension.
* @param leader Leader length.
*/
RS_DimAngularData::RS_DimAngularData(const RS_Vector& _definitionPoint1,
const RS_Vector& _definitionPoint2,
const RS_Vector& _definitionPoint3,
const RS_Vector& _definitionPoint4):
definitionPoint1( _definitionPoint1),
definitionPoint2( _definitionPoint2),
definitionPoint3( _definitionPoint3),
definitionPoint4( _definitionPoint4)
{
}
/**
* Constructor with initialisation.
*
* @param dimscale general scale (DIMSCALE)
* @param dimexo distance from entities (DIMEXO)
* @param dimexe extension line extension (DIMEXE)
* @param dimtxt text height (DIMTXT)
* @param dimgap text distance to line (DIMGAP)
* @param arrowSize arrow length
*/
LC_DimAngularVars::LC_DimAngularVars(const double _dimscale,
const double _dimexo,
const double _dimexe,
const double _dimtxt,
const double _dimgap,
const double _arrowSize) :
dimscale( _dimscale),
dimexo( _dimexo * _dimscale),
dimexe( _dimexe * _dimscale),
dimtxt( _dimtxt * _dimscale),
dimgap( _dimgap * _dimscale),
arrowSize( _arrowSize * _dimscale)
{
}
LC_DimAngularVars::LC_DimAngularVars(const LC_DimAngularVars& av) :
dimscale( av.dimscale),
dimexo( av.dimexo),
dimexe( av.dimexe),
dimtxt( av.dimtxt),
dimgap( av.dimgap),
arrowSize( av.arrowSize)
{
}
/**
* Constructor.
*
* @para parent Parent Entity Container.
* @para d Common dimension geometrical data.
* @para ed Extended geometrical data for angular dimension.
*/
RS_DimAngular::RS_DimAngular(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimAngularData& ed) :
RS_Dimension( parent, d),
edata( ed)
{
calcDimension();
calculateBorders();
}
RS_Entity* RS_DimAngular::clone() const
{
RS_DimAngular *d {new RS_DimAngular(*this)};
d->setOwner( isOwner());
d->initId();
d->detach();
return d;
}
/**
* @return Automatically created label for the default
* measurement of this dimension.
*/
QString RS_DimAngular::getMeasuredLabel()
{
int dimaunit {getGraphicVariableInt( QStringLiteral( "$DIMAUNIT"), 0)};
int dimadec {getGraphicVariableInt( QStringLiteral( "$DIMADEC"), 0)};
int dimazin {getGraphicVariableInt( QStringLiteral( "$DIMAZIN"), 0)};
RS2::AngleFormat format {RS_Units::numberToAngleFormat( dimaunit)};
QString strLabel( RS_Units::formatAngle( dimAngle, format, dimadec));
if (RS2::DegreesMinutesSeconds != format
&& RS2::Surveyors != format) {
strLabel = stripZerosAngle( strLabel, dimazin);
}
//verify if units are decimal and comma separator
if (RS2::DegreesMinutesSeconds != dimaunit) {
if (',' == getGraphicVariableInt( QStringLiteral( "$DIMDSEP"), 0)) {
strLabel.replace( QChar('.'), QChar(','));
}
}
return strLabel;
}
/**
* @return Center of the measured dimension.
*/
RS_Vector RS_DimAngular::getCenter() const
{
return dimCenter;
}
/**
* @brief Add an extension line if necessary
*
* @param dimLine dimension definition line including extension offset
* @param dimPoint point where the arc meets the definition line
* @param dirStart unit vector defining the lines starting point direction
* @param dirEnd unit vector defining the lines ending point direction
* @param av DXF variables with offset and extension line length
* @param pen pen to draw the extension line
*/
void RS_DimAngular::extensionLine(const RS_ConstructionLine& dimLine,
const RS_Vector& dimPoint,
const RS_Vector& dirStart,
const RS_Vector& dirEnd,
const LC_DimAngularVars& av,
const RS_Pen& pen)
{
double diffLine {RS_Vector::posInLine( dimLine.getStartpoint(), dimLine.getEndpoint(), dimPoint)};
double diffCenter {RS_Vector::posInLine( dimLine.getStartpoint(), dimCenter, dimPoint)};
if( 0.0 <= diffLine && 1.0 >= diffLine) {
// dimension ends on entity, nothing to extend
return;
}
if( 0.0 > diffLine && 0.0 > diffCenter) {
RS_Line* line {new RS_Line( this,
dimLine.getStartpoint(),
dimPoint - dirStart * av.exe())};
line->setPen( pen);
line->setLayer( nullptr);
addEntity( line);
}
else if( 1.0 < diffLine && 0.0 < diffCenter) {
RS_Line* line {new RS_Line( this,
dimLine.getEndpoint(),
dimPoint - dirEnd * av.exe())};
line->setPen( pen);
line->setLayer( nullptr);
addEntity( line);
}
else if( 0.0 > diffLine && 1.0 < diffCenter) {
RS_Line* line {new RS_Line( this,
dimCenter - dirStart * av.exo(),
dimPoint + dirEnd * av.exe())};
line->setPen( pen);
line->setLayer( nullptr);
addEntity( line);
}
}
/**
* @brief Add an arrow to the dimension arc
*
* @param point arc endpoint, the arrow tip
* @param angle the angle from center to the arc endpoint
* @param direction this holds the sign for the arrow endpoint direction
* @param outsideArrow when the arc becomes too small, arrows are placed outside
* @param av DXF variables with offset and extension line length
* @param pen pen to draw the extension line
*/
void RS_DimAngular::arrow(const RS_Vector& point,
const double angle,
const double direction,
const bool outsideArrows,
const LC_DimAngularVars& av,
const RS_Pen& pen)
{
if (RS_TOLERANCE_ANGLE >= av.arrow()) {
// arrow size is 0, no need to add an arrow
return;
}
double arrowAngle {0.0};
if (outsideArrows) {
// for outside arrows use tangent angle on endpoints
// because for small radius the arrows looked inclined
arrowAngle = angle + std::copysign( M_PI_2, direction);
}
else {
// compute the angle from center to the endpoint of the arrow on the arc
double endAngle {0.0};
if (RS_TOLERANCE_ANGLE < dimRadius) {
endAngle = av.arrow() / dimRadius;
}
// compute the endpoint of the arrow on the arc
RS_Vector arrowEnd;
arrowEnd.setPolar( dimRadius, angle + std::copysign( endAngle, direction));
arrowEnd += dimCenter;
arrowAngle = arrowEnd.angleTo( point);
}
RS_SolidData sd;
RS_Solid* arrow;
arrow = new RS_Solid( this, sd);
arrow->shapeArrow( point, arrowAngle, av.arrow());
arrow->setPen( pen);
arrow->setLayer( nullptr);
addEntity( arrow);
}
/**
* Updates the sub entities of this dimension. Called when the
* dimension or the position, alignment, .. changes.
*
* @param autoText Automatically reposition the text label
*/
void RS_DimAngular::updateDim(bool autoText /*= false*/)
{
Q_UNUSED( autoText)
RS_DEBUG->print("RS_DimAngular::update");
clear();
if (isUndone()) {
return;
}
if ( ! dimCenter.valid) {
return;
}
LC_DimAngularVars av( getGeneralScale(),
getExtensionLineOffset(),
getExtensionLineExtension(),
getTextHeight(),
getDimensionLineGap(),
getArrowSize());
// create new lines with offsets for extension lines
RS_ConstructionLine line1( nullptr,
RS_ConstructionLineData( dimLine1.getStartpoint() - dimDir1s * av.exo(),
dimLine1.getEndpoint() - dimDir1e * av.exo()));
RS_ConstructionLine line2( nullptr,
RS_ConstructionLineData( dimLine2.getStartpoint() - dimDir2s * av.exo(),
dimLine2.getEndpoint() - dimDir2e * av.exo()));
RS_Vector p1 {dimCenter + dimDir1e * dimRadius};
RS_Vector p2 {dimCenter + dimDir2e * dimRadius};
RS_Pen pen( getExtensionLineColor(), getExtensionLineWidth(), RS2::LineByBlock);
extensionLine( line1, p1, dimDir1s, dimDir1e, av, pen);
extensionLine( line2, p2, dimDir2s, dimDir2e, av, pen);
// Create dimension line (arc)
RS_Arc* arc {new RS_Arc( this, RS_ArcData( dimCenter, dimRadius, dimAngleL1, dimAngleL2, false))};
pen.setWidth( getDimensionLineWidth());
pen.setColor( getDimensionLineColor());
arc->setPen( pen);
arc->setLayer( nullptr);
addEntity( arc);
// do we have to put the arrows outside of the arc?
bool outsideArrows {arc->getLength() < 3.0 * av.arrow()};
arrow( p1, dimAngleL1, +1.0, outsideArrows, av, pen);
arrow( p2, dimAngleL2, -1.0, outsideArrows, av, pen);
// text label
RS_MTextData textData;
RS_Vector textPos {arc->getMiddlePoint()};
RS_Vector distV;
double textAngle {0.0};
double angle1 {textPos.angleTo( dimCenter) - M_PI_2};
// rotate text so it's readable from the bottom or right (ISO)
// quadrant 1 & 4
if (angle1 > M_PI_2 * 3.0 + 0.001
|| angle1 < M_PI_2 + 0.001) {
distV.setPolar( av.gap(), angle1 + M_PI_2);
textAngle = angle1;
}
// quadrant 2 & 3
else {
distV.setPolar( av.gap(), angle1 - M_PI_2);
textAngle = angle1 + M_PI;
}
// move text away from dimension line:
textPos += distV;
textData = RS_MTextData( textPos,
av.txt(), 30.0,
RS_MTextData::VABottom,
RS_MTextData::HACenter,
RS_MTextData::LeftToRight,
RS_MTextData::Exact,
1.0,
getLabel(),
getTextStyle(),
textAngle);
RS_MText* text {new RS_MText( this, textData)};
// move text to the side:
text->setPen( RS_Pen( getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
text->setLayer( nullptr);
addEntity( text);
calculateBorders();
}
void RS_DimAngular::update()
{
calcDimension();
RS_Dimension::update();
}
void RS_DimAngular::move(const RS_Vector& offset)
{
RS_Dimension::move( offset);
edata.definitionPoint1.move( offset);
edata.definitionPoint2.move( offset);
edata.definitionPoint3.move( offset);
edata.definitionPoint4.move( offset);
update();
}
void RS_DimAngular::rotate(const RS_Vector& center, const double& angle)
{
rotate( center, RS_Vector( angle));
}
void RS_DimAngular::rotate(const RS_Vector& center, const RS_Vector& angleVector)
{
RS_Dimension::rotate( center, angleVector);
edata.definitionPoint1.rotate( center, angleVector);
edata.definitionPoint2.rotate( center, angleVector);
edata.definitionPoint3.rotate( center, angleVector);
edata.definitionPoint4.rotate( center, angleVector);
update();
}
void RS_DimAngular::scale(const RS_Vector& center, const RS_Vector& factor)
{
RS_Dimension::scale( center, factor);
edata.definitionPoint1.scale( center, factor);
edata.definitionPoint2.scale( center, factor);
edata.definitionPoint3.scale( center, factor);
edata.definitionPoint4.scale( center, factor);
update();
}
void RS_DimAngular::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2)
{
RS_Dimension::mirror( axisPoint1, axisPoint2);
edata.definitionPoint1.mirror( axisPoint1, axisPoint2);
edata.definitionPoint2.mirror( axisPoint1, axisPoint2);
edata.definitionPoint3.mirror( axisPoint1, axisPoint2);
edata.definitionPoint4.mirror( axisPoint1, axisPoint2);
update();
}
/**
* @brief Compute all static values for dimension.
*
* From DXF reference the lines are P2-P1 and P-P3.
* The dimension is drawn from line1 (P2-P1) to line2 (P-P3) in CCW direction.
*/
void RS_DimAngular::calcDimension(void)
{
// get unit vectors for definition points
dimDir1s = RS_Vector::polar( 1.0, RS_Math::correctAngle( edata.definitionPoint2.angleTo( edata.definitionPoint1)));
dimDir1e = RS_Vector::polar( 1.0, RS_Math::correctAngle( edata.definitionPoint1.angleTo( edata.definitionPoint2)));
dimDir2s = RS_Vector::polar( 1.0, RS_Math::correctAngle( data.definitionPoint.angleTo( edata.definitionPoint3)));
dimDir2e = RS_Vector::polar( 1.0, RS_Math::correctAngle( edata.definitionPoint3.angleTo( data.definitionPoint)));
// create the two dimension definition lines
dimLine1 = RS_ConstructionLine( nullptr,
RS_ConstructionLineData( edata.definitionPoint2,
edata.definitionPoint1));
dimLine2 = RS_ConstructionLine( nullptr,
RS_ConstructionLineData( data.definitionPoint,
edata.definitionPoint3));
RS_VectorSolutions vs {RS_Information::getIntersection( &dimLine1, &dimLine2, false)};
dimCenter = vs.get(0);
dimRadius = dimCenter.distanceTo( edata.definitionPoint4);
dimDirRad = RS_Vector::polar( 1.0, RS_Math::correctAngle( dimCenter.angleTo( edata.definitionPoint4)));
fixDimension();
dimAngleL1 = dimLine1.getDirection2();
dimAngleL2 = dimLine2.getDirection2();
dimAngle = RS_Math::correctAngle( dimLine2.getDirection1() - dimLine1.getDirection1());
}
/**
* @brief check the dimension and fix non conform values from foreign CAD systems
*
* check if the radius definition point is on the arc,
* from line1 to line2 in counter clockwise direction
* LibreCAD takes care on correct orientation and line order in RS_ActionDimAngular
* but angular dimensions, created in other CAD software, may fail and must be fixed here
*/
void RS_DimAngular::fixDimension(void)
{
if( ! RS_Math::isAngleBetween( dimDirRad.angle(), dimDir2s.angle(), dimDir1s.angle(), false)) {
double distance0 {data.definitionPoint.distanceTo( dimCenter)};
double distance1 {edata.definitionPoint1.distanceTo( dimCenter)};
double distance2 {edata.definitionPoint2.distanceTo( dimCenter)};
double distance3 {edata.definitionPoint3.distanceTo( dimCenter)};
double angle0 {0.0};
double angle1 {0.0};
double angle2 {0.0};
double angle3 {0.0};
if( RS_TOLERANCE >= distance0) {
angle3 = (edata.definitionPoint3 - dimCenter).angle();
angle0 = angle3;
}
else if( RS_TOLERANCE >= distance3) {
angle0 = (data.definitionPoint - dimCenter).angle();
angle3 = angle0;
}
else {
angle0 = (data.definitionPoint - dimCenter).angle();
angle3 = (edata.definitionPoint3 - dimCenter).angle();
}
if( RS_TOLERANCE >= distance1) {
angle2 = (edata.definitionPoint2- dimCenter).angle();
angle1 = angle2;
}
else if( RS_TOLERANCE >= distance2) {
angle1 = (edata.definitionPoint1 - dimCenter).angle();
angle2 = angle1;
}
else {
angle1 = (edata.definitionPoint1 - dimCenter).angle();
angle2 = (edata.definitionPoint2 - dimCenter).angle();
}
if( angle2 == angle1
&& distance2 < distance1
&& angle0 == angle3
&& distance0 < distance3) {
// revert both lines
dimLine1 = RS_ConstructionLine( nullptr,
RS_ConstructionLineData( dimLine1.getEndpoint(),
dimLine1.getStartpoint()));
dimLine2 = RS_ConstructionLine( nullptr,
RS_ConstructionLineData( dimLine2.getEndpoint(),
dimLine2.getStartpoint()));
// and their unit vectors
RS_Vector swapDir {dimDir1s};
dimDir1s = dimDir1e;
dimDir1e = swapDir;
swapDir = dimDir2s;
dimDir2s = dimDir2e;
dimDir2e = swapDir;
}
// check again, as the previous revert may have made this condition false
if( ! RS_Math::isAngleBetween( dimDirRad.angle(), dimDir2s.angle(), dimDir1s.angle(), false)) {
// swap the lines
RS_ConstructionLine swapLine {dimLine1};
dimLine1 = dimLine2;
dimLine2 = swapLine;
// and their unit vectors
RS_Vector swapDir {dimDir1s};
dimDir1s = dimDir2s;
dimDir2s = swapDir;
swapDir = dimDir1e;
dimDir1e = dimDir2e;
dimDir2e = swapDir;
}
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_DimAngular& d)
{
os << " DimAngular: "
<< d.getData() << std::endl
<< d.getEData() << std::endl;
return os;
}
std::ostream& operator << (std::ostream& os, const RS_DimAngularData& dd)
{
os << "(" << dd.definitionPoint1
<< "," << dd.definitionPoint2
<< "," << dd.definitionPoint3
<< "," << dd.definitionPoint3
<< ")";
return os;
}