This commit is contained in:
Chenwenxuan
2024-03-06 14:54:30 +08:00
commit edac2715f0
1525 changed files with 809982 additions and 0 deletions

202
lib/engine/lc_hyperbola.cpp Normal file
View File

@@ -0,0 +1,202 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "lc_hyperbola.h"
#include "rs_graphic.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
#include "rs_information.h"
#include "rs_linetypepattern.h"
#include "lc_quadratic.h"
LC_HyperbolaData::LC_HyperbolaData(const RS_Vector& _center,
const RS_Vector& _majorP,
double _ratio,
double _angle1, double _angle2,
bool _reversed):
center(_center)
,majorP(_majorP)
,ratio(_ratio)
,angle1(_angle1)
,angle2(_angle2)
,reversed(_reversed)
{
}
std::ostream& operator << (std::ostream& os, const LC_HyperbolaData& ed) {
os << "(" << ed.center <<
"/" << ed.majorP <<
" " << ed.ratio <<
" " << ed.angle1 <<
"," << ed.angle2 <<
")";
return os;
}
#ifdef EMU_C99
#include "emu_c99.h" /* C99 math */
#endif
/**
* Constructor.
*/
LC_Hyperbola::LC_Hyperbola(RS_EntityContainer* parent,
const LC_HyperbolaData& d)
:RS_AtomicEntity(parent)
,data(d)
,m_bValid(true)
{
if(data.majorP.squared()<RS_TOLERANCE2) {
m_bValid=false;
return;
}
//calculateEndpoints();
calculateBorders();
}
/** create data based on foci and a point on hyperbola */
LC_HyperbolaData::LC_HyperbolaData(const RS_Vector& focus0,
const RS_Vector& focus1,
const RS_Vector& point):
center((focus0+focus1)*0.5)
{
double ds0=focus0.distanceTo(point);
ds0 -= focus1.distanceTo(point);
majorP= (ds0>0.)?focus0-center:focus1-center;
double dc=focus0.distanceTo(focus1);
double dd=fabs(ds0);
//no hyperbola for middle equidistant
if(dc<RS_TOLERANCE||dd<RS_TOLERANCE) {
majorP.set(0.,0.);
return;
}
ratio= dc/dd;
majorP /= ratio;
ratio=sqrt(ratio*ratio - 1.);
}
///** create data based on foci and a point on hyperbola */
//LC_Hyperbola::LC_Hyperbola(const RS_Vector& focus0,
// const RS_Vector& focus1,
// const RS_Vector& point):
// data(focus0,focus1,point)
//{
// m_bValid = data.majorP.squared()> RS_TOLERANCE2;
//}
/**
* Recalculates the endpoints using the angles and the radius.
*/
/*
void LC_Hyperbola::calculateEndpoints() {
double angle = data.majorP.angle();
double radius1 = getMajorRadius();
double radius2 = getMinorRadius();
startpoint.set(data.center.x + cos(data.angle1) * radius1,
data.center.y + sin(data.angle1) * radius2);
startpoint.rotate(data.center, angle);
endpoint.set(data.center.x + cos(data.angle2) * radius1,
data.center.y + sin(data.angle2) * radius2);
endpoint.rotate(data.center, angle);
}
*/
/**
* Calculates the boundary box of this ellipse.
*/
RS_Entity* LC_Hyperbola::clone() const {
LC_Hyperbola* e = new LC_Hyperbola(*this);
e->initId();
return e;
}
/**
* return the foci of ellipse
*
*@Author: Dongxu Li
*/
RS_VectorSolutions LC_Hyperbola::getFoci() const {
RS_Vector vp(getMajorP()*sqrt(1.-getRatio()*getRatio()));
return RS_VectorSolutions({getCenter()+vp, getCenter()-vp});
}
RS_VectorSolutions LC_Hyperbola::getRefPoints() const{
RS_VectorSolutions ret({data.center});
ret.push_back(getFoci());
return ret;
}
bool LC_Hyperbola::isPointOnEntity(const RS_Vector& coord,
double tolerance) const
{
double a=data.majorP.magnitude();
double b=a*data.ratio;
if(fabs(a)<tolerance || fabs(b)<tolerance) return false;
RS_Vector vp(coord - data.center);
vp=vp.rotate(-data.majorP.angle());
return fabs( vp.x*vp.x/(a*a)- vp.y*vp.y/(b*b) -1.)<tolerance;
}
LC_Quadratic LC_Hyperbola::getQuadratic() const
{
std::vector<double> ce(6,0.);
ce[0]=data.majorP.squared();
ce[2]=-data.ratio*data.ratio*ce[0];
if(ce[0]>RS_TOLERANCE2) ce[0]=1./ce[0];
if(fabs(ce[2])>RS_TOLERANCE2) ce[2]=1./ce[2];
ce[5]=-1.;
LC_Quadratic ret(ce);
if(ce[0]<RS_TOLERANCE2 || fabs(ce[2])<RS_TOLERANCE2) {
ret.setValid(false);
return ret;
}
ret.rotate(data.majorP.angle());
ret.move(data.center);
return ret;
}
//RS_Vector LC_Hyperbola::getNearestEndpoint(const RS_Vector& /*coord*/,
// double* /*dist*/ = NULL) const
//{
//}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const LC_Hyperbola& a) {
os << " Hyperbola: " << a.data << "\n";
return os;
}

262
lib/engine/lc_hyperbola.h Normal file
View File

@@ -0,0 +1,262 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2011-2012 Dongxu Li (dongxuli2011@gmail.com)
Copyright (C) 2012 Dongxu Li (dongxuli2011@gmail.com)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
**********************************************************************/
#ifndef LC_HYPERBOLA_H
#define LC_HYPERBOLA_H
#include "rs_atomicentity.h"
class RS_Circle;
class LC_Quadratic;
/**
* Holds the data that defines one branch of a hyperbola.
* majorP is the vector from center to the vertex
* ratio is the ratio between semi-major and semi-minor axis
*/
struct LC_HyperbolaData {
LC_HyperbolaData() = default;
LC_HyperbolaData(const RS_Vector& center,
const RS_Vector& majorP,
double ratio,
double angle1, double angle2,
bool reversed);
~LC_HyperbolaData() = default;
/** create data based on foci and a point on hyperbola */
LC_HyperbolaData(const RS_Vector& focus0,
const RS_Vector& focus1,
const RS_Vector& point);
//! Hyperbola center
RS_Vector center;
//! Endpoint of major axis relative to center.
RS_Vector majorP;
//! Ratio of minor axis to major axis.
double ratio;
//! Start angle
double angle1;
//! End angle
double angle2;
//! Reversed (cw) flag
bool reversed;
};
std::ostream& operator << (std::ostream& os, const LC_HyperbolaData& ed);
/**
* Class for an hyperbola entity.
*
* @author Dongxu Li
*/
class LC_Hyperbola : public RS_AtomicEntity {
public:
LC_Hyperbola() = default;
LC_Hyperbola(RS_EntityContainer* parent,
const LC_HyperbolaData& d);
/** create data based on foci and a point on hyperbola */
LC_Hyperbola(const RS_Vector& focus0,
const RS_Vector& focus1,
const RS_Vector& point);
RS_Entity* clone() const override;
/** @return RS2::EntityHyperbola */
RS2::EntityType rtti() const override{
return RS2::EntityHyperbola;
}
bool isValid() const{
return m_bValid;
}
// double getLength() const;
// /**
// //Hyperbola must have ratio<1, and not reversed
// *@ x1, hyperbola angle
// *@ x2, hyperbola angle
// //@return the arc length between hyperbola angle x1, x2
// **/
// double getHyperbolaLength(double a1, double a2) const;
// double getHyperbolaLength(double a2) const;
/** @return Copy of data that defines the hyperbola. **/
LC_HyperbolaData getData() const {
return data;
}
RS_VectorSolutions getFoci() const;
RS_VectorSolutions getRefPoints() const override;
/**
* @retval true if the arc is reversed (clockwise),
* @retval false otherwise
*/
bool isReversed() const{
return data.reversed;
}
/** sets the reversed status. */
void setReversed(bool r){
data.reversed = r;
}
/** @return The rotation angle of this hyperbola */
double getAngle() const {
return data.majorP.angle();
}
/** @return The start angle of this arc */
double getAngle1() const {
return data.angle1;
}
/** Sets new start angle. */
void setAngle1(double a1) {
data.angle1 = a1;
}
/** @return The end angle of this arc */
double getAngle2() const {
return data.angle2;
}
/** Sets new end angle. */
void setAngle2(double a2) {
data.angle2 = a2;
}
/** @return The center point (x) of this arc */
RS_Vector getCenter() const override{
return data.center;
}
/** Sets new center. */
void setCenter(const RS_Vector& c) {
data.center = c;
}
/** @return The endpoint of the major axis (relative to center). */
RS_Vector getMajorP() const {
return data.majorP;
}
/** Sets new major point (relative to center). */
void setMajorP(const RS_Vector& p) {
data.majorP = p;
}
/** @return The ratio of minor to major axis */
double getRatio() const {
return data.ratio;
}
/** Sets new ratio. */
void setRatio(double r) {
data.ratio = r;
}
/** @return The major radius of this hyperbola. Same as getRadius() */
double getMajorRadius() const {
return data.majorP.magnitude();
}
/** @return The minor radius of this hyperbola */
double getMinorRadius() const {
return data.majorP.magnitude()*data.ratio;
}
void calculateBorders() override{}
RS_Vector getMiddlePoint(void)const override{return RS_Vector(false);}
RS_Vector getNearestEndpoint(const RS_Vector& /*coord*/,
double*/* dist = NULL*/) const override
{return RS_Vector(false);}
RS_Vector getNearestPointOnEntity(const RS_Vector& /*coord*/,
bool /*onEntity = true*/, double*/* dist = NULL*/, RS_Entity**/* entity=NULL*/) const override
{return RS_Vector(false);}
RS_Vector getNearestCenter(const RS_Vector& /*coord*/,
double*/* dist = NULL*/) const override
{return RS_Vector(false);}
RS_Vector getNearestMiddle(const RS_Vector& /*coord*/,
double*/* dist = NULL*/,
int/* middlePoints = 1*/
)const override
{return RS_Vector(false);}
RS_Vector getNearestDist(double /*distance*/,
const RS_Vector&/* coord*/,
double*/* dist = NULL*/) const override
{return RS_Vector(false);}
RS_Vector getNearestOrthTan(const RS_Vector& /*coord*/,
const RS_Line& /*normal*/,
bool /*onEntity = false*/) const override
{return RS_Vector(false);}
double getDistanceToPoint(const RS_Vector& /*coord*/,
RS_Entity** /*entity=NULL*/,
RS2::ResolveLevel/* level=RS2::ResolveNone*/,
double /*solidDist = RS_MAXDOUBLE*/) const override
{return RS_MAXDOUBLE;}
bool isPointOnEntity(const RS_Vector& /*coord*/,
double /*tolerance=RS_TOLERANCE*/) const override;
void move(const RS_Vector& /*offset*/) override{}
void rotate(const double& /*angle*/) {}
void rotate(const RS_Vector& /*angleVector*/){}
void rotate(const RS_Vector& /*center*/, const double& /*angle*/) override{}
void rotate(const RS_Vector& /*center*/, const RS_Vector& /*angle*/)override{}
void scale(const RS_Vector& /*center*/, const RS_Vector& /*factor*/)override{}
void mirror(const RS_Vector& /*axisPoint1*/, const RS_Vector& /*axisPoint2*/)override{}
void moveRef(const RS_Vector& /*ref*/, const RS_Vector& /*offset*/)override{}
void draw(RS_Painter* /*painter*/, RS_GraphicView* /*view*/, double& /*patternOffset*/)override{}
friend std::ostream& operator << (std::ostream& os, const LC_Hyperbola& a);
//void calculateEndpoints();
// void calculateBorders();
//direction of tangent at endpoints
double getDirection1() const override{return 0.;}
double getDirection2() const override{return 0.;}
/** return the equation of the entity
for quadratic,
return a vector contains:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
LC_Quadratic getQuadratic() const override;
protected:
LC_HyperbolaData data;
bool m_bValid;
};
#endif
//EOF

309
lib/engine/lc_rect.cpp Normal file
View File

@@ -0,0 +1,309 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2015 Dongxu Li (dongxuli2011@gmail.com)
** Copyright (C) 2015 librecad.org (www.librecad.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
**********************************************************************/
#include<iostream>
#include <QDebug>
#include <cassert>
#include "lc_rect.h"
#define INTERT_TEST(s) qDebug()<<"\ntesting " #s; \
assert(s); \
qDebug()<<"Passed";
using namespace lc::geo;
Coordinate LC_Rect::Vector(Coordinate const& p, Coordinate const& q) {
return {q.x - p.x, q.y - p.y};
}
/**
* Create a new Area. The coordinates coordA and coordB will be ordered so that minP will always be < maxP
* The coordinates are not allowed to describe a volume
*
* @param CoordA First coordinate of an area
* @param CoordB Second coordinate of an area
*/
LC_Rect::Area(const Coordinate& coordA, const Coordinate& coordB) :
_minP{std::min(coordA.x, coordB.x), std::min(coordA.y, coordB.y)},
_maxP{std::max(coordA.x, coordB.x), std::max(coordA.y, coordB.y)}
{
}
LC_Rect::Area() : _minP{0., 0.}, _maxP{0., 0.} {}
/**
* @brief Area
* given at a coordinate with a given width and height
* @param coordA
* @param width
* @param height
*/
LC_Rect::Area(const Coordinate& coord, double width, double height):
Area(coord, {coord.x + width, coord.y + height})
{}
/**
* Return the smallest corner (closest to (0,0,0) )
*/
const Coordinate& LC_Rect::minP() const {
return _minP;
}
/**
* Return the highest corner
*/
const Coordinate& LC_Rect::maxP() const {
return _maxP;
}
/**
* @brief topLeftCorner return the upperLeftCorner coordinates
* _minP is considered lowerLeft, _maxP is the upperRight
* @return {_minP.x, _maxP.y}
*/
Coordinate LC_Rect::upperLeftCorner() const {
return {_minP.x, _maxP.y};
}
Coordinate LC_Rect::upperRightCorner() const {
return _maxP;
}
/**
* @brief lowerRightCorner return the lowerRight coordinates
* _minP is considered lowerLeft, _maxP is the upperRight
* @return {_maxP.x, _minP.y}
*/
Coordinate LC_Rect::lowerRightCorner() const {
return {_maxP.x, _minP.y};
}
Coordinate LC_Rect::lowerLeftCorner() const {
return _minP;
}
/**
* @brief width
* Returns the wid th of this area
* @return
*/
double LC_Rect::width() const {
return _maxP.x - _minP.x;
}
/**
* @brief height
* Returns the height f this area
* @return
*/
double LC_Rect::height() const {
return _maxP.y - _minP.y;
}
/**
* @brief Test of a specific point lies within an area
* @param point Point to test against
* @return boolean true of the point is within the area
*/
bool LC_Rect::inArea(const Coordinate& point, double tolerance) const {
return (point.x >= _minP.x - tolerance && point.x <= _maxP.x + tolerance && point.y >= _minP.y - tolerance && point.y <= _maxP.y + tolerance);
}
/**
* @brief inArea
* test if this object's fit's fully in area
* @param area
* @return
*/
bool LC_Rect::inArea(const Area& area) const {
return _minP.x >= area._minP.x && _minP.y >= area._minP.y && _maxP.x <= area._maxP.x && _maxP.y <= area._maxP.y;
}
/**
* @brief overlaps
* returns true if any overlap is happening between the two area's, even if otherArea fit's within this area
* @param other
* @return
*/
bool LC_Rect::overlaps(const Area& otherArea) const {
return intersects(otherArea);
}
/**
* @brief numCornersInside
* count the number of corners this object has in otherArea
* @param other
* @return
*/
short LC_Rect::numCornersInside(const Area& otherArea) const {
short pointsInside = 0;
if (otherArea.inArea(_minP)) {
pointsInside++;
}
if (otherArea.inArea(_maxP)) {
pointsInside++;
}
if (otherArea.inArea(upperLeftCorner())) {
pointsInside++;
}
if (otherArea.inArea(lowerRightCorner())) {
pointsInside++;
}
return pointsInside;
}
/**
* @brief merge
* two area's and expand if required to largest containing area
* @param other
* @return
*/
Area LC_Rect::merge(const Area& other) const {
return {
{std::min(other.minP().x, this->minP().x), std::min(other.minP().y, this->minP().y)},
{std::max(other.maxP().x, this->maxP().x), std::max(other.maxP().y, this->maxP().y)}
};
}
/**
* @brief merge
* two area's and expand if required to largest containing area
* @param other
* @return
*/
Area LC_Rect::merge(const Coordinate& other) const {
return {
{std::min(other.x, this->minP().x), std::min(other.y, this->minP().y)},
{std::max(other.x, this->maxP().x), std::max(other.y, this->maxP().y)}
};
}
/**
* @brief merge
* two area's and expand if required to largest containing area
* @param other
* @param tolerance, tolerance to detect zero size intersection
* @return
*/
Area LC_Rect::intersection(const Area& other, double tolerance) const {
Area const ret{
{std::max(other.minP().x, this->minP().x), std::max(other.minP().y, this->minP().y)},
{std::min(other.maxP().x, this->maxP().x), std::min(other.maxP().y, this->maxP().y)}
};
if (ret.width() < tolerance || ret.height() < tolerance) {
return {};
}
return ret;
}
bool LC_Rect::intersects(Area const& rhs, double tolerance) const {
return maxP().x + tolerance >= rhs.minP().x &&
maxP().y + tolerance >= rhs.minP().y &&
rhs.maxP().x + tolerance >= minP().x &&
rhs.maxP().y + tolerance >= minP().y;
}
/**
* @brief top
* vector of this area
* @return
*/
Coordinate LC_Rect::top() const {
return Vector(upperLeftCorner(), _maxP);
}
/**
* @brief bottom
* vector of this area
* @return
*/
Coordinate LC_Rect::bottom() const {
return Vector(_minP, lowerRightCorner());
}
/**
* @brief left
* vector for this area
* @return
*/
Coordinate LC_Rect::left() const {
return Vector(_minP, upperLeftCorner());
}
/**
* @brief right
* vector of this area
* @return
*/
Coordinate LC_Rect::right() const {
return Vector(lowerRightCorner(), _maxP);
}
/**
* Increase the area on each side by increaseBy
*/
Area LC_Rect::increaseBy(double increaseBy) const {
return {_minP - increaseBy, _maxP + increaseBy};
}
std::array<Coordinate, 4> LC_Rect::vertices() const
{
return {{lowerLeftCorner(), lowerRightCorner(),
upperRightCorner(), upperLeftCorner()}};
}
std::ostream& operator<<(std::ostream& os, const Area& area) {
os << "Area(" << area.minP() << " " << area.maxP() << ")";
return os;
}
void LC_Rect::unitTest() {
LC_Rect const rect0{{0., 0.}, {1., 1.}};
LC_Rect const rect1{{0.5, 0.5}, {1.5, 1.5}};
LC_Rect const rect2{{1.5, 1.5}, {2.5, 2.5}};
LC_Rect const rect3{{0.0, 1.}, {1.5, 1.5}};
//intersects() tests
INTERT_TEST(rect0.intersects(rect1))
INTERT_TEST(rect1.intersects(rect0))
INTERT_TEST(rect1.intersects(rect2))
INTERT_TEST(rect2.intersects(rect1))
INTERT_TEST(!rect0.intersects(rect2))
INTERT_TEST(!rect2.intersects(rect0))
INTERT_TEST(rect2.intersects(rect3))
INTERT_TEST(rect3.intersects(rect2))
// inArea() tests
INTERT_TEST(rect0.inArea({0.1, 0.1}))
INTERT_TEST(rect0.inArea({0.5, 0.5}))
INTERT_TEST(!rect0.inArea({1.1, 1.1}))
INTERT_TEST(!rect0.inArea({-1.1, -1.1}))
}

217
lib/engine/lc_rect.h Normal file
View File

@@ -0,0 +1,217 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2015 Dongxu Li (dongxuli2011@gmail.com)
** Copyright (C) 2015 librecad.org (www.librecad.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
**********************************************************************/
#ifndef LC_RECT_H
#define LC_RECT_H
#include "rs_vector.h"
#include <array>
//ported from LibreCAD V3
namespace lc {
namespace geo {
typedef RS_Vector Coordinate;
/**
* Class that describes an area or window.
*/
class Area {
public:
Area();
/**
* Create a new Area. The coordinates coordA and coordB will be ordered so that minP will always be < maxP
* The coordinates are not allowed to describe a volume
*
* @param CoordA First coordinate of an area
* @param CoordB Second coordinate of an area
*/
Area(const Coordinate& coordA, const Coordinate& coordB);
/**
* @brief Area
* given at a coordinate with a given width and height
* @param coordA
* @param width
* @param height
*/
explicit Area(const Coordinate& coord, double width, double height);
/**
* Return the smallest corner (closest to (0,0,0) )
*/
const Coordinate& minP() const;
/**
* Return the highest corner
*/
const Coordinate& maxP() const;
/**
* @brief topLeftCorner return the upperLeftCorner coordinates
* _minP is considered lowerLeft, _maxP is the upperRight
* @return {_minP.x, _maxP.y}
*/
Coordinate upperLeftCorner() const;
Coordinate upperRightCorner() const;
/**
* @brief lowerRightCorner return the lowerRight coordinates
* _minP is considered lowerLeft, _maxP is the upperRight
* @return {_maxP.x, _minP.y}
*/
Coordinate lowerLeftCorner() const;
Coordinate lowerRightCorner() const;
/**
* @brief width
* Returns the width of this area
* @return
*/
double width() const;
/**
* @brief height
* Returns the height of this area
* @return
*/
double height() const;
/**
* @brief Test of a specific point lies within an area
* @param point Point to test against
* @return boolean true of the point is within the area
*/
bool inArea(const Coordinate& point, double tolerance = 0.) const;
/**
* @brief inArea
* test if this object's fit's fully in area
* @param area
* @return
*/
bool inArea(const Area& area) const;
/**
* @brief overlaps
* returns true if any overlap is happening between the two area's, even if otherArea fit's within this area
* @param other
* @return
*/
bool overlaps(const Area& otherArea) const;
/**
* @brief numCornersInside
* count the number of corners this object has in otherArea
* @param other
* @return
*/
short numCornersInside(const Area& otherArea) const;
/**
* @brief merge
* two area's and expand if required to largest containing area
* @param other
* @return
*/
Area merge(const Area& other) const;
/**
* @brief merge
* two area's and expand if required to largest containing area
* @param other
* @return
*/
Area merge(const Coordinate& other) const;
/**
* @brief merge
* two area's and expand if required to largest containing area
* @param other
* @param tolerance, tolerance to detect zero size intersection
* @return
*/
Area intersection(const Area& other, double tolerance = 0.) const;
/**
* @brief intersects whether two rectangular area
* if the closest distance between two Areas is smaller than tolerance, they
* are considered to have intersection
* @param rhs the other rect
* @return true if closest distance is smaller than or equal to tolerance
*/
bool intersects(Area const& rhs, double tolerance = 0.) const;
/**
* @brief top
* vector of this area
* @return
*/
Coordinate top() const;
/**
* @brief bottom
* vector of this area
* @return
*/
Coordinate bottom() const;
/**
* @brief left
* vector for this area
* @return
*/
Coordinate left() const;
/**
* @brief right
* vector of this area
* @return
*/
Coordinate right() const;
/**
* Increase the area on each side by increaseBy
*/
Area increaseBy (double increaseBy) const;
/**
* @brief vertices generate vertices of the rectangular area
* @return array of vertices by the order {ll, lr, ur, rl} starting with
* lower-left corner, i.e. _minP
*/
std::array<Coordinate, 4> vertices() const;
static void unitTest();
private:
static Coordinate Vector(Coordinate const& p, Coordinate const& q);
friend std::ostream& operator<<(std::ostream& os, const Area& area);
private:
Coordinate _minP;
Coordinate _maxP;
};
}
}
using LC_Rect = lc::geo::Area;
#endif // LC_RECT_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,245 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2014 Dongxu Li (dongxuli2011@gmail.com)
** Copyright (C) 2014 Pavel Krejcir (pavel@pamsoft.cz)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
**********************************************************************/
#ifndef LC_SPLINEPOINTS_H
#define LC_SPLINEPOINTS_H
#include <vector>
#include "rs_atomicentity.h"
class QPolygonF;
struct RS_LineTypePattern;
/**
* Holds the data that defines a line.
* Few notes about implementation:
* When drawing, the spline is defined via splinePoints collection.
* However, since we want to allow trimming/cutting the spline,
* we cannot guarantee that the shape would stay unchanged after
* a part of the spline would be cut off. This would espetially be
* obvious after cutting closed splines. So we introduce the "cut"
* state. After that, all splinePoints will be deleted except start
* and end points, and the controlPoints become the reference points
* of that shape. It will be further possible to modify the spline,
* but the control points will serve as handles then.
*/
struct LC_SplinePointsData
{
/**
* Default constructor. Leaves the data object uninitialized.
*/
LC_SplinePointsData() = default;
~LC_SplinePointsData() = default;
LC_SplinePointsData(bool closed, bool cut);
bool closed;
bool cut;
/** points on the spline. */
std::vector<RS_Vector> splinePoints;
std::vector<RS_Vector> controlPoints;
};
std::ostream& operator << (std::ostream& os, const LC_SplinePointsData& ld);
/**
* Class for a spline entity.
*
* @author Pavel Krejcir
*/
class LC_SplinePoints : public RS_AtomicEntity // RS_EntityContainer
{
private:
void drawPattern(RS_Painter* painter, RS_GraphicView* view,
double& patternOffset, const RS_LineTypePattern* pat);
void drawSimple(RS_Painter* painter, RS_GraphicView* view);
void UpdateControlPoints();
void UpdateQuadExtent(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2);
int GetNearestQuad(const RS_Vector& coord, double* dist, double* dt) const;
RS_Vector GetSplinePointAtDist(double dDist, int iStartSeg, double dStartT,
int *piSeg, double *pdt) const;
int GetQuadPoints(int iSeg, RS_Vector *pvStart, RS_Vector *pvControl,
RS_Vector *pvEnd) const;
bool offsetCut(const RS_Vector& coord, const double& distance);
bool offsetSpline(const RS_Vector& coord, const double& distance);
std::vector<RS_Entity*> offsetTwoSidesSpline(const double& distance) const;
std::vector<RS_Entity*> offsetTwoSidesCut(const double& distance) const;
LC_SplinePointsData data;
public:
LC_SplinePoints(RS_EntityContainer* parent, const LC_SplinePointsData& d);
RS_Entity* clone() const override;
/** @return RS2::EntitySpline */
RS2::EntityType rtti() const override;
/** @return false */
bool isEdge() const override;
/** @return Copy of data that defines the spline. */
LC_SplinePointsData const& getData() const;
LC_SplinePointsData& getData();
/** @return Number of control points. */
size_t getNumberOfControlPoints() const;
/**
* @retval true if the spline is closed.
* @retval false otherwise.
*/
bool isClosed() const;
/**
* Sets the closed flag of this spline.
*/
void setClosed(bool c);
void update() override;
RS_VectorSolutions getRefPoints() const override;
/** @return Start point of the entity */
RS_Vector getStartpoint() const override;
/** @return End point of the entity */
RS_Vector getEndpoint() const override;
/** Sets the startpoint */
//void setStartpoint(RS_Vector s) {
// data.startpoint = s;
// calculateBorders();
//}
/** Sets the endpoint */
//void setEndpoint(RS_Vector e) {
// data.endpoint = e;
// calculateBorders();
//}
double getDirection1() const override;
double getDirection2() const override;
//void moveStartpoint(const RS_Vector& pos) override;
//void moveEndpoint(const RS_Vector& pos) override;
//RS2::Ending getTrimPoint(const RS_Vector& coord,
// const RS_Vector& trimPoint);
//void reverse() override;
/** @return the center point of the line. */
//RS_Vector getMiddlePoint() {
// return (data.startpoint + data.endpoint)/2.0;
//}
//bool hasEndpointsWithinWindow(RS_Vector v1, RS_Vector v2) override;
/**
* @return The length of the line.
*/
double getLength() const override;
/**
* @return The angle of the line (from start to endpoint).
*/
//double getAngle1() {
// return data.startpoint.angleTo(data.endpoint);
//}
/**
* @return The angle of the line (from end to startpoint).
*/
//double getAngle2() {
// return data.endpoint.angleTo(data.startpoint);
//}
RS_VectorSolutions getTangentPoint(const RS_Vector& point) const override;
RS_Vector getTangentDirection(const RS_Vector& point) const override;
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr) const override;
/**
* @brief getNearestPointOnEntity
* @param coord
* @param onEntity, unused, because current implementation finds the nearest point on the spline
* @param dist
* @param entity
* @return
*/
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true, double* dist = nullptr, RS_Entity** entity = nullptr) const override;
// RS_Vector getNearestCenter(const RS_Vector& coord,
// double* dist = nullptr) const;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr, int middlePoints = 1) const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord, double* dist = nullptr) const override;
//RS_Vector getNearestRef(const RS_Vector& coord,
// double* dist = nullptr);
double getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity = nullptr, RS2::ResolveLevel level = RS2::ResolveNone,
double solidDist = RS_MAXDOUBLE) const override;
bool addPoint(const RS_Vector& v);
void removeLastPoint();
void addControlPoint(const RS_Vector& v);
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
void revertDirection() override;
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
std::vector<RS_Vector> const& getPoints() const;
std::vector<RS_Vector> const& getControlPoints() const;
std::vector<RS_Vector> getStrokePoints() const;
friend std::ostream& operator << (std::ostream& os, const LC_SplinePoints& l);
void calculateBorders() override;
bool offset(const RS_Vector& coord, const double& distance) override;
std::vector<RS_Entity*> offsetTwoSides(const double& distance) const override;
static RS_VectorSolutions getIntersection(RS_Entity const* e1, RS_Entity const* e2);
RS_VectorSolutions getLineIntersect(const RS_Vector& x1, const RS_Vector& x2);
void addQuadIntersect(RS_VectorSolutions *pVS, const RS_Vector& x1,
const RS_Vector& c1, const RS_Vector& x2);
RS_VectorSolutions getSplinePointsIntersect(LC_SplinePoints* l1);
RS_VectorSolutions getQuadraticIntersect(RS_Entity const* e1);
// we will not enable trimming, maybe in the future
//void trimStartpoint(const RS_Vector& pos) override;
//void trimEndpoint(const RS_Vector& pos) override;
LC_SplinePoints* cut(const RS_Vector& pos);
//! \{ getBoundingRect find bounding rectangle for the bezier segment
//! \param x1,c1,x2 first/center/last control points
//! \return rectangle as a polygon
static QPolygonF getBoundingRect(const RS_Vector& x1, const RS_Vector& c1, const RS_Vector& x2);
//! \}
};
#endif

View File

@@ -0,0 +1,50 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2018 A. Stebich (librecad@mail.lordofbikes.de)
**
**
** 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 "lc_undosection.h"
#include "rs_document.h"
LC_UndoSection::LC_UndoSection(RS_Document *doc, const bool handleUndo /*= true*/) :
document( doc),
valid( handleUndo && nullptr != doc)
{
if (valid) {
document->startUndoCycle();
}
}
LC_UndoSection::~LC_UndoSection()
{
if (valid) {
document->endUndoCycle();
}
}
void LC_UndoSection::addUndoable(RS_Undoable *undoable)
{
if (valid) {
document->addUndoable( undoable);
}
}

View File

@@ -0,0 +1,56 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2018 A. Stebich (librecad@mail.lordofbikes.de)
**
**
** 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!
**
**********************************************************************/
#ifndef LC_UNDOSECTION_H
#define LC_UNDOSECTION_H
class RS_Document;
class RS_Undoable;
/** \brief This class is a wrapper for RS_Undo methods
*
* It's important that calls to RS_Undo::startUndoCycle() and
* RS_Undo::endUndoCycle() are balanced.
* On instantiation the constructor calls startUndoCycle().
* The class handles also validation of the RS_Document pointer.
* When the instance is leaving the scope, the destructor
* calls endUndoCycle().
* This way the balance is guaranteed.
* It simplifies undo handling specially in RS_Creation and RS_Modification classes
*/
class LC_UndoSection
{
public:
LC_UndoSection(RS_Document * doc, const bool handleUndo = true);
~LC_UndoSection();
void addUndoable(RS_Undoable * undoable);
private:
RS_Document *document {nullptr};
bool valid {true};
};
#endif // LC_UNDOSECTION_H

51
lib/engine/rs.cpp Normal file
View File

@@ -0,0 +1,51 @@
#include<iostream>
#include<vector>
#include<utility>
#include<climits>
#include "rs.h"
RS2::LineWidth RS2::intToLineWidth(int w) {
std::vector<std::pair<int, LineWidth>> const table{
{-2, WidthDefault}, //for w = -3
{-1, WidthByBlock}, //for w = -2
{0, WidthByLayer}, //for w = -1
// for w < 3, return Width00
{3, Width00},
{8, Width01},
{12, Width02},
{14, Width03},
{17, Width04},
{19, Width05},
{23, Width06},
{28, Width07},
{33, Width08},
{38, Width09},
{46, Width10},
{52, Width11},
{57, Width12},
{66, Width13},
{76, Width14},
{86, Width15},
{96, Width16},
{104, Width17},
{114, Width18},
{131, Width19},
{150, Width20},
{180, Width21},
{206, Width22},
{INT_MAX, Width23}
};
//binary search
//assume table size is at least 2
size_t low = -1;
size_t high = table.size() - 1;
while (low + 1 < high) {
size_t const mid = low + (high - low)/2;
if (w >= table.at(mid).first)
low = mid;
else
high = mid;
}
return table.at(high).second;
}

945
lib/engine/rs.h Normal file
View File

@@ -0,0 +1,945 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_H
#define RS_H
//#define RS_TEST
// Windoze XP can't handle the original MAX/MINDOUBLE's
#define RS_MAXDOUBLE 1.0E+10
#define RS_MINDOUBLE -1.0E+10
//tolerance
#define RS_TOLERANCE 1.0e-10
//squared tolerance
#define RS_TOLERANCE15 1.5e-15
#define RS_TOLERANCE2 1.0e-20
#define RS_TOLERANCE_ANGLE 1.0e-8
/**
* Class namespace for various enums along with some simple
* wrapper methods for converting the enums to the Qt
* counterparts.
*
* @author Andrew Mustun
*/
class RS2 {
public:
/**
* Flags.
*/
enum Flags {
/** Flag for Undoables. */
FlagUndone = 1<<0,
/** Entity Visibility. */
FlagVisible = 1<<1,
/** Entity attribute (e.g. color) is defined by layer. */
FlagByLayer = 1<<2,
/** Entity attribute (e.g. color) defined by block. */
FlagByBlock = 1<<3,
/** Layer frozen. */
FlagFrozen = 1<<4,
/** Layer frozen by default. */
FlagDefFrozen = 1<<5,
/** Layer locked. */
FlagLocked = 1<<6,
/** Used for invalid pens. */
FlagInvalid = 1<<7,
/** Entity in current selection. */
FlagSelected = 1<<8,
/** Polyline closed? */
FlagClosed = 1<<9,
/** Flag for temporary entities (e.g. hatch) */
FlagTemp = 1<<10,
/** Flag for processed entities (optcontour) */
FlagProcessed = 1<<11,
/** Startpoint selected */
FlagSelected1 = 1<<12,
/** Endpoint selected */
FlagSelected2 = 1<<13,
/** Entity is highlighted temporarily (as a user action feedback) */
FlagHighlighted = 1<<14
};
/**
* Variable types used by RS_VariableDict and RS_Variable.
*/
enum VariableType {
VariableString,
VariableInt,
VariableDouble,
VariableVector,
VariableVoid
};
/**
* File types. Used by file dialogs. Note: the extension might not
* be enough to distinguish file types.
*/
enum FormatType {
FormatUnknown, /**< Unknown / unsupported format. */
FormatDXF1, /**< QCad 1 compatibility DXF format. */
FormatDXFRW, /**< DXF format. v2007. */
FormatDXFRW2004, /**< DXF format. v2004. */
FormatDXFRW2000, /**< DXF format. v2000. */
FormatDXFRW14, /**< DXF format. v14. */
FormatDXFRW12, /**< DXF format. v12. */
#ifdef DWGSUPPORT
FormatDWG, /**< DWG format. */
#endif
FormatLFF, /**< LibreCAD Font File format. */
FormatCXF, /**< CAM Expert Font format. */
FormatJWW, /**< JWW Format type */
FormatJWC /**< JWC Format type */
};
/**
* Entity types returned by the rtti() method
*/
enum EntityType {
EntityUnknown, /**< Unknown */
EntityContainer, /**< Container */
EntityBlock, /**< Block (Group definition) */
EntityFontChar, /**< Font character */
EntityInsert, /**< Insert (Group instance) */
EntityGraphic, /**< Graphic with layers */
EntityPoint, /**< Point */
EntityLine, /**< Line */
EntityPolyline, /**< Polyline */
EntityVertex, /**< Vertex (part of a polyline) */
EntityArc, /**< Arc */
EntityCircle, /**< Circle */
EntityEllipse, /**< Ellipse */
EntityHyperbola, /**< Hyperbola */
EntitySolid, /**< Solid */
EntityConstructionLine, /**< Construction line */
EntityMText, /**< Multi-line Text */
EntityText, /**< Single-line Text */
EntityDimAligned, /**< Aligned Dimension */
EntityDimLinear, /**< Linear Dimension */
EntityDimRadial, /**< Radial Dimension */
EntityDimDiametric, /**< Diametric Dimension */
EntityDimAngular, /**< Angular Dimension */
EntityDimLeader, /**< Leader Dimension */
EntityHatch, /**< Hatch */
EntityImage, /**< Image */
EntitySpline, /**< Spline */
EntitySplinePoints, /**< SplinePoints */
EntityOverlayBox, /**< OverlayBox */
EntityPreview, /**< Preview Container */
EntityPattern,
EntityOverlayLine
};
/**
* Action types used by action factories.
*/
enum ActionType {
ActionNone, /**< Invalid action id. */
ActionDefault,
ActionFileNew,
ActionFileNewTemplate,
ActionFileOpen,
ActionFileSave,
ActionFileSaveAs,
ActionFileExport,
ActionFileClose,
ActionFilePrint,
ActionFilePrintPDF,
ActionFilePrintPreview,
ActionFileExportMakerCam,
ActionFileQuit,
ActionEditKillAllActions,
ActionEditUndo,
ActionEditRedo,
ActionEditCut,
ActionEditCutNoSelect,
ActionEditCopy,
ActionEditCopyNoSelect,
ActionEditPaste,
ActionOrderNoSelect,
ActionOrderBottom,
ActionOrderLower,
ActionOrderRaise,
ActionOrderTop,
ActionViewStatusBar,
ActionViewLayerList,
ActionViewBlockList,
ActionViewCommandLine,
ActionViewLibrary,
ActionViewPenToolbar,
ActionViewOptionToolbar,
ActionViewCadToolbar,
ActionViewFileToolbar,
ActionViewEditToolbar,
ActionViewSnapToolbar,
ActionViewGrid,
ActionViewDraft,
ActionZoomIn,
ActionZoomOut,
ActionZoomAuto,
ActionZoomWindow,
ActionZoomPan,
ActionZoomRedraw,
ActionZoomPrevious,
ActionSelect,
ActionSelectSingle,
ActionSelectContour,
ActionSelectWindow,
ActionDeselectWindow,
ActionSelectAll,
ActionDeselectAll,
ActionSelectIntersected,
ActionDeselectIntersected,
ActionSelectInvert,
ActionSelectLayer,
ActionSelectDouble,
ActionGetSelect,
ActionDrawArc,
ActionDrawArc3P,
ActionDrawArcParallel,
ActionDrawArcTangential,
ActionDrawCircle,
ActionDrawCircle2P,
ActionDrawCircle2PR,
ActionDrawCircle3P,
ActionDrawCircleCR,
ActionDrawCircleParallel,
ActionDrawCircleInscribe,
ActionDrawCircleTan2_1P,
ActionDrawCircleTan1_2P,
ActionDrawCircleTan2,
ActionDrawCircleTan3,
ActionDrawEllipseArcAxis,
ActionDrawEllipseAxis,
ActionDrawEllipseFociPoint,
ActionDrawEllipse4Points,
ActionDrawEllipseCenter3Points,
ActionDrawEllipseInscribe,
ActionDrawHatch,
ActionDrawHatchNoSelect,
ActionDrawImage,
ActionDrawLine,
ActionDrawLineAngle,
ActionDrawLineBisector,
ActionDrawLineFree,
ActionDrawLineHorVert,
ActionDrawLineHorizontal,
ActionDrawLineOrthogonal,
ActionDrawLineOrthTan,
ActionDrawLineParallel,
ActionDrawLineParallelThrough,
ActionDrawLinePolygonCenCor,
ActionDrawLinePolygonCenTan,//add by txmy
ActionDrawLinePolygonCorCor,
ActionDrawLineRectangle,
ActionDrawLineRelAngle,
ActionDrawLineTangent1,
ActionDrawLineTangent2,
ActionDrawLineVertical,
ActionDrawMText,
ActionDrawPoint,
ActionDrawSpline,
ActionDrawSplinePoints, //interpolation spline
ActionDrawPolyline,
ActionDrawText,
ActionPolylineAdd,
ActionPolylineAppend,
ActionPolylineDel,
ActionPolylineDelBetween,
ActionPolylineTrim,
ActionPolylineEquidistant,
ActionPolylineSegment,
ActionDimAligned,
ActionDimLinear,
ActionDimLinearVer,
ActionDimLinearHor,
ActionDimRadial,
ActionDimDiametric,
ActionDimAngular,
ActionDimLeader,
ActionModifyAttributes,
ActionModifyAttributesNoSelect,
ActionModifyDelete,
ActionModifyDeleteNoSelect,
ActionModifyDeleteQuick,
ActionModifyDeleteFree,
ActionModifyMove,
ActionModifyMoveNoSelect,
ActionModifyRotate,
ActionModifyRotateNoSelect,
ActionModifyScale,
ActionModifyScaleNoSelect,
ActionModifyMirror,
ActionModifyMirrorNoSelect,
ActionModifyMoveRotate,
ActionModifyMoveRotateNoSelect,
ActionModifyRevertDirection,
ActionModifyRevertDirectionNoSelect,
ActionModifyRotate2,
ActionModifyRotate2NoSelect,
ActionModifyEntity,
ActionModifyTrim,
ActionModifyTrim2,
ActionModifyTrimAmount,
ActionModifyCut,
ActionModifyStretch,
ActionModifyBevel,
ActionModifyRound,
ActionModifyOffset,
ActionModifyOffsetNoSelect,
ActionSnapFree,
ActionSnapGrid,
ActionSnapEndpoint,
ActionSnapOnEntity,
ActionSnapCenter,
ActionSnapMiddle,
ActionSnapDist,
ActionSnapIntersection,
ActionSnapIntersectionManual,
ActionRestrictNothing,
ActionRestrictOrthogonal,
ActionRestrictHorizontal,
ActionRestrictVertical,
ActionSetRelativeZero,
ActionLockRelativeZero,
ActionUnlockRelativeZero,
ActionInfoInside,
ActionInfoDist,
ActionInfoDist2,
ActionInfoAngle,
ActionInfoTotalLength,
ActionInfoTotalLengthNoSelect,
ActionInfoArea,
ActionLayersDefreezeAll,
ActionLayersFreezeAll,
ActionLayersUnlockAll,
ActionLayersLockAll,
ActionLayersAdd,
ActionLayersRemove,
ActionLayersEdit,
ActionLayersToggleView,
ActionLayersToggleLock,
ActionLayersTogglePrint,
ActionLayersToggleConstruction,
ActionBlocksDefreezeAll,
ActionBlocksFreezeAll,
ActionBlocksAdd,
ActionBlocksRemove,
ActionBlocksAttributes,
ActionBlocksEdit,
ActionBlocksSave,
ActionBlocksInsert,
ActionBlocksToggleView,
ActionBlocksCreate,
ActionBlocksCreateNoSelect,
ActionBlocksExplode,
ActionBlocksExplodeNoSelect,
ActionBlocksImport,
ActionModifyExplodeText,
ActionModifyExplodeTextNoSelect,
ActionLibraryInsert,
ActionOptionsGeneral,
ActionOptionsDrawing,
ActionToolRegenerateDimensions,
ActionScriptOpenIDE,
ActionScriptRun,
/** Needed to loop through all actions */
ActionLast
};
/**
* Entity ending. Used for returning which end of an entity is meant.
*/
enum Ending {
EndingStart, /**< Start point. */
EndingEnd, /**< End point. */
EndingNone /**< Neither. */
};
/**
* Update mode for non-atomic entities that need to be updated when
* they change. e.g. texts, inserts, ...
*/
enum UpdateMode {
NoUpdate, /**< No automatic updates. */
Update, /**< Always update automatically when modified. */
PreviewUpdate /**< Update automatically but only for previews (quick update) */
};
/**
* Drawing mode.
*/
enum DrawingMode {
ModeFull, /**< Draw everything always detailed (default) */
ModeAuto, /**< Draw details when reasonable */
ModePreview, /**< Draw only in black/white without styles */
ModeBW, /**< Black/white. Can be used for printing. */
ModeWB, /**< White/black, used for export */
};
/**
* Undoable rtti.
*/
enum UndoableType {
UndoableUnknown, /**< Unknown undoable */
UndoableEntity, /**< Entity */
UndoableLayer /**< Layer */
};
/**
* Units.
*/
enum Unit {
None = 0, /**< No unit (unit from parent) */
Inch = 1, /**< Inch */
Foot = 2, /**< Foot: 12 Inches */
Mile = 3, /**< Mile: 1760 Yards = 1609 m */
Millimeter = 4, /**< Millimeter: 0.001m */
Centimeter = 5, /**< Centimeter: 0.01m */
Meter = 6, /**< Meter */
Kilometer = 7, /**< Kilometer: 1000m */
Microinch = 8, /**< Microinch: 0.000001 */
Mil = 9, /**< Mil = 0.001 Inch*/
Yard = 10, /**< Yard: 3 Feet */
Angstrom = 11, /**< Angstrom: 10^-10m */
Nanometer = 12, /**< Nanometer: 10^-9m */
Micron = 13, /**< Micron: 10^-6m */
Decimeter = 14, /**< Decimeter: 0.1m */
Decameter = 15, /**< Decameter: 10m */
Hectometer = 16, /**< Hectometer: 100m */
Gigameter = 17, /**< Gigameter: 1000000m */
Astro = 18, /**< Astro: 149.6 x 10^9m */
Lightyear = 19, /**< Lightyear: 9460731798 x 10^6m */
Parsec = 20, /**< Parsec: 30857 x 10^12 */
LastUnit = 21 /**< Used to iterate through units */
};
/**
* Format for length values.
*/
enum LinearFormat {
/** Scientific (e.g. 2.5E+05) */
Scientific,
/** Decimal (e.g. 9.5)*/
Decimal,
/** Engineering (e.g. 7' 11.5")*/
Engineering,
/** Architectural (e.g. 7'-9 1/8")*/
Architectural,
/** Fractional (e.g. 7 9 1/8) */
Fractional,
/** Metric Architectural using DIN 406 (e.g. 1.12⁵)*/
ArchitecturalMetric
};
/**
* Angle Units.
*/
enum AngleUnit {
Deg, /**< Degrees */
Rad, /**< Radians */
Gra /**< Gradians */
};
/**
* Display formats for angles.
*/
enum AngleFormat {
/** Degrees with decimal point (e.g. 24.5<EFBFBD>) */
DegreesDecimal,
/** Degrees, Minutes and Seconds (e.g. 24<32>30'5'') */
DegreesMinutesSeconds,
/** Gradians with decimal point (e.g. 390.5)*/
Gradians,
/** Radians with decimal point (e.g. 1.57)*/
Radians,
/** Surveyor's units */
Surveyors
};
/**
* Enum of levels of resolving when iterating through an entity tree.
*/
enum ResolveLevel {
/** Groups are not resolved */
ResolveNone,
/**
* Resolve all but not Inserts.
*/
ResolveAllButInserts,
/**
* Resolve all but not Text or MText.
*/
ResolveAllButTexts,
/**
* Resolve no text or images, added as a quick fix for bug#422
*/
ResolveAllButTextImage,
/**
* all Entity Containers are resolved
* (including Texts, Polylines, ...)
*/
ResolveAll
};
/**
* Direction used for scrolling actions.
*/
enum Direction {
Up, Left, Right, Down
};
enum SubWindowMode {
CurrentMode = -1, Maximized, Cascade, Tile, TileVertical, TileHorizontal
};
enum TabShape {
AnyShape = -1, Rounded, Triangular
};
enum TabPosition {
AnyPosition = -1, North, South, West, East
};
/**
* Vertical alignments.
*/
// enum VAlign {
// VAlignTop, /**< Top. */
// VAlignMiddle, /**< Middle */
// VAlignBottom /**< Bottom */
// };
/**
* Horizontal alignments.
*/
// enum HAlign {
// HAlignLeft, /**< Left */
// HAlignCenter, /**< Centered */
// HAlignRight /**< Right */
// };
/**
* Text drawing direction.
*/
// enum TextDrawingDirection {
// LeftToRight, /**< Left to right */
// TopToBottom, /**< Top to bottom */
// ByStyle /**< Inherited from associated text style */
// };
/**
* Line spacing style for texts.
*/
// enum TextLineSpacingStyle {
// AtLeast, /**< Taller characters will override */
// Exact /**< Taller characters will not override */
// };
/**
* Leader path type.
*/
enum LeaderPathType {
Straight, /**< Straight line segments */
Spline /**< Splines */
};
/**
* Direction for zooming actions.
*/
enum ZoomDirection {
In, Out
};
/**
* Axis specification for zooming actions.
*/
enum Axis {
OnlyX, OnlyY, Both
};
/**
* Crosshair type
*/
enum CrosshairType {
LeftCrosshair, /**< Left type isometric Crosshair */
TopCrosshair, /**< Top type isometric Crosshair */
RightCrosshair, /**< Right type isometric Crosshair */
OrthogonalCrosshair /**< Orthogonal Crosshair */
};
/**
* Snapping modes
*/
enum SnapMode {
SnapFree, /**< Free positioning */
SnapGrid, /**< Snap to grid points */
SnapEndpoint, /**< Snap to endpoints */
SnapMiddle, /**< Snap to middle points */
SnapCenter, /**< Snap to centers */
SnapOnEntity, /**< Snap to the next point on an entity */
SnapDist, /**< Snap to points with a distance to an endpoint */
SnapIntersection, /**< Snap to intersection */
SnapIntersectionManual /**< Snap to intersection manually */
};
/**
* Snap restrictions
*/
enum SnapRestriction {
RestrictNothing, /**< No restriction to snap mode */
RestrictHorizontal, /**< Restrict to 0,180 degrees */
RestrictVertical, /**< Restrict to 90,270 degrees */
RestrictOrthogonal /**< Restrict to 90,180,270,0 degrees */
};
/**
* Enum of line styles:
*/
enum LineType {
LineByBlock = -2, /**< Line type defined by block not entity */
LineByLayer = -1, /**< Line type defined by layer not entity */
NoPen = 0, /**< No line at all. */
SolidLine = 1, /**< Normal line. */
DotLine = 2, /**< Dotted line. */
DotLineTiny = 3, /**< Dotted line tiny */
DotLine2 = 4, /**< Dotted line small. */
DotLineX2 = 5, /**< Dotted line large. */
DashLine = 6, /**< Dashed line. */
DashLineTiny=7, /**< Dashed line tiny */
DashLine2 = 8, /**< Dashed line small. */
DashLineX2 = 9, /**< Dashed line large. */
DashDotLine = 10, /**< Alternate dots and dashes. */
DashDotLineTiny = 11, /**< Alternate dots and dashes tiny. */
DashDotLine2 = 12, /**< Alternate dots and dashes small. */
DashDotLineX2 = 13, /**< Alternate dots and dashes large. */
DivideLine = 14, /**< dash, dot, dot. */
DivideLineTiny = 15, /**< dash, dot, dot, tiny */
DivideLine2 = 16, /**< dash, dot, dot small. */
DivideLineX2 = 17, /**< dash, dot, dot large. */
CenterLine = 18, /**< dash, small dash. */
CenterLineTiny = 19, /**< dash, small dash tiny */
CenterLine2 = 20, /**< dash, small dash small. */
CenterLineX2 = 21, /**< dash, small dash large. */
BorderLine = 22, /**< dash, dash, dot. */
BorderLineTiny = 23, /**< dash, dash, dot tiny */
BorderLine2 = 24, /**< dash, dash, dot small. */
BorderLineX2 = 25, /**< dash, dash, dot large. */
LineTypeUnchanged=26 /**< Line type defined by block not entity */
};
/**
* Enum of line widths:
*/
enum LineWidth {
Width00 = 0, /**< Width 1. (0.00mm) */
Width01 = 5, /**< Width 2. (0.05mm) */
Width02 = 9, /**< Width 3. (0.09mm) */
Width03 = 13, /**< Width 4. (0.13mm) */
Width04 = 15, /**< Width 5. (0.15mm) */
Width05 = 18, /**< Width 6. (0.18mm) */
Width06 = 20, /**< Width 7. (0.20mm) */
Width07 = 25, /**< Width 8. (0.25mm) */
Width08 = 30, /**< Width 9. (0.30mm) */
Width09 = 35, /**< Width 10. (0.35mm) */
Width10 = 40, /**< Width 11. (0.40mm) */
Width11 = 50, /**< Width 12. (0.50mm) */
Width12 = 53, /**< Width 13. (0.53mm) */
Width13 = 60, /**< Width 14. (0.60mm) */
Width14 = 70, /**< Width 15. (0.70mm) */
Width15 = 80, /**< Width 16. (0.80mm) */
Width16 = 90, /**< Width 17. (0.90mm) */
Width17 = 100, /**< Width 18. (1.00mm) */
Width18 = 106, /**< Width 19. (1.06mm) */
Width19 = 120, /**< Width 20. (1.20mm) */
Width20 = 140, /**< Width 21. (1.40mm) */
Width21 = 158, /**< Width 22. (1.58mm) */
Width22 = 200, /**< Width 23. (2.00mm) */
Width23 = 211, /**< Width 24. (2.11mm) */
WidthByLayer = -1, /**< Line width defined by layer not entity. */
WidthByBlock = -2, /**< Line width defined by block not entity. */
WidthDefault = -3 /**< Line width defaults to the predefined line width. */
};
/**
* Wrapper for Qt
*/
/*
static int qw(RS2::LineWidth w) {
switch (w) {
case Width00:
return 1; // 0 is more accurate but quite slow
break;
case WidthByLayer:
case WidthByBlock:
case WidthDefault:
return 1;
break;
case Width01:
case Width02:
case Width03:
case Width04:
case Width05:
case Width06:
case Width07:
case Width08:
return 1;
break;
case Width09:
case Width10:
return 3;
break;
case Width11:
return 4;
break;
case Width12:
case Width13:
return 5;
break;
case Width14:
return 6;
break;
case Width15:
return 7;
break;
case Width16:
return 8;
break;
case Width17:
return 9;
break;
case Width18:
case Width19:
return 10;
break;
case Width20:
return 12;
break;
case Width21:
case Width22:
case Width23:
//case Width24:
return 14;
break;
default:
return (int)w;
break;
}
return (int)w;
}
*/
/**
* Wrapper for Qt
*/
static LineWidth intToLineWidth(int w);
/**
* Enum of cursor types.
*/
enum CursorType {
ArrowCursor, /**< ArrowCursor - standard arrow cursor. */
UpArrowCursor, /**< UpArrowCursor - upwards arrow. */
CrossCursor, /**< CrossCursor - crosshair. */
WaitCursor, /**< WaitCursor - hourglass/watch. */
IbeamCursor, /**< IbeamCursor - ibeam/text entry. */
SizeVerCursor, /**< SizeVerCursor - vertical resize. */
SizeHorCursor, /**< SizeHorCursor - horizontal resize. */
SizeBDiagCursor, /**< SizeBDiagCursor - diagonal resize (/). */
SizeFDiagCursor, /**< SizeFDiagCursor - diagonal resize (\). */
SizeAllCursor, /**< SizeAllCursor - all directions resize. */
BlankCursor, /**< BlankCursor - blank/invisible cursor. */
SplitVCursor, /**< SplitVCursor - vertical splitting. */
SplitHCursor, /**< SplitHCursor - horizontal splitting. */
PointingHandCursor, /**< PointingHandCursor - a pointing hand. */
ForbiddenCursor, /**< ForbiddenCursor - a slashed circle. */
WhatsThisCursor, /**< WhatsThisCursor - an arrow with a ?. */
OpenHandCursor, /**< Qt OpenHandCursor */
ClosedHandCursor, /**< Qt ClosedHandCursor */
CadCursor, /**< CadCursor - a bigger cross. */
DelCursor, /**< DelCursor - cursor for choosing entities */
SelectCursor, /**< SelectCursor - for selecting single entities */
MagnifierCursor, /**< MagnifierCursor - a magnifying glass. */
MovingHandCursor /**< Moving hand - a little flat hand. */
};
/**
* Wrapper for Qt.
*/
/*
static Qt::CursorShape rsToQtCursorType(RS2::CursorType t) {
switch (t) {
case ArrowCursor:
return Qt::ArrowCursor;
case UpArrowCursor:
return Qt::UpArrowCursor;
case CrossCursor:
return Qt::CrossCursor;
case WaitCursor:
return Qt::WaitCursor;
case IbeamCursor:
return Qt::IBeamCursor;
case SizeVerCursor:
return Qt::SizeVerCursor;
case SizeHorCursor:
return Qt::SizeHorCursor;
case SizeBDiagCursor:
return Qt::SizeBDiagCursor;
case SizeFDiagCursor:
return Qt::SizeFDiagCursor;
case SizeAllCursor:
return Qt::SizeAllCursor;
case BlankCursor:
return Qt::BlankCursor;
case SplitVCursor:
return Qt::SplitVCursor;
case SplitHCursor:
return Qt::SplitHCursor;
case PointingHandCursor:
return Qt::PointingHandCursor;
case OpenHandCursor:
return Qt::OpenHandCursor;
case ClosedHandCursor:
return Qt::ClosedHandCursor;
case ForbiddenCursor:
return Qt::ForbiddenCursor;
case WhatsThisCursor:
return Qt::WhatsThisCursor;
default:
return Qt::ArrowCursor;
}
}
*/
/**
* Paper formats.
*/
enum PaperFormat {
FirstPaperFormat,
Custom = FirstPaperFormat,
/* ISO "A" Series */
A0, /* 841 x 1189 mm 33.1 x 46.8 in */
A1, /* 594 x 841 mm 23.4 x 33.1 in */
A2, /* 420 x 594 mm 16.5 x 23.4 in */
A3, /* 297 x 420 mm 11.7 x 16.5 in */
A4, /* 210 x 297 mm 8.3 x 11.7 in */
/* Removed ISO "B" and "C" series, C5E, Comm10E, DLE, (envelope sizes) */
/* US "Office" */
Letter, /* 216 x 279 mm 8.5 x 11.0 in */
Legal, /* 216 x 356 mm 8.5 x 14.0 in */
Tabloid, /* 279 x 432 mm 11.0 x 17.0 in */
/* Tabloid = Ledger = ANSI B. Although, technically, both ANSI B and
Ledger are defined in the qt library as 431.8 mm x 279.4 mm / 17
x 11", while Tabloid is 279 x 432 mm / 11.0 x 17.0 in . Using either
"Ledger" or "AnsiB" will result in the wrong page orientation when
printing or exporting to PDF.) */
/* ANSI */
//Ansi_A, /* 216 x 279 mm 8.5 x 11.0 in */
//Ansi_B, /* 279 x 432 mm 11.0 x 17.0 in */
Ansi_C, /* 432 x 559 mm 17.0 x 22.0 in */
Ansi_D, /* 559 x 864 mm 22.0 x 34.0 in */
Ansi_E, /* 864 x 1118 mm 34.0 x 44.0 in */
/* Architectural */
Arch_A, /* 229 x 305 mm 9.0 x 12.0 in */
Arch_B, /* 305 x 457 mm 12.0 x 18.0 in */
Arch_C, /* 457 x 610 mm 18.0 x 24.0 in */
Arch_D, /* 610 x 914 mm 24.0 x 36.0 in */
Arch_E, /* 914 x 1219 mm 36.0 x 48.0 in */
NPageFormat
};
/**
* Items that can be put on a overlay, the items are rendered in this order. Best is to leave snapper as last so
* it always shows up
*/
enum OverlayGraphics {
ActionPreviewEntity, // Action Entities
Snapper // Snapper
};
//Different re-draw methods to speed up rendering of the screen
enum RedrawMethod {
RedrawNone = 0,
RedrawGrid = 1,
RedrawOverlay = 2,
RedrawDrawing = 4,
RedrawAll = 0xffff
};
/**
* Text drawing direction.
*/
enum TextLocaleDirection {
locLeftToRight, /** Left to right **/
locRightToLeft /** Right to Left **/
};
};
#endif

1169
lib/engine/rs_arc.cpp Normal file

File diff suppressed because it is too large Load Diff

257
lib/engine/rs_arc.h Normal file
View File

@@ -0,0 +1,257 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_ARC_H
#define RS_ARC_H
#include "rs_atomicentity.h"
class LC_Quadratic;
/**
* Holds the data that defines an arc.
*/
struct RS_ArcData {
RS_ArcData() = default;
~RS_ArcData() = default;
RS_ArcData(const RS_Vector& center,
double radius,
double angle1, double angle2,
bool reversed);
void reset();
bool isValid() const;
RS_Vector center;
double radius;
double angle1;
double angle2;
bool reversed;
};
std::ostream& operator << (std::ostream& os, const RS_ArcData& ad);
/**
* Class for an arc entity. All angles are in Rad.
*
* @author Andrew Mustun
*/
class RS_Arc : public RS_AtomicEntity {
public:
RS_Arc()=default;
RS_Arc(RS_EntityContainer* parent,
const RS_ArcData& d);
RS_Entity* clone() const override;
/** @return RS2::EntityArc */
RS2::EntityType rtti() const override
{
return RS2::EntityArc;
}
/** @return true */
bool isEdge() const override {
return true;
}
/** @return Copy of data that defines the arc. **/
RS_ArcData getData() const {
return data;
}
RS_VectorSolutions getRefPoints() const override;
/** Sets new arc parameters. **/
void setData(RS_ArcData d) {
data = d;
}
/** @return The center point (x) of this arc */
RS_Vector getCenter() const override {
return data.center;
}
/** Sets new center. */
void setCenter(const RS_Vector& c) {
data.center = c;
}
/** @return The radius of this arc */
double getRadius() const override {
return data.radius;
}
/** Sets new radius. */
void setRadius(double r) {
data.radius = r;
}
/** @return The start angle of this arc */
double getAngle1() const {
return data.angle1;
}
/** Sets new start angle. */
void setAngle1(double a1) {
data.angle1 = a1;
}
/** @return The end angle of this arc */
double getAngle2() const {
return data.angle2;
}
/** Sets new end angle. */
void setAngle2(double a2) {
data.angle2 = a2;
}
/** get angle relative arc center*/
double getArcAngle(const RS_Vector& vp) {
return (vp - data.center).angle();
}
/**
* @return Direction 1. The angle at which the arc starts at
* the startpoint.
*/
double getDirection1() const override;
/**
* @return Direction 2. The angle at which the arc starts at
* the endpoint.
*/
double getDirection2() const override;
/**
* @retval true if the arc is reversed (clockwise),
* @retval false otherwise
*/
bool isReversed() const {
return data.reversed;
}
/** sets the reversed status. */
void setReversed(bool r) {
data.reversed = r;
}
/** @return Start point of the entity. */
RS_Vector getStartpoint() const override;
/** @return End point of the entity. */
RS_Vector getEndpoint() const override;
std::vector<RS_Entity* > offsetTwoSides(const double& distance) const override;
/**
* implementations must revert the direction of an atomic entity
*/
void revertDirection() override;
void correctAngles();//make sure angleLength() is not more than 2*M_PI
void moveStartpoint(const RS_Vector& pos) override;
void moveEndpoint(const RS_Vector& pos) override;
bool offset(const RS_Vector& position, const double& distance) override;
void trimStartpoint(const RS_Vector& pos) override;
void trimEndpoint(const RS_Vector& pos) override;
RS2::Ending getTrimPoint(const RS_Vector& coord,
const RS_Vector& trimPoint) override;
/** choose an intersection to trim to based on mouse point */
RS_Vector prepareTrim(const RS_Vector& mousePoint,
const RS_VectorSolutions& trimSol)override;
void reverse() override;
RS_Vector getMiddlePoint() const override;
double getAngleLength() const;
double getLength() const override;
double getBulge() const;
bool createFrom3P(const RS_Vector& p1, const RS_Vector& p2,
const RS_Vector& p3);
bool createFrom2PDirectionRadius(const RS_Vector& startPoint, const RS_Vector& endPoint,
double direction1, double radius);
bool createFrom2PDirectionAngle(const RS_Vector& startPoint, const RS_Vector& endPoint,
double direction1, double angleLength);
bool createFrom2PBulge(const RS_Vector& startPoint, const RS_Vector& endPoint,
double bulge);
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true,
double* dist = nullptr,
RS_Entity** entity=nullptr) const override;
RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1
) const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestDist(double distance,
bool startp) const override;
RS_Vector getNearestOrthTan(const RS_Vector& coord,
const RS_Line& normal,
bool onEntity = false) const override;
RS_VectorSolutions getTangentPoint(const RS_Vector& point) const override;//find the tangential points seeing from given point
RS_Vector getTangentDirection(const RS_Vector& point) const override;
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
/** find the visible part of the arc, and call drawVisible() to draw */
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
/** directly draw the arc, assuming the whole arc is within visible window */
void drawVisible(RS_Painter* painter, RS_GraphicView* view, double& patternOffset);
friend std::ostream& operator << (std::ostream& os, const RS_Arc& a);
virtual void calculateBorders() override;
/** return the equation of the entity
for quadratic,
return a vector contains:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
virtual LC_Quadratic getQuadratic() const override;
/**
* @brief areaLineIntegral, line integral for contour area calculation by Green's Theorem
* Contour Area =\oint x dy
* @return line integral \oint x dy along the entity
* \oint x dy = c_x r \sin t + \frac{1}{4}r^2\sin 2t + \frac{1}{2}r^2 t
*/
virtual double areaLineIntegral() const override;
protected:
RS_ArcData data;
};
#endif

View File

@@ -0,0 +1,206 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
Copyright (C) 2012-2015 Dongxu Li (dongxuli2011@gmail.com)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
**********************************************************************/
#include "rs_atomicentity.h"
RS_AtomicEntity::RS_AtomicEntity(RS_EntityContainer* parent) : RS_Entity(parent) {}
bool RS_AtomicEntity::isContainer() const {
return false;
}
/**
* @return true because entities made from subclasses are
* atomic entities.
*/
bool RS_AtomicEntity::isAtomic() const {
return true;
}
/**
* @return Always 1 for atomic entities.
*/
unsigned int RS_AtomicEntity::count() const{
return 1;
}
/**
* @return Always 1 for atomic entities.
*/
unsigned int RS_AtomicEntity::countDeep() const{
return 1;
}
/**
* Implementation must return the endpoint of the entity or
* an invalid vector if the entity has no endpoint.
*/
RS_Vector RS_AtomicEntity::getEndpoint() const {
return RS_Vector(false);
}
/**
* Implementation must return the startpoint of the entity or
* an invalid vector if the entity has no startpoint.
*/
RS_Vector RS_AtomicEntity::getStartpoint() const {
return RS_Vector(false);
}
/**
* Implementation must return the angle in which direction the entity starts.
*/
double RS_AtomicEntity::getDirection1() const {
return 0.0;
}
/**
* Implementation must return the angle in which direction the entity starts the opposite way.
*/
double RS_AtomicEntity::getDirection2() const {
return 0.0;
}
RS_Vector RS_AtomicEntity::getCenter() const {
return RS_Vector(false);
}
double RS_AtomicEntity::getRadius() const {
return 0.;
}
/**
* return the nearest center for snapping
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between 'coord' and the closest center point. The passed
* pointer can also be NULL in which case the distance will be
* lost.
*
* @return The closest center point.
*/
RS_Vector RS_AtomicEntity::getNearestCenter(const RS_Vector& /*coord*/,
double* /*dist*/) const{
return RS_Vector(false);
}
/**
* (De-)selects startpoint.
*/
void RS_AtomicEntity::setStartpointSelected(bool select) {
if (select) {
setFlag(RS2::FlagSelected1);
} else {
delFlag(RS2::FlagSelected1);
}
}
/**
* (De-)selects endpoint.
*/
void RS_AtomicEntity::setEndpointSelected(bool select) {
if (select) {
setFlag(RS2::FlagSelected2);
} else {
delFlag(RS2::FlagSelected2);
}
}
bool RS_AtomicEntity::isTangent(const RS_CircleData& /* circleData */) const{
return false;
}
/**
* @return True if the entities startpoint is selected.
*/
bool RS_AtomicEntity::isStartpointSelected() const {
return getFlag(RS2::FlagSelected1);
}
/**
* @return True if the entities endpoint is selected.
*/
bool RS_AtomicEntity::isEndpointSelected() const {
return getFlag(RS2::FlagSelected2);
}
void RS_AtomicEntity::revertDirection(){}
/**
* Implementation must create offset of the entity to
* the given direction and distance
*/
bool RS_AtomicEntity::offset(const RS_Vector& /*position*/, const double& /*distance*/) {return false;}
/**
* Implementation must move the startpoint of the entity to
* the given position.
*/
void RS_AtomicEntity::moveStartpoint(const RS_Vector& /*pos*/) {}
/**
* Implementation must move the endpoint of the entity to
* the given position.
*/
void RS_AtomicEntity::moveEndpoint(const RS_Vector& /*pos*/) {}
/**
* Implementation must trim the startpoint of the entity to
* the given position.
*/
void RS_AtomicEntity::trimStartpoint(const RS_Vector& pos) {
moveStartpoint(pos);
}
/**
* Implementation must trim the endpoint of the entity to
* the given position.
*/
void RS_AtomicEntity::trimEndpoint(const RS_Vector& pos) {
moveEndpoint(pos);
}
/**
* Implementation must return which ending of the entity will
* be trimmed if 'coord' is the coordinate chosen to indicate the
* trim entity and 'trimPoint' is the point to which the entity will
* be trimmed.
*/
RS2::Ending RS_AtomicEntity::getTrimPoint(const RS_Vector& /*coord*/,
const RS_Vector& /*trimPoint*/) {
return RS2::EndingNone;
}
/**
* Implementation must trim the entity in the case of multiple
* intersections and return the trimPoint
* trimCoord indicts the trigger trim position
* trimSol contains intersections
* */
RS_Vector RS_AtomicEntity::prepareTrim(const RS_Vector& /*trimCoord*/,
const RS_VectorSolutions& /*trimSol*/) {
return RS_Vector(false);
}
void RS_AtomicEntity::reverse() {}
void RS_AtomicEntity::moveSelectedRef(const RS_Vector& ref, const RS_Vector& offset) {
if (isSelected()) {
moveRef(ref, offset);
}
}

View File

@@ -0,0 +1,185 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_ATOMICENTITY_H
#define RS_ATOMICENTITY_H
#include "rs_entity.h"
struct RS_CircleData;
/**
* Class representing an atomic entity
* Typical atomic entities: RS_Line, RS_Arc, RS_Circle, RS_Ellipse
*
* @author Andrew Mustun
*/
class RS_AtomicEntity : public RS_Entity {
public:
/**
* Constructor.
*/
RS_AtomicEntity(RS_EntityContainer* parent=nullptr);
/**
* @return false because entities made from subclasses are
* atomic entities.
*/
bool isContainer() const override;
/**
* @return true because entities made from subclasses are
* atomic entities.
*/
bool isAtomic() const override;
/**
* @return Always 1 for atomic entities.
*/
unsigned count() const override;
/**
* @return Always 1 for atomic entities.
*/
unsigned countDeep() const override;
/**
* Implementation must return the endpoint of the entity or
* an invalid vector if the entity has no endpoint.
*/
RS_Vector getEndpoint() const override;
/**
* Implementation must return the startpoint of the entity or
* an invalid vector if the entity has no startpoint.
*/
RS_Vector getStartpoint() const override;
/**
* Implementation must return the angle in which direction the entity starts.
*/
double getDirection1() const override;
/**
* Implementation must return the angle in which direction the entity starts the opposite way.
*/
double getDirection2() const override;
RS_Vector getCenter() const override;
double getRadius() const override;
/**
* return the nearest center for snapping
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between 'coord' and the closest center point. The passed
* pointer can also be NULL in which case the distance will be
* lost.
*
* @return The closest center point.
*/
RS_Vector getNearestCenter(const RS_Vector& /*coord*/,
double* /*dist*/) const override;
/**
* (De-)selects startpoint.
*/
virtual void setStartpointSelected(bool select);
/**
* (De-)selects endpoint.
*/
virtual void setEndpointSelected(bool select);
virtual bool isTangent(const RS_CircleData& /* circleData */) const;
/**
* @return True if the entities startpoint is selected.
*/
bool isStartpointSelected() const;
/**
* @return True if the entities endpoint is selected.
*/
bool isEndpointSelected() const;
void revertDirection() override;
/**
* Implementation must create offset of the entity to
* the given direction and distance
*/
bool offset(const RS_Vector& /*position*/, const double& /*distance*/) override;
/**
* Implementation must move the startpoint of the entity to
* the given position.
*/
virtual void moveStartpoint(const RS_Vector& /*pos*/);
/**
* Implementation must move the endpoint of the entity to
* the given position.
*/
virtual void moveEndpoint(const RS_Vector& /*pos*/);
/**
* Implementation must trim the startpoint of the entity to
* the given position.
*/
virtual void trimStartpoint(const RS_Vector& pos);
/**
* Implementation must trim the endpoint of the entity to
* the given position.
*/
virtual void trimEndpoint(const RS_Vector& pos);
/**
* Implementation must return which ending of the entity will
* be trimmed if 'coord' is the coordinate chosen to indicate the
* trim entity and 'trimPoint' is the point to which the entity will
* be trimmed.
*/
virtual RS2::Ending getTrimPoint(const RS_Vector& /*coord*/,
const RS_Vector& /*trimPoint*/);
/**
* Implementation must trim the entity in the case of multiple
* intersections and return the trimPoint
* trimCoord indicts the trigger trim position
* trimSol contains intersections
* */
virtual RS_Vector prepareTrim(const RS_Vector& /*trimCoord*/,
const RS_VectorSolutions& /*trimSol*/);
virtual void reverse();
void moveSelectedRef(const RS_Vector& ref, const RS_Vector& offset) override;
};
#endif

202
lib/engine/rs_block.cpp Normal file
View File

@@ -0,0 +1,202 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_block.h"
#include "rs_graphic.h"
#include "rs_insert.h"
RS_BlockData::RS_BlockData(const QString& _name,
const RS_Vector& _basePoint,
bool _frozen):
name(_name)
,basePoint(_basePoint)
,frozen(_frozen)
{
}
bool RS_BlockData::isValid() const{
return (!name.isEmpty() && basePoint.valid);
}
/**
* @param parent The graphic this block belongs to.
* @param name The name of the block used as an identifier.
* @param basePoint Base point (offset) of the block.
*/
RS_Block::RS_Block(RS_EntityContainer* parent,
const RS_BlockData& d)
: RS_Document(parent), data(d) {
pen = RS_Pen(RS_Color(128,128,128), RS2::Width01, RS2::SolidLine);
}
RS_Entity* RS_Block::clone() const {
RS_Block* blk = new RS_Block(*this);
blk->setOwner(isOwner());
blk->detach();
blk->initId();
return blk;
}
RS_LayerList* RS_Block::getLayerList() {
RS_Graphic* g = getGraphic();
if (g) {
return g->getLayerList();
} else {
return NULL;
}
}
RS_BlockList* RS_Block::getBlockList() {
RS_Graphic* g = getGraphic();
if (g) {
return g->getBlockList();
} else {
return NULL;
}
}
bool RS_Block::save(bool isAutoSave) {
RS_Graphic* g = getGraphic();
if (g) {
return g->save(isAutoSave);
} else {
return false;
}
}
bool RS_Block::saveAs(const QString& filename, RS2::FormatType type, bool force) {
RS_Graphic* g = getGraphic();
if (g) {
return g->saveAs(filename, type, force);
} else {
return false;
}
}
/**
* Sets the parent documents modified status to 'm'.
*/
void RS_Block::setModified(bool m) {
RS_Graphic* p = getGraphic();
if (p) {
p->setModified(m);
}
modified = m;
}
/**
* Sets the visibility of the Block in block list
*
* @param v true: visible, false: invisible
*/
void RS_Block::visibleInBlockList(bool v) {
data.visibleInBlockList = v;
}
/**
* Returns the visibility of the Block in block list
*/
bool RS_Block::isVisibleInBlockList() const {
return data.visibleInBlockList;
}
/**
* Sets selection state of the block in block list
*
* @param v true: selected, false: deselected
*/
void RS_Block::selectedInBlockList(bool v) {
data.selectedInBlockList = v;
}
/**
* Returns selection state of the block in block list
*/
bool RS_Block::isSelectedInBlockList() const {
return data.selectedInBlockList;
}
/**
* Block may contain inserts of other blocks.
* Find name of the nested block that contain the insert
* of specified block.
*
* @param bName name of the block the nested insert references to
*
* @return block name chain to the block that contain searched insert
*/
QStringList RS_Block::findNestedInsert(const QString& bName) {
QStringList bnChain;
for (RS_Entity* e: entities) {
if (e->rtti()==RS2::EntityInsert) {
RS_Insert* i = ((RS_Insert*)e);
QString iName = i->getName();
if (iName == bName) {
bnChain << data.name;
break;
} else {
RS_BlockList* bList = getBlockList();
if (bList) {
RS_Block* nestedBlock = bList->find(iName);
if (nestedBlock) {
QStringList nestedChain;
nestedChain = nestedBlock->findNestedInsert(bName);
if (!nestedChain.empty()) {
bnChain << data.name;
bnChain << nestedChain;
break;
}
}
}
}
}
}
return bnChain;
}
std::ostream& operator << (std::ostream& os, const RS_Block& b) {
os << " name: " << b.getName().toLatin1().data() << "\n";
os << " entities: " << (RS_EntityContainer&)b << "\n";
return os;
}

228
lib/engine/rs_block.h Normal file
View File

@@ -0,0 +1,228 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_BLOCK_H
#define RS_BLOCK_H
#include "rs_document.h"
/**
* Holds the data that defines a block.
*/
struct RS_BlockData {
RS_BlockData() = default;
RS_BlockData(const QString& name,
const RS_Vector& basePoint,
bool frozen);
bool isValid() const;
/**
* Block name. Acts as an id.
*/
QString name;
/**
* Base point of the Block. Usually 0/0 since blocks can be moved around
* using the insertion point of Insert entities.
*/
RS_Vector basePoint;
bool frozen {false}; //!< Frozen flag
bool visibleInBlockList {true}; //!< Visible in block list
bool selectedInBlockList {false}; //!< selected in block list
};
/**
* A block is a group of entities. A block unlike an other entity
* container has a base point which defines the offset of the
* block. Note that although technically possible, a block should
* never be part of the entity tree of a graphic. Blocks are
* stored in a separate list inside the graphic document (a block list).
* The graphic can contain RS_Insert entities that refer to such
* blocks.
*
* blocks are documents and can therefore be handled by graphic views.
*
* @author Andrew Mustun
*/
class RS_Block : public RS_Document {
friend class RS_BlockList;
public:
/**
* @param parent The graphic this block belongs to.
* @param blockData defining data of the block.
*/
RS_Block(RS_EntityContainer* parent, const RS_BlockData& d);
virtual ~RS_Block() = default;
virtual RS_Entity* clone() const;
/** @return RS2::EntityBlock */
virtual RS2::EntityType rtti() const {
return RS2::EntityBlock;
}
/**
* @return Name of this block (the name is an Id for this block).
*/
QString getName() const {
return data.name;
}
/**
* @return base point of this block.
*/
RS_Vector getBasePoint() const {
return data.basePoint;
}
virtual RS_LayerList* getLayerList();
virtual RS_BlockList* getBlockList();
/**
* Reimplementation from RS_Document. Does nothing.
*/
virtual void newDoc() {
// do nothing
}
/**
* Reimplementation from RS_Document. Saves the parent graphic document.
*/
virtual bool save(bool isAutoSave = false);
/**
* Reimplementation from RS_Document. Does nothing.
*/
virtual bool saveAs(const QString& filename, RS2::FormatType type, bool force = false);
/**
* Reimplementation from RS_Document. Does nothing.
*/
virtual bool open(const QString& , RS2::FormatType) {
// do nothing
return false;
}
virtual bool loadTemplate(const QString& , RS2::FormatType) {
// do nothing
return false;
}
friend std::ostream& operator << (std::ostream& os, const RS_Block& b);
/**
* sets a new name for the block. Only called by blocklist to
* assure that block names stay unique.
*/
void setName(const QString& n) {
data.name = n;
}
/**
* @retval true if this block is frozen (invisible)
* @retval false if this block isn't frozen (visible)
*/
bool isFrozen() const {
return data.frozen;
}
/**
* Toggles the visibility of this block.
* Freezes the block if it's not frozen, thaws the block otherwise
*/
void toggle() {
data.frozen = !data.frozen;
}
/**
* (De-)freezes this block.
*
* @param freeze true: freeze, false: defreeze
*/
void freeze(bool freeze) {
data.frozen = freeze;
}
/**
* Sets the parent documents modified status to 'm'.
*/
virtual void setModified(bool m);
/**
* Sets only this block modified status to 'm'
* without touching parent document.
*/
void setModifiedFlag(bool m) { modified = m; }
/**
* Sets the visibility of the Block in block list
*
* @param v true: visible, false: invisible
*/
void visibleInBlockList(bool v);
/**
* Returns the visibility of the Block in block list
*/
bool isVisibleInBlockList() const;
/**
* Sets selection state of the block in block list
*
* @param v true: selected, false: deselected
*/
void selectedInBlockList(bool v);
/**
* Returns selection state of the block in block list
*/
bool isSelectedInBlockList() const;
/**
* Block may contain inserts of other blocks.
* Find name of the nested block that contain the insert
* of specified block.
*
* @param bName name of the block the nested insert references to
*
* @return block name chain to the block that contain searched insert
*/
QStringList findNestedInsert(const QString& bName);
protected:
//! Block data
RS_BlockData data;
};
#endif

426
lib/engine/rs_blocklist.cpp Normal file
View File

@@ -0,0 +1,426 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 <set>
#include <iostream>
#include <QString>
#include <QRegExp>
#include "rs_debug.h"
#include "rs_blocklist.h"
#include "rs_block.h"
#include "rs_blocklistlistener.h"
/**
* Constructor.
*
* @param owner true if this is the owner of the blocks added.
* If so, the blocks will be deleted when the block
* list is deleted.
*/
RS_BlockList::RS_BlockList(bool owner) {
this->owner = owner;
//blocks.setAutoDelete(owner);
activeBlock = nullptr;
setModified(false);
}
/**
* Removes all blocks in the blocklist.
*/
void RS_BlockList::clear() {
blocks.clear();
activeBlock = nullptr;
setModified(true);
}
/**
* Activates the given block.
* Listeners are notified.
*/
void RS_BlockList::activate(const QString& name) {
RS_DEBUG->print("RS_BlockList::activateBlock");
activate(find(name));
}
/**
* Activates the given block.
* Listeners are notified.
*/
void RS_BlockList::activate(RS_Block* block) {
RS_DEBUG->print("RS_BlockList::activateBlock");
activeBlock = block;
}
/**
* Adds a block to the block list. If a block with the same name
* exists already, the given block will be deleted if the blocklist
* owns the blocks.
*
* @param notify true if you want listeners to be notified.
*
* @return false: block already existed and was deleted.
*/
bool RS_BlockList::add(RS_Block* block, bool notify) {
RS_DEBUG->print("RS_BlockList::add()");
if (!block) {
return false;
}
// check if block already exists:
RS_Block* b = find(block->getName());
if (!b) {
blocks.append(block);
if (notify) {
addNotification();
}
setModified(true);
return true;
} else {
if (owner) {
delete block;
block = nullptr;
}
return false;
}
}
/**
* Notifies the listeners about blocks that were added. This can be
* used after adding a lot of blocks without auto-update or simply
* to force an update of GUI blocklists.
*/
void RS_BlockList::addNotification() {
for(auto l: blockListListeners){
l->blockAdded(nullptr);
}
}
/**
* Removes a block from the list.
* Listeners are notified after the block was removed from
* the list but before it gets deleted.
*/
void RS_BlockList::remove(RS_Block* block) {
RS_DEBUG->print("RS_BlockList::removeBlock()");
// here the block is removed from the list but not deleted
blocks.removeOne(block);
for(auto l: blockListListeners){
l->blockRemoved(block);
}
setModified(true);
// / *
// activate an other block if necessary:
if (activeBlock==block) {
//activate(blocks.first());
activeBlock = nullptr;
}
// * /
// now it's save to delete the block
if (owner) {
delete block;
}
}
/**
* Tries to rename the given block to 'name'. Block names are unique in the
* block list.
*
* @retval true block was successfully renamed.
* @retval false block couldn't be renamed.
*/
bool RS_BlockList::rename(RS_Block* block, const QString& name) {
if (block) {
if (!find(name)) {
QString oldName = block->getName();
block->setName(name);
setModified(true);
// when the renamed block is nested within other block, we need to rename its inserts as well
for(RS_Block* b: blocks) {
b->renameInserts(oldName, name);
}
return true;
}
}
return false;
}
/**
* Changes a block's attributes. The attributes of block 'block'
* are copied from block 'source'.
* Listeners are notified.
*/
/*
void RS_BlockList::editBlock(RS_Block* block, const RS_Block& source) {
*block = source;
for (unsigned i=0; i<blockListListeners.count(); ++i) {
RS_BlockListListener* l = blockListListeners.at(i);
l->blockEdited(block);
}
}
*/
/**
* @return Pointer to the block with the given name or
* \p nullptr if no such block was found.
*/
RS_Block* RS_BlockList::find(const QString& name) {
try {
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_BlockList::find(): %s", name.toLatin1().constData());
}
catch(...) {
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_BlockList::find(): wrong name to find");
return nullptr;
}
// Todo : reduce this from O(N) to O(log(N)) complexity based on sorted list or hash
//DFS
for(RS_Block* b: blocks) {
if (b->getName()==name) {
return b;
}
}
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_BlockList::find(): bad");
return nullptr;
}
/**
* Finds a new unique block name.
*
* @param suggestion Suggested name the new name will be based on.
*/
QString RS_BlockList::newName(const QString& suggestion) {
// qDebug()<<"begin: suggestion: "<<suggestion;
if(!find(suggestion))
return suggestion;
QString name=suggestion;
QRegExp const rx(R"(-\d+$)");
int index=name.lastIndexOf(rx);
int i=-1;
if(index>0){
i=name.mid(index+1).toInt();
name=name.mid(0, index);
}
for(RS_Block* b: blocks){
index=b->getName().lastIndexOf(rx);
if(index<0) continue;
QString const part1= b->getName().mid(0, index);
if(part1 != name) continue;
i=std::max(b->getName().mid(index+1).toInt(),i);
}
// qDebug()<<QString("%1-%2").arg(name).arg(i+1);
return QString("%1-%2").arg(name).arg(i+1);
}
/**
* Switches on / off the given block.
* Listeners are notified.
*/
void RS_BlockList::toggle(const QString& name) {
toggle(find(name));
}
/**
* Switches on / off the given block.
* Listeners are notified.
*/
void RS_BlockList::toggle(RS_Block* block) {
if (!block) {
return;
}
block->toggle();
// TODO LordOfBikes: when block attributes are saved, activate this
//setModified(true);
// Notify listeners:
for(auto l: blockListListeners){
l->blockToggled(block);
}
}
/**
* Freezes or defreezes all blocks.
*
* @param freeze true: freeze, false: defreeze
*/
void RS_BlockList::freezeAll(bool freeze) {
for (int l=0; l<count(); l++) {
if (at(l)->isVisibleInBlockList()) {
at(l)->freeze(freeze);
}
}
// TODO LordOfBikes: when block attributes are saved, activate this
//setModified(true);
for(auto l: blockListListeners){
l->blockToggled(nullptr);
}
}
/**
* Switches on / off the given block.
* Listeners are notified.
*/
/*
void RS_BlockList::toggleBlock(const QString& name) {
RS_Block* block = findBlock(name);
block->toggle();
// Notify listeners:
for (unsigned i=0; i<blockListListeners.count(); ++i) {
RS_BlockListListener* l = blockListListeners.at(i);
l->blockToggled(block);
}
}
*/
/**
* adds a BlockListListener to the list of listeners. Listeners
* are notified when the block list changes.
*/
void RS_BlockList::addListener(RS_BlockListListener* listener) {
blockListListeners.append(listener);
}
/**
* removes a BlockListListener from the list of listeners.
*/
void RS_BlockList::removeListener(RS_BlockListListener* listener) {
blockListListeners.removeOne(listener);
}
int RS_BlockList::count() const{
return blocks.count();
}
/**
* @return Block at given position or nullptr if i is out of range.
*/
RS_Block* RS_BlockList::at(int i) {
return blocks.at(i);
}
RS_Block* RS_BlockList::at(int i) const{
return blocks.at(i);
}
QList<RS_Block*>::iterator RS_BlockList::begin()
{
return blocks.begin();
}
QList<RS_Block*>::iterator RS_BlockList::end()
{
return blocks.end();
}
QList<RS_Block*>::const_iterator RS_BlockList::begin()const
{
return blocks.begin();
}
QList<RS_Block*>::const_iterator RS_BlockList::end()const
{
return blocks.end();
}
//! @return The active block of nullptr if no block is activated.
RS_Block* RS_BlockList::getActive() {
return activeBlock;
}
/**
* Sets the block list modified status to 'm'.
*/
void RS_BlockList::setModified(bool m) {
modified = m;
// Update each block modified status,
// but only when the status is set to false.
if (m == false) {
for (auto b: blocks) {
b->setModifiedFlag(false);
}
}
// Notify listeners
for (auto l: blockListListeners) {
l->blockListModified(m);
}
}
/**
* @retval true The block list has been modified.
* @retval false The block list has not been modified.
*/
bool RS_BlockList::isModified() const {
return modified;
}
/**
* Dumps the blocks to stdout.
*/
std::ostream& operator << (std::ostream& os, RS_BlockList& b) {
os << "Blocklist: \n";
for (int i=0; i<b.count(); ++i) {
RS_Block* blk = b.at(i);
os << *blk << "\n";
}
return os;
}

116
lib/engine/rs_blocklist.h Normal file
View File

@@ -0,0 +1,116 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_BLOCKLIST_H
#define RS_BLOCKLIST_H
#include <QList>
class QString;
class RS_Block;
class RS_BlockListListener;
/**
* List of blocks.
*
* @see RS_Block
*
* @author Andrew Mustun
*/
class RS_BlockList {
public:
RS_BlockList(bool owner=false);
virtual ~RS_BlockList() = default;
void clear();
/**
* @return Number of blocks available.
*/
int count() const;
/**
* @return Block at given position or NULL if i is out of range.
*/
RS_Block* at(int i);
RS_Block* at(int i) const;
//! \{ \brief range based loop
QList<RS_Block*>::iterator begin();
QList<RS_Block*>::iterator end();
QList<RS_Block*>::const_iterator begin()const;
QList<RS_Block*>::const_iterator end()const;
//! \}
void activate(const QString& name);
void activate(RS_Block* block);
//! @return The active block of NULL if no block is activated.
RS_Block* getActive();
virtual bool add(RS_Block* block, bool notify=true);
virtual void addNotification();
virtual void remove(RS_Block* block);
virtual bool rename(RS_Block* block, const QString& name);
//virtual void editBlock(RS_Block* block, const RS_Block& source);
RS_Block* find(const QString& name);
QString newName(const QString& suggestion = "");
void toggle(const QString& name);
void toggle(RS_Block* block);
void freezeAll(bool freeze);
void addListener(RS_BlockListListener* listener);
void removeListener(RS_BlockListListener* listener);
bool isOwner() const {return owner;}
void setOwner(bool ow) {owner = ow;}
/**
* Sets the block list modified status to 'm'.
*/
void setModified(bool m);
/**
* @retval true The block list has been modified.
* @retval false The block list has not been modified.
*/
bool isModified() const;
friend std::ostream& operator << (std::ostream& os, RS_BlockList& b);
private:
//! Is the list owning the blocks?
bool owner;
//! Blocks in the graphic
QList<RS_Block*> blocks;
//! List of registered BlockListListeners
QList<RS_BlockListListener*> blockListListeners;
//! Currently active block
RS_Block* activeBlock;
/** Flag set if the block list was modified and not yet saved. */
bool modified;
};
#endif

View File

@@ -0,0 +1,74 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_BLOCKLISTLISTENER_H
#define RS_BLOCKLISTLISTENER_H
#include "rs_block.h"
/**
* This class is an interface for classes that are interested in
* knowing about changes in the block list.
*/
class RS_BlockListListener {
public:
RS_BlockListListener() {}
virtual ~RS_BlockListListener() {}
/**
* Called when the active block changes.
*/
virtual void blockActivated(RS_Block*) {}
/**
* Called when a new block is added to the list.
*/
virtual void blockAdded(RS_Block*) {}
/**
* Called when a block is removed from the list.
*/
virtual void blockRemoved(RS_Block*) {}
/**
* Called when a block's attributes are modified.
*/
virtual void blockEdited(RS_Block*) {}
/**
* Called when a block's visibility is toggled.
*/
virtual void blockToggled(RS_Block*) {}
/**
* Called when block list is modified.
*/
virtual void blockListModified(bool) {}
}
;
#endif

841
lib/engine/rs_circle.cpp Normal file
View File

@@ -0,0 +1,841 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 A. Stebich (librecad@mail.lordofbikes.de)
** Copyright (C) 2011-2012 Dongxu Li (dongxuli2011@gmail.com)
** 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 <cfloat>
#include <QPolygonF>
#include "rs_circle.h"
#include "rs_arc.h"
#include "rs_line.h"
#include "rs_constructionline.h"
#include "rs_information.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
#include "rs_linetypepattern.h"
#include "rs_math.h"
#include "lc_hyperbola.h"
#include "lc_quadratic.h"
#include "rs_debug.h"
RS_CircleData::RS_CircleData(RS_Vector const& center, double radius):
center(center)
, radius(radius)
{
}
bool RS_CircleData::isValid() const {
return (center.valid && radius>RS_TOLERANCE);
}
bool RS_CircleData::operator == (RS_CircleData const& rhs) const
{
if(! (center.valid && rhs.center.valid)) return false;
if( center.squaredTo(rhs.center) > RS_TOLERANCE2) return false;
return fabs(radius - rhs.radius)< RS_TOLERANCE;
}
std::ostream& operator << (std::ostream& os, const RS_CircleData& ad)
{
os << "(" << ad.center <<
"/" << ad.radius <<
")";
return os;
}
/**
* constructor.
*/
RS_Circle::RS_Circle(RS_EntityContainer* parent,
const RS_CircleData& d)
:RS_AtomicEntity(parent), data(d) {
calculateBorders();
}
RS_Entity* RS_Circle::clone() const {
RS_Circle* c = new RS_Circle(*this);
c->initId();
return c;
}
void RS_Circle::calculateBorders() {
RS_Vector r(data.radius,data.radius);
minV = data.center - r;
maxV = data.center + r;
}
/** @return The center point (x) of this arc */
RS_Vector RS_Circle::getCenter() const {
return data.center;
}
/** Sets new center. */
void RS_Circle::setCenter(const RS_Vector& c) {
data.center = c;
}
/** @return The radius of this arc */
double RS_Circle::getRadius() const {
return data.radius;
}
/** Sets new radius. */
void RS_Circle::setRadius(double r) {
data.radius = r;
}
/**
* @return Angle length in rad.
*/
double RS_Circle::getAngleLength() const {
return 2*M_PI;
}
/**
* @return Length of the circle which is the circumference.
*/
double RS_Circle::getLength() const {
return 2*M_PI*data.radius;
}
bool RS_Circle::isTangent(const RS_CircleData& circleData) const{
const double d=circleData.center.distanceTo(data.center);
// DEBUG_HEADER
const double r0=fabs(circleData.radius);
const double r1=fabs(data.radius);
// std::cout<<fabs(d-fabs(r0-r1))<<" : "<<fabs(d-fabs(r0+r1))<<std::endl;
if( fabs(d-fabs(r0-r1))<20.*RS_TOLERANCE ||
fabs(d-fabs(r0+r1))<20.*RS_TOLERANCE ) return true;
return false;
}
/**
* Creates this circle from a center point and a radius.
*
* @param c Center.
* @param r Radius
*/
bool RS_Circle::createFromCR(const RS_Vector& c, double r) {
if (fabs(r)>RS_TOLERANCE && c.valid ) {
data.radius = fabs(r);
data.center = c;
return true;
} else {
RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Circle::createFromCR(): "
"Cannot create a circle with radius 0.0.");
return false;
}
}
/**
* Creates this circle from two opposite points.
*
* @param p1 1st point.
* @param p2 2nd point.
*/
bool RS_Circle::createFrom2P(const RS_Vector& p1, const RS_Vector& p2) {
double r=0.5*p1.distanceTo(p2);
if (r>RS_TOLERANCE) {
data.radius = r;
data.center = (p1+p2)*0.5;
return true;
} else {
// RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Circle::createFrom2P(): "
// "Cannot create a circle with radius 0.0.");
return false;
}
}
/**
* Creates this circle from 3 given points which define the circle line.
*
* @param p1 1st point.
* @param p2 2nd point.
* @param p3 3rd point.
*/
bool RS_Circle::createFrom3P(const RS_Vector& p1, const RS_Vector& p2,
const RS_Vector& p3) {
RS_Vector vra=p2 - p1;
RS_Vector vrb=p3 - p1;
double ra2=vra.squared()*0.5;
double rb2=vrb.squared()*0.5;
double crossp=vra.x * vrb.y - vra.y * vrb.x;
if (fabs(crossp)< RS_TOLERANCE2) {
RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Circle::createFrom3P(): "
"Cannot create a circle with radius 0.0.");
return false;
}
crossp=1./crossp;
data.center.set((ra2*vrb.y - rb2*vra.y)*crossp,(rb2*vra.x - ra2*vrb.x)*crossp);
data.radius=data.center.magnitude();
data.center += p1;
return true;
}
//*create Circle from 3 points
//Author: Dongxu Li
bool RS_Circle::createFrom3P(const RS_VectorSolutions& sol) {
if(sol.getNumber() < 2) return false;
if(sol.getNumber() == 2) return createFrom2P(sol.get(0),sol.get(1));
if((sol.get(1)-sol.get(2)).squared() < RS_TOLERANCE2)
return createFrom2P(sol.get(0),sol.get(1));
RS_Vector vra(sol.get(1) - sol.get(0));
RS_Vector vrb(sol.get(2) - sol.get(0));
double ra2=vra.squared()*0.5;
double rb2=vrb.squared()*0.5;
double crossp=vra.x * vrb.y - vra.y * vrb.x;
if (fabs(crossp)< RS_TOLERANCE2) {
RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Circle::createFrom3P(): "
"Cannot create a circle with radius 0.0.");
return false;
}
crossp=1./crossp;
data.center.set((ra2*vrb.y - rb2*vra.y)*crossp,(rb2*vra.x - ra2*vrb.x)*crossp);
data.radius=data.center.magnitude();
data.center += sol.get(0);
return true;
}
/**
*create circle inscribled in a triangle
*
*Author: Dongxu Li
*/
bool RS_Circle::createInscribe(const RS_Vector& coord, const std::vector<RS_Line*>& lines){
if(lines.size()<3) return false;
std::vector<RS_Line*> tri(lines);
RS_VectorSolutions sol=RS_Information::getIntersectionLineLine(tri[0],tri[1]);
if(sol.getNumber() == 0 ) {//move parallel to opposite
std::swap(tri[1],tri[2]);
sol=RS_Information::getIntersectionLineLine(tri[0],tri[1]);
}
if(sol.getNumber() == 0 ) return false;
RS_Vector vp0(sol.get(0));
sol=RS_Information::getIntersectionLineLine(tri[2],tri[1]);
if(sol.getNumber() == 0 ) return false;
RS_Vector vp1(sol.get(0));
RS_Vector dvp(vp1-vp0);
double a(dvp.squared());
if( a< RS_TOLERANCE2) return false; //three lines share a common intersecting point
RS_Vector vp(coord - vp0);
vp -= dvp*(RS_Vector::dotP(dvp,vp)/a); //normal component
RS_Vector vl0(tri[0]->getEndpoint() - tri[0]->getStartpoint());
a=dvp.angle();
double angle0(0.5*(vl0.angle() + a));
if( RS_Vector::dotP(vp,vl0) <0.) {
angle0 += 0.5*M_PI;
}
RS_Line line0(vp0, vp0+RS_Vector(angle0));//first bisecting line
vl0=(tri[2]->getEndpoint() - tri[2]->getStartpoint());
angle0=0.5*(vl0.angle() + a+M_PI);
if( RS_Vector::dotP(vp,vl0) <0.) {
angle0 += 0.5*M_PI;
}
RS_Line line1(vp1, vp1+RS_Vector(angle0));//second bisection line
sol=RS_Information::getIntersectionLineLine(&line0,&line1);
if(sol.getNumber() == 0 ) return false;
bool ret=createFromCR(sol.get(0),tri[1]->getDistanceToPoint(sol.get(0)));
if(!ret) return false;
for(auto p: lines){
if(! p->isTangent(data)) return false;
}
return true;
}
std::vector<RS_Entity* > RS_Circle::offsetTwoSides(const double& distance) const
{
std::vector<RS_Entity*> ret(0,nullptr);
ret.push_back(new RS_Circle(nullptr, {getCenter(),getRadius()+distance}));
if(fabs(getRadius()-distance)>RS_TOLERANCE)
ret.push_back(new RS_Circle(nullptr, {getCenter(),fabs(getRadius()-distance)}));
return ret;
}
RS_VectorSolutions RS_Circle::createTan1_2P(const RS_AtomicEntity* circle, const std::vector<RS_Vector>& points)
{
RS_VectorSolutions ret;
if (!circle||points.size()<2) return ret;
return LC_Quadratic::getIntersection(
LC_Quadratic(circle,points[0]),
LC_Quadratic(circle,points[1])
);
}
/**
* create a circle of radius r and tangential to two given entities
*/
RS_VectorSolutions RS_Circle::createTan2(const std::vector<RS_AtomicEntity*>& circles, const double& r)
{
if(circles.size()<2) return false;
auto e0=circles[0]->offsetTwoSides(r);
auto e1=circles[1]->offsetTwoSides(r);
RS_VectorSolutions centers;
if(e0.size() && e1.size()) {
for(auto it0=e0.begin();it0!=e0.end();it0++){
for(auto it1=e1.begin();it1!=e1.end();it1++){
centers.push_back(RS_Information::getIntersection(*it0,*it1));
}
}
}
for(auto it0=e0.begin();it0!=e0.end();it0++){
delete *it0;
}
for(auto it0=e1.begin();it0!=e1.end();it0++){
delete *it0;
}
return centers;
}
std::vector<RS_Circle> RS_Circle::createTan3(const std::vector<RS_AtomicEntity*>& circles)
{
std::vector<RS_Circle> ret;
if(circles.size()!=3) return ret;
std::vector<RS_Circle> cs;
for(auto c: circles){
cs.emplace_back(RS_Circle(nullptr, {c->getCenter(),c->getRadius()}));
}
unsigned short flags=0;
do{
for(unsigned short j=0u;j<3u;++j){
if(flags & (1u<<j)) {
cs[j].setRadius( - fabs(cs[j].getRadius()));
}else{
cs[j].setRadius( fabs(cs[j].getRadius()));
}
}
// RS_DEBUG->print(RS_Debug::D_ERROR, "flags=%d\n",flags);
auto list=solveAppolloniusSingle(cs);
if(list.size()>=1){
for(RS_Circle& c0: list){
bool addNew=true;
for(RS_Circle& c: ret){
if((c0.getCenter()-c.getCenter()).squared()<RS_TOLERANCE15 && fabs(c0.getRadius() - c.getRadius())<RS_TOLERANCE){
addNew=false;
break;
}
}
if(addNew) ret.push_back(c0);
}
}
}while(++flags<8u);
// std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
// std::cout<<"before testing, ret.size()="<<ret.size()<<std::endl;
for(size_t i=0;i<ret.size();){
if(ret[i].testTan3(circles) == false) {
ret.erase(ret.begin()+i);
}else{
++i;
}
}
// DEBUG_HEADER
// std::cout<<"after testing, ret.size()="<<ret.size()<<std::endl;
return ret;
}
bool RS_Circle::testTan3(const std::vector<RS_AtomicEntity*>& circles)
{
if(circles.size()!=3) return false;
// std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
// std::cout<<"to verify Center = ( "<<data.center.x<<" , "<<data.center.y<<" ), r= "<<data.radius<<std::endl;
for(auto const& c: circles){
const double r0 = fabs(data.radius);
const double r1 = fabs(c->getRadius());
const double dist=fabs((data.center - c->getCenter()).magnitude());
// DEBUG_HEADER
// std::cout<<"testing: "<<getCenter()<<" r="<<getRadius()<<". \twith Center = ( "<<(*it)->getCenter().x<<" , "<<(*it)->getCenter().y<<" ), r= "<<(*it)->getRadius()<<std::endl;
// std::cout<<"r0="<<r0<<"\tr1="<<r1<<"\tdist="<<dist<<"\tdelta0="<<fabs(dist - fabs(r0 - r1)) <<"\tdelta1="<<fabs(dist - fabs(r0 + r1))
// <<"\t"<<sqrt(DBL_EPSILON)*qMax(r0,r1)<<std::endl;
double const rmax=std::max(r0,r1);
if( dist < rmax )
return fabs(dist - fabs(r0 - r1)) <= sqrt(DBL_EPSILON)*rmax;
else
return fabs(dist - fabs(r0 + r1)) <= sqrt(DBL_EPSILON)*rmax;
}
return true;
}
/** solve one of the eight Appollonius Equations
| Cx - Ci|^2=(Rx+Ri)^2
with Cx the center of the common tangent circle, Rx the radius. Ci and Ri are the Center and radius of the i-th existing circle
**/
std::vector<RS_Circle> RS_Circle::solveAppolloniusSingle(const std::vector<RS_Circle>& circles)
{
// std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
// for(int i=0;i<circles.size();i++){
//std::cout<<"i="<<i<<"\t center="<<circles[i].getCenter()<<"\tr="<<circles[i].getRadius()<<std::endl;
// }
std::vector<RS_Circle> ret;
std::vector<RS_Vector> centers;
std::vector<double> radii;
for(auto c: circles){
if(c.getCenter().valid==false) return ret;
centers.push_back(c.getCenter());
radii.push_back(c.getRadius());
}
// for(int i=0;i<circles.size();i++){
// std::cout<<"i="<<i<<"\t center="<<circles[i].getCenter()<<"\tr="<<radii.at(i)<<std::endl;
// }
/** form the linear equation to solve center in radius **/
std::vector<std::vector<double> > mat(2,std::vector<double>(3,0.));
mat[0][0]=centers[2].x - centers[0].x;
mat[0][1]=centers[2].y - centers[0].y;
mat[1][0]=centers[2].x - centers[1].x;
mat[1][1]=centers[2].y - centers[1].y;
if(fabs(mat[0][0]*mat[1][1] - mat[0][1]*mat[1][0])<RS_TOLERANCE2){
// DEBUG_HEADER
// std::cout<<"The provided circles are in a line, not common tangent circle"<<std::endl;
size_t i0=0;
if( centers[0].distanceTo(centers[1]) <= RS_TOLERANCE || centers[0].distanceTo(centers[2]) <= RS_TOLERANCE) i0 = 1;
LC_Quadratic lc0(& (circles[i0]), & (circles[(i0+1)%3]));
LC_Quadratic lc1(& (circles[i0]), & (circles[(i0+2)%3]));
auto c0 = LC_Quadratic::getIntersection(lc0, lc1);
// qDebug()<<"c0.size()="<<c0.size();
for(size_t i=0; i<c0.size(); i++){
const double dc = c0[i].distanceTo(centers[i0]);
ret.push_back(RS_Circle(nullptr, {c0[i], fabs(dc - radii[i0])}));
if( dc > radii[i0]) {
ret.push_back(RS_Circle(nullptr, {c0[i], dc + radii[i0]}));
}
}
return ret;
}
// r^0 term
mat[0][2]=0.5*(centers[2].squared()-centers[0].squared()+radii[0]*radii[0]-radii[2]*radii[2]);
mat[1][2]=0.5*(centers[2].squared()-centers[1].squared()+radii[1]*radii[1]-radii[2]*radii[2]);
// std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
// for(unsigned short i=0;i<=1;i++){
// std::cout<<"eqs P:"<<i<<" : "<<mat[i][0]<<"*x + "<<mat[i][1]<<"*y = "<<mat[i][2]<<std::endl;
// }
// std::vector<std::vector<double> > sm(2,std::vector<double>(2,0.));
std::vector<double> sm(2,0.);
if(RS_Math::linearSolver(mat,sm)==false){
return ret;
}
RS_Vector vp(sm[0],sm[1]);
// std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
// std::cout<<"vp="<<vp<<std::endl;
// r term
mat[0][2]= radii[0]-radii[2];
mat[1][2]= radii[1]-radii[2];
// for(unsigned short i=0;i<=1;i++){
// std::cout<<"eqs Q:"<<i<<" : "<<mat[i][0]<<"*x + "<<mat[i][1]<<"*y = "<<mat[i][2]<<std::endl;
// }
if(RS_Math::linearSolver(mat,sm)==false){
return ret;
}
RS_Vector vq(sm[0],sm[1]);
// std::cout<<"vq="<<vq<<std::endl;
//form quadratic equation for r
RS_Vector dcp=vp-centers[0];
double a=vq.squared()-1.;
if(fabs(a)<RS_TOLERANCE*1e-4) {
return ret;
}
std::vector<double> ce(0,0.);
ce.push_back(2.*(dcp.dotP(vq)-radii[0])/a);
ce.push_back((dcp.squared()-radii[0]*radii[0])/a);
std::vector<double>&& vr=RS_Math::quadraticSolver(ce);
for(size_t i=0; i < vr.size();i++){
if(vr.at(i)<RS_TOLERANCE) continue;
ret.emplace_back(RS_Circle(nullptr, {vp+vq*vr.at(i),fabs(vr.at(i))}));
}
// std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl;
// std::cout<<"Found "<<ret.size()<<" solutions"<<std::endl;
return ret;
}
RS_VectorSolutions RS_Circle::getRefPoints() const
{
RS_Vector v1(data.radius, 0.0);
RS_Vector v2(0.0, data.radius);
return RS_VectorSolutions ({data.center,
data.center+v1, data.center+v2,
data.center-v1, data.center-v2});
}
/**
* @brief compute nearest endpoint, intersection with X/Y axis at 0, 90, 180 and 270 degree
*
* Use getNearestMiddle() method to compute the nearest circle quadrant endpoints
*
* @param coord coordinates to compute, e.g. mouse cursor position
* @param dist double pointer to return distance between mouse pointer and nearest entity point
* @return the nearest intersection of the circle with X/Y axis.
*/
RS_Vector RS_Circle::getNearestEndpoint(const RS_Vector& coord, double* dist /*= nullptr*/) const
{
return getNearestMiddle( coord, dist, 0);
}
RS_Vector RS_Circle::getNearestPointOnEntity(const RS_Vector& coord,
bool /*onEntity*/, double* dist, RS_Entity** entity)const {
if (entity) {
*entity = const_cast<RS_Circle*>(this);
}
RS_Vector vp(coord - data.center);
double d(vp.magnitude());
if( d < RS_TOLERANCE ) return RS_Vector(false);
vp =data.center+vp*(data.radius/d);
// RS_DEBUG->print(RS_Debug::D_ERROR, "circle(%g, %g), r=%g: distance to point (%g, %g)\n",data.center.x,data.center.y,coord.x,coord.y);
if(dist){
*dist=coord.distanceTo(vp);
// RS_DEBUG->print(RS_Debug::D_ERROR, "circle(%g, %g), r=%g: distance to point (%g, %g)=%g\n",data.center.x,data.center.y,coord.x,coord.y,*dist);
}
return vp;
}
/**
*find the tangential points from a given point, i.e., the tangent lines should pass
* the given point and tangential points
*
*Author: Dongxu Li
*/
RS_VectorSolutions RS_Circle::getTangentPoint(const RS_Vector& point) const {
RS_VectorSolutions ret;
double r2(getRadius()*getRadius());
if(r2<RS_TOLERANCE2) return ret; //circle too small
RS_Vector vp(point-getCenter());
double c2(vp.squared());
if(c2<r2-getRadius()*2.*RS_TOLERANCE) {
//inside point, no tangential point
return ret;
}
if(c2>r2+getRadius()*2.*RS_TOLERANCE) {
//external point
RS_Vector vp1(-vp.y,vp.x);
vp1*=getRadius()*sqrt(c2-r2)/c2;
vp *= r2/c2;
vp += getCenter();
if(vp1.squared()>RS_TOLERANCE2) {
ret.push_back(vp+vp1);
ret.push_back(vp-vp1);
return ret;
}
}
ret.push_back(point);
return ret;
}
RS_Vector RS_Circle::getTangentDirection(const RS_Vector& point) const {
RS_Vector vp(point-getCenter());
// double c2(vp.squared());
// if(c2<r2-getRadius()*2.*RS_TOLERANCE) {
// //inside point, no tangential point
// return RS_Vector(false);
// }
return RS_Vector(-vp.y,vp.x);
}
RS_Vector RS_Circle::getNearestCenter(const RS_Vector& coord,
double* dist) const{
if (dist) {
*dist = coord.distanceTo(data.center);
}
return data.center;
}
RS_Vector RS_Circle::getMiddlePoint(void)const
{
return RS_Vector(false);
}
/**
* @brief compute middlePoints for each quadrant of a circle
*
* 0 middlePoints snaps to axis intersection at 0, 90, 180 and 270 degree (getNearestEndpoint) \n
* 1 middlePoints snaps to 45, 135, 225 and 315 degree \n
* 2 middlePoints snaps to 30, 60, 120, 150, 210, 240, 300 and 330 degree \n
* and so on
*
* @param coord coordinates to compute, e.g. mouse cursor position
* @param dist double pointer to return distance between mouse pointer and nearest entity point
* @param middlePoints number of middle points to compute per quadrant (0 for endpoints)
* @return the nearest of equidistant middle points of the circles quadrants.
*/
RS_Vector RS_Circle::getNearestMiddle(const RS_Vector& coord,
double* dist /*= nullptr*/,
const int middlePoints /*= 1*/) const
{
if( data.radius <= RS_TOLERANCE) {
//circle too short
if ( nullptr != dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
RS_Vector vPoint( getNearestPointOnEntity( coord, true, dist));
int iCounts = middlePoints + 1;
double dAngleSteps = M_PI_2 / iCounts;
double dAngleToPoint = data.center.angleTo(vPoint);
int iStepCount = static_cast<int>((dAngleToPoint + 0.5 * dAngleSteps) / dAngleSteps);
if( 0 < middlePoints) {
// for nearest middle eliminate start/endpoints
int iQuadrant = static_cast<int>(dAngleToPoint / 0.5 / M_PI);
int iQuadrantStep = iStepCount - iQuadrant * iCounts;
if( 0 == iQuadrantStep) {
++iStepCount;
}
else if( iCounts == iQuadrantStep) {
--iStepCount;
}
}
vPoint.setPolar( data.radius, dAngleSteps * iStepCount);
vPoint.move( data.center);
if(dist) {
*dist = vPoint.distanceTo( coord);
}
return vPoint;
}
RS_Vector RS_Circle::getNearestDist(double /*distance*/,
const RS_Vector& /*coord*/,
double* dist) const{
if (dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
RS_Vector RS_Circle::getNearestDist(double /*distance*/,
bool /*startp*/) const{
return RS_Vector(false);
}
RS_Vector RS_Circle::getNearestOrthTan(const RS_Vector& coord,
const RS_Line& normal,
bool /*onEntity = false*/) const
{
if ( !coord.valid) {
return RS_Vector(false);
}
RS_Vector vp0(coord-getCenter());
RS_Vector vp1(normal.getAngle1());
double d=RS_Vector::dotP(vp0,vp1);
if(d >= 0. ) {
return getCenter() + vp1*getRadius();
}else{
return getCenter() - vp1*getRadius();
}
}
void RS_Circle::move(const RS_Vector& offset) {
data.center.move(offset);
moveBorders(offset);
// calculateBorders();
}
/**
* this function creates offset
*@coord, position indicates the direction of offset
*@distance, distance of offset
* return true, if success, otherwise, false
*
*Author: Dongxu Li
*/
bool RS_Circle::offset(const RS_Vector& coord, const double& distance) {
double r0(coord.distanceTo(getCenter()));
if(r0 > getRadius()){
//external
r0 = getRadius()+ fabs(distance);
}else{
r0 = getRadius()- fabs(distance);
if(r0<RS_TOLERANCE) {
return false;
}
}
setRadius(r0);
calculateBorders();
return true;
}
void RS_Circle::rotate(const RS_Vector& center, const double& angle) {
data.center.rotate(center, angle);
calculateBorders();
}
void RS_Circle::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
data.center.rotate(center, angleVector);
calculateBorders();
}
void RS_Circle::scale(const RS_Vector& center, const RS_Vector& factor) {
data.center.scale(center, factor);
//radius always is positive
data.radius *= fabs(factor.x);
scaleBorders(center,factor);
// calculateBorders();
}
double RS_Circle::getDirection1() const{
return M_PI_2;
}
double RS_Circle::getDirection2() const{
return M_PI_2*3.0;
}
void RS_Circle::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
data.center.mirror(axisPoint1, axisPoint2);
calculateBorders();
}
/** whether the entity's bounding box intersects with visible portion of graphic view
//fix me, need to handle overlay container separately
*/
bool RS_Circle::isVisibleInWindow(RS_GraphicView* view) const
{
RS_Vector vpMin(view->toGraph(0,view->getHeight()));
RS_Vector vpMax(view->toGraph(view->getWidth(),0));
QPolygonF visualBox(QRectF(vpMin.x,vpMin.y,vpMax.x-vpMin.x, vpMax.y-vpMin.y));
std::vector<RS_Vector> vps;
for(unsigned short i=0;i<4;i++){
const QPointF& vp(visualBox.at(i));
vps.emplace_back(vp.x(),vp.y());
}
for(unsigned short i=0;i<4;i++){
RS_Line line{nullptr, {vps.at(i),vps.at((i+1)%4)}};
RS_Circle c0{nullptr, getData()};
if( RS_Information::getIntersection(&c0, &line, true).size()>0) return true;
}
if( getCenter().isInWindowOrdered(vpMin,vpMax)==false) return false;
return (vpMin-getCenter()).squared() > getRadius()*getRadius();
}
void RS_Circle::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) {
// // draw circle as a 2 pi arc
// RS_Arc arc(getParent(), RS_ArcData(getCenter(),getRadius(),0.,2.*M_PI, false));
// arc.setSelected(isSelected());
// arc.setPen(getPen());
// arc.draw(painter,view,patternOffset);
painter->drawCircle(view->toGui(getCenter()), view->toGuiDX(getRadius()));
}
void RS_Circle::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
if(ref.distanceTo(data.center)<1.0e-4){
data.center += offset;
return;
}
RS_Vector v1(data.radius, 0.0);
RS_VectorSolutions sol;
sol.push_back(data.center + v1);
sol.push_back(data.center - v1);
v1.set(0., data.radius);
sol.push_back(data.center + v1);
sol.push_back(data.center - v1);
double dist;
v1=sol.getClosest(ref,&dist);
if(dist>1.0e-4) return;
data.radius = data.center.distanceTo(v1 + offset);
}
/** return the equation of the entity
for quadratic,
return a vector contains:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
LC_Quadratic RS_Circle::getQuadratic() const
{
std::vector<double> ce(6,0.);
ce[0]=1.;
ce[2]=1.;
ce[5]=-data.radius*data.radius;
LC_Quadratic ret(ce);
ret.move(data.center);
return ret;
}
/**
* @brief Returns area of full circle
* Note: Circular arcs are handled separately by RS_Arc (areaLIneIntegral)
* However, full ellipses and ellipse arcs are handled by RS_Ellipse
* @return \pi r^2
*/
double RS_Circle::areaLineIntegral() const
{
const double r = getRadius();
return M_PI*r*r;
}
/**
* Dumps the circle's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Circle& a) {
os << " Circle: " << a.data << "\n";
return os;
}

186
lib/engine/rs_circle.h Normal file
View File

@@ -0,0 +1,186 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 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!
**
**********************************************************************/
#ifndef RS_CIRCLE_H
#define RS_CIRCLE_H
#include <vector>
#include "rs_atomicentity.h"
class LC_Quadratic;
/**
* Holds the data that defines a circle.
*/
struct RS_CircleData {
RS_CircleData() = default;
RS_CircleData(RS_Vector const& center, double radius);
bool isValid() const;
bool operator == (RS_CircleData const&) const;
RS_Vector center;
double radius;
};
std::ostream& operator << (std::ostream& os, const RS_CircleData& ad);
/**
* Class for a circle entity.
*
* @author Andrew Mustun
*/
class RS_Circle : public RS_AtomicEntity {
public:
RS_Circle()=default;
RS_Circle (RS_EntityContainer* parent,
const RS_CircleData& d);
~RS_Circle() = default;
RS_Entity* clone() const override;
/** @return RS2::EntityCircle */
RS2::EntityType rtti() const override{
return RS2::EntityCircle;
}
/** @return true */
bool isEdge() const override{
return true;
}
/** @return Copy of data that defines the circle. **/
const RS_CircleData& getData() const {
return data;
}
RS_VectorSolutions getRefPoints() const override;
//no start/end point for whole circle
// RS_Vector getStartpoint() const {
// return data.center + RS_Vector(data.radius, 0.0);
// }
// RS_Vector getEndpoint() const {
// return data.center + RS_Vector(data.radius, 0.0);
// }
/**
* @return Direction 1. The angle at which the arc starts at
* the startpoint.
*/
double getDirection1() const override;
/**
* @return Direction 2. The angle at which the arc starts at
* the endpoint.
*/
double getDirection2() const override;
/** @return The center point (x) of this arc */
RS_Vector getCenter() const override;
/** Sets new center. */
void setCenter(const RS_Vector& c);
/** @return The radius of this arc */
double getRadius() const override;
/** Sets new radius. */
void setRadius(double r);
double getAngleLength() const;
double getLength() const override;
bool isTangent(const RS_CircleData& circleData) const override;
bool createFromCR(const RS_Vector& c, double r);
bool createFrom2P(const RS_Vector& p1, const RS_Vector& p2);
bool createFrom3P(const RS_Vector& p1, const RS_Vector& p2,
const RS_Vector& p3);
bool createFrom3P(const RS_VectorSolutions& sol);
bool createInscribe(const RS_Vector& coord, const std::vector<RS_Line*>& lines);
std::vector<RS_Entity* > offsetTwoSides(const double& distance) const override;
RS_VectorSolutions createTan1_2P(const RS_AtomicEntity* circle, const std::vector<RS_Vector>& points);
static RS_VectorSolutions createTan2(const std::vector<RS_AtomicEntity*>& circles, const double& r);
/** solve one of the eight Appollonius Equations
| Cx - Ci|^2=(Rx+Ri)^2
with Cx the center of the common tangent circle, Rx the radius. Ci and Ri are the Center and radius of the i-th existing circle
**/
static std::vector<RS_Circle> solveAppolloniusSingle(const std::vector<RS_Circle>& circles);
std::vector<RS_Circle> createTan3(const std::vector<RS_AtomicEntity*>& circles);
bool testTan3(const std::vector<RS_AtomicEntity*>& circles);
RS_Vector getMiddlePoint(void)const override;
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true, double* dist = NULL, RS_Entity** entity=NULL)const override;
RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = NULL)const override;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1 ) const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = NULL)const override;
RS_Vector getNearestDist(double distance,
bool startp)const override;
RS_Vector getNearestOrthTan(const RS_Vector& coord,
const RS_Line& normal,
bool onEntity = false) const override;
bool offset(const RS_Vector& coord, const double& distance) override;
RS_VectorSolutions getTangentPoint(const RS_Vector& point) const override;//find the tangential points seeing from given point
RS_Vector getTangentDirection(const RS_Vector& point)const override;
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
/** whether the entity's bounding box intersects with visible portion of graphic view */
bool isVisibleInWindow(RS_GraphicView* view) const override;
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
/** return the equation of the entity
for quadratic,
return a vector contains:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
LC_Quadratic getQuadratic() const override;
/**
* @brief Returns area of full circle
* Note: Circular arcs are handled separately by RS_Arc (areaLIneIntegral)
* However, full ellipses and ellipse arcs are handled by RS_Ellipse
* @return \pi r^2
*/
double areaLineIntegral() const override;
friend std::ostream& operator << (std::ostream& os, const RS_Circle& a);
void calculateBorders() override;
protected:
RS_CircleData data;
};
#endif

View File

@@ -0,0 +1,91 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_clipboard.h"
#include "rs_block.h"
#include "rs_layer.h"
#include "rs_entity.h"
RS_Clipboard* RS_Clipboard::uniqueInstance = NULL;
void RS_Clipboard::clear() {
graphic.clear();
graphic.clearBlocks();
graphic.clearLayers();
graphic.clearVariables();
}
void RS_Clipboard::addBlock(RS_Block* b) {
if (b) {
graphic.addBlock(b, false);
}
}
bool RS_Clipboard::hasBlock(const QString& name) {
return (graphic.findBlock(name));
}
void RS_Clipboard::addLayer(RS_Layer* l) {
if (l) {
//graphic.addLayer(l->clone());
graphic.addLayer(l);
}
}
bool RS_Clipboard::hasLayer(const QString& name) {
return (graphic.findLayer(name));
}
void RS_Clipboard::addEntity(RS_Entity* e) {
if (e) {
//graphic.addEntity(e->clone());
graphic.addEntity(e);
e->reparent(&graphic);
}
}
/**
* Dumps the clipboard contents to stdout.
*/
std::ostream& operator << (std::ostream& os, RS_Clipboard& cb) {
os << "Clipboard: " << cb.graphic << "\n";
return os;
}

112
lib/engine/rs_clipboard.h Normal file
View File

@@ -0,0 +1,112 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_CLIPBOARD_H
#define RS_CLIPBOARD_H
#include <iosfwd>
#include "rs_graphic.h"
#define RS_CLIPBOARD RS_Clipboard::instance()
class RS_Block;
class RS_Layer;
class RS_Entity;
/**
* LibreCAD internal clipboard. We don't use the system clipboard for
* better portaility.
* Implemented as singleton.
*
* @author Andrew Mustun
*/
class RS_Clipboard {
protected:
RS_Clipboard() {
}
public:
/**
* @return Instance to the unique clipboard object.
*/
static RS_Clipboard* instance() {
if (uniqueInstance==NULL) {
uniqueInstance = new RS_Clipboard();
}
return uniqueInstance;
}
void clear();
void addBlock(RS_Block* b);
bool hasBlock(const QString& name);
int countBlocks() {
return graphic.countBlocks();
}
RS_Block* blockAt(int i) {
return graphic.blockAt(i);
}
void addLayer(RS_Layer* l);
bool hasLayer(const QString& name);
int countLayers() {
return graphic.countLayers();
}
RS_Layer* layerAt(int i) {
return graphic.layerAt(i);
}
void addEntity(RS_Entity* e);
unsigned count() {
return graphic.count();
}
RS_Entity* entityAt(unsigned i) {
return graphic.entityAt(i);
}
RS_Entity* firstEntity() {
return graphic.firstEntity();
}
RS_Entity* nextEntity() {
return graphic.nextEntity();
}
RS_Graphic* getGraphic() {
return &graphic;
}
friend std::ostream& operator << (std::ostream& os, RS_Clipboard& cb);
protected:
static RS_Clipboard* uniqueInstance;
RS_Graphic graphic;
};
#endif

94
lib/engine/rs_color.cpp Normal file
View File

@@ -0,0 +1,94 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2020 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_color.h"
//This method is used for plugins
int RS_Color::toIntColor(void) const {
if (isByLayer())
return -1;
if (isByBlock())
return -2;
// int tmp1 = red() << 16;
// int tmp2 = green() << 8;
// int tmp3 = tmp1+tmp2+blue();
int cd = (red() << 16) + (green() << 8) + blue();
return cd;
}
//This method is used for plugins
void RS_Color::fromIntColor(int co) {
if (co == -1)
setFlags(RS2::FlagByLayer);
else if (co == -2)
setFlags(RS2::FlagByBlock);
else {
setRed((co >> 16) & 0xFF);
setGreen((co >> 8) & 0xFF);
setBlue(co & 0xFF);
}
}
/**
* Color distance
*
* Calculate distance between two RGB colors using low-cost approximation of
* human eye response by Thiadmer Riemersma. Formula explanation found here:
* https://www.compuphase.com/cmetric.htm
*
* @author Thiadmer Riemersma
* @author Jeremy Ruhland
*
* @param c Color to perform comparison against
*
* @return Distance between colors in percent, value ranging from 0 (identical)
* to 100 (maximum difference)
*/
int RS_Color::colorDistance(const RS_Color& c) const {
int myRed {red()};
int otherRed {c.red()};
int redMean {(myRed + otherRed) / 2};
// Convert difference value to percentage using maximum color difference (764.834 / 100)
return std::lround( std::sqrt( std::pow(otherRed - myRed, 2) * (512 + redMean) / 256
+ std::pow(c.green() - green(), 2) * 4
+ std::pow(c.blue() - blue(), 2) * (767 - redMean) / 256)
/ 7.64834);
}
std::ostream& operator << (std::ostream& os, const RS_Color& c) {
os << " color: " << c.name().toLatin1().data()
<< " flags: " << (c.getFlag(RS2::FlagByLayer) ? "RS2::FlagByLayer " : "")
<< (c.getFlag(RS2::FlagByBlock) ? "RS2::FlagByBlock " : "");
return os;
}

114
lib/engine/rs_color.h Normal file
View File

@@ -0,0 +1,114 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2020 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!
**
**********************************************************************/
#ifndef RS_COLOR_H
#define RS_COLOR_H
#include <QColor>
#include "rs.h"
#include "rs_flags.h"
//! Color defined by layer not entity
//#define C_BY_LAYER 0x00000001
//! Color defined by block not entity
//#define C_BY_BLOCK 0x00000002
/**
* Color class.
*
* @author Andrew Mustun
*/
class RS_Color: public QColor, public RS_Flags {
public:
RS_Color() : QColor(), RS_Flags() {}
RS_Color(int r, int g, int b) : QColor(r, g, b), RS_Flags() {}
RS_Color(int r, int g, int b, int a) : QColor(r, g, b, a), RS_Flags() {}
RS_Color(const QColor& c) : QColor(c), RS_Flags() {}
RS_Color(const Qt::GlobalColor color) : QColor(color), RS_Flags() {}
RS_Color(const RS_Color& c) : QColor(c), RS_Flags() {
setFlags(c.getFlags());
}
RS_Color(unsigned int f) : QColor(), RS_Flags(f) {}
RS_Color(QString name) : QColor(name), RS_Flags() {}
/** @return A copy of this color without flags. */
RS_Color stripFlags() const {
return RS_Color(red(), green(), blue());
}
/** @return true if the color is defined by layer. */
bool isByLayer() const {
return getFlag(RS2::FlagByLayer);
}
/** @return true if the color is defined by block. */
bool isByBlock() const {
return getFlag(RS2::FlagByBlock);
}
QColor toQColor(void) const {
QColor c0;
c0.setRgb(red(),green(),blue());
return c0;
}
//These 3 methods are used for plugins
int toIntColor(void) const;
void fromIntColor(int co);
int colorDistance(const RS_Color& c) const;
enum {
Black = 0,
/**
* Minimum acceptable distance between two colors before visibility
* enhancement is required. Determined empirically.
*/
MinColorDistance = 20, //< in %
};
RS_Color& operator = (const RS_Color& c) {
setRgb(c.red(), c.green(), c.blue());
setFlags(c.getFlags());
return *this;
}
bool operator == (const RS_Color& c) const {
return (red()==c.red() &&
green()==c.green() &&
blue()==c.blue() &&
getFlags()==c.getFlags());
}
friend std::ostream& operator << (std::ostream& os, const RS_Color& c);
};
#endif

View File

@@ -0,0 +1,286 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_constructionline.h"
#include "rs_debug.h"
#include "lc_quadratic.h"
#include "rs_math.h"
RS_ConstructionLineData::RS_ConstructionLineData():
point1(false),
point2(false)
{}
RS_ConstructionLineData::RS_ConstructionLineData(const RS_Vector& point1,
const RS_Vector& point2):
point1(point1)
,point2(point2)
{
}
std::ostream& operator << (std::ostream& os,
const RS_ConstructionLineData& ld)
{
os << "(" << ld.point1 <<
"/" << ld.point2 <<
")";
return os;
}
/**
* Constructor.
*/
RS_ConstructionLine::RS_ConstructionLine(RS_EntityContainer* parent,
const RS_ConstructionLineData& d)
:RS_AtomicEntity(parent), data(d) {
calculateBorders();
}
RS_Entity* RS_ConstructionLine::clone() const {
RS_ConstructionLine* c = new RS_ConstructionLine(*this);
c->initId();
return c;
}
void RS_ConstructionLine::calculateBorders() {
minV = RS_Vector::minimum(data.point1, data.point2);
maxV = RS_Vector::maximum(data.point1, data.point2);
}
RS_Vector RS_ConstructionLine::getNearestEndpoint(const RS_Vector& coord,
double* dist) const{
double dist1, dist2;
dist1 = (data.point1-coord).squared();
dist2 = (data.point2-coord).squared();
if (dist2<dist1) {
if (dist) {
*dist = sqrt(dist2);
}
return data.point2;
} else {
if (dist) {
*dist = sqrt(dist1);
}
return data.point1;
}
}
RS_Vector RS_ConstructionLine::getNearestPointOnEntity(const RS_Vector& coord,
bool /*onEntity*/, double* /*dist*/, RS_Entity** entity) const{
if (entity) {
*entity = const_cast<RS_ConstructionLine*>(this);
}
RS_Vector ae = data.point2-data.point1;
RS_Vector ea = data.point1-data.point2;
RS_Vector ap = coord-data.point1;
// RS_Vector ep = coord-data.point2;
if (ae.magnitude()<1.0e-6 || ea.magnitude()<1.0e-6) {
return RS_Vector(false);
}
// Orthogonal projection from both sides:
RS_Vector ba = ae * RS_Vector::dotP(ae, ap)
/ (ae.magnitude()*ae.magnitude());
// RS_Vector be = ea * RS_Vector::dotP(ea, ep)
// / (ea.magnitude()*ea.magnitude());
return data.point1+ba;
}
RS_Vector RS_ConstructionLine::getNearestCenter(const RS_Vector& /*coord*/,
double* dist) const{
if (dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
/** @return Copy of data that defines the line. */
RS_ConstructionLineData const& RS_ConstructionLine::getData() const {
return data;
}
/** @return First definition point. */
RS_Vector const& RS_ConstructionLine::getPoint1() const {
return data.point1;
}
/** @return Second definition point. */
RS_Vector const& RS_ConstructionLine::getPoint2() const {
return data.point2;
}
/** @return Start point of the entity */
RS_Vector RS_ConstructionLine::getStartpoint() const
{
return data.point1;
}
/** @return End point of the entity */
RS_Vector RS_ConstructionLine::getEndpoint() const
{
return data.point2;
}
/**
* @return Direction 1. The angle at which the arc starts at
* the startpoint.
*/
double RS_ConstructionLine::getDirection1(void) const
{
return RS_Math::correctAngle( data.point1.angleTo( data.point2));
}
/**
* @return Direction 2. The angle at which the arc starts at
* the endpoint.
*/
double RS_ConstructionLine::getDirection2(void) const
{
return RS_Math::correctAngle( data.point2.angleTo( data.point1));
}
/** return the equation of the entity
for quadratic,
return a vector contains:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
LC_Quadratic RS_ConstructionLine::getQuadratic() const
{
std::vector<double> ce(3,0.);
auto dvp=data.point2 - data.point1;
RS_Vector normal(-dvp.y,dvp.x);
ce[0]=normal.x;
ce[1]=normal.y;
ce[2]= -normal.dotP(data.point2);
return LC_Quadratic(ce);
}
RS_Vector RS_ConstructionLine::getMiddlePoint() const{
return RS_Vector(false);
}
RS_Vector RS_ConstructionLine::getNearestMiddle(const RS_Vector& /*coord*/,
double* dist, const int /*middlePoints*/)const {
if (dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
RS_Vector RS_ConstructionLine::getNearestDist(double /*distance*/,
const RS_Vector& /*coord*/,
double* dist) const{
if (dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
double RS_ConstructionLine::getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity,
RS2::ResolveLevel /*level*/, double /*solidDist*/) const {
RS_DEBUG->print("RS_ConstructionLine::getDistanceToPoint");
if (entity) {
*entity = const_cast<RS_ConstructionLine*>(this);
}
//double dist = RS_MAXDOUBLE;
RS_Vector se = data.point2-data.point1;
double d(se.magnitude());
if(d<RS_TOLERANCE) {
//line too short
return RS_MAXDOUBLE;
}
se.set( se.x/d,-se.y/d); //normalized
RS_Vector vpc= coord - data.point1;
vpc.rotate(se); // rotate to use the line as x-axis, and the distance is fabs(y)
return ( fabs(vpc.y) );
}
void RS_ConstructionLine::move(const RS_Vector& offset) {
data.point1.move(offset);
data.point2.move(offset);
//calculateBorders();
}
void RS_ConstructionLine::rotate(const RS_Vector& center, const double& angle) {
RS_Vector angleVector(angle);
data.point1.rotate(center, angleVector);
data.point2.rotate(center, angleVector);
//calculateBorders();
}
void RS_ConstructionLine::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
data.point1.rotate(center, angleVector);
data.point2.rotate(center, angleVector);
//calculateBorders();
}
void RS_ConstructionLine::scale(const RS_Vector& center, const RS_Vector& factor) {
data.point1.scale(center, factor);
data.point2.scale(center, factor);
//calculateBorders();
}
void RS_ConstructionLine::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
data.point1.mirror(axisPoint1, axisPoint2);
data.point2.mirror(axisPoint1, axisPoint2);
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_ConstructionLine& l) {
os << " ConstructionLine: " << l.getData() << "\n";
return os;
}

View File

@@ -0,0 +1,140 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_CONSTRUCTIONLINE_H
#define RS_CONSTRUCTIONLINE_H
#include "rs_atomicentity.h"
#include "rs_vector.h"
/**
* Holds the data that defines a construction line (a line
* which is not limited to both directions).
*/
class RS_ConstructionLineData {
public:
/**
* Default constructor
*/
RS_ConstructionLineData();
RS_ConstructionLineData(const RS_Vector& point1,
const RS_Vector& point2);
friend class RS_ConstructionLine;
friend std::ostream& operator << (std::ostream& os,
const RS_ConstructionLineData& ld);
private:
RS_Vector point1;
RS_Vector point2;
};
/**
* Class for a construction line entity.
*
* @author Andrew Mustun
*/
class RS_ConstructionLine : public RS_AtomicEntity {
public:
RS_ConstructionLine()=default;
RS_ConstructionLine(RS_EntityContainer* parent,
const RS_ConstructionLineData& d);
virtual RS_Entity* clone() const;
virtual ~RS_ConstructionLine()=default;
/** @return RS2::EntityConstructionLine */
virtual RS2::EntityType rtti() const {
return RS2::EntityConstructionLine;
}
/** @return Copy of data that defines the line. */
RS_ConstructionLineData const& getData() const;
/** @return First definition point. */
RS_Vector const& getPoint1() const;
/** @return Second definition point. */
RS_Vector const& getPoint2() const;
/** @return Start point of the entity */
RS_Vector getStartpoint() const override;
/** @return End point of the entity */
RS_Vector getEndpoint() const override;
double getDirection1(void) const override;
double getDirection2(void) const override;
/** return the equation of the entity
for quadratic,
return a vector contains:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
virtual LC_Quadratic getQuadratic() const;
virtual RS_Vector getMiddlePoint(void) const;
virtual RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = NULL)const;
virtual RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true, double* dist = NULL, RS_Entity** entity=NULL)const;
virtual RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = NULL)const;
virtual RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = NULL,
int middlePoints = 1)const;
virtual RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = NULL)const;
virtual double getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity=NULL,
RS2::ResolveLevel level=RS2::ResolveNone,
double solidDist = RS_MAXDOUBLE) const;
virtual void move(const RS_Vector& offset);
virtual void rotate(const RS_Vector& center, const double& angle);
virtual void rotate(const RS_Vector& center, const RS_Vector& angleVector);
virtual void scale(const RS_Vector& center, const RS_Vector& factor);
virtual void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2);
virtual void draw(RS_Painter* /*painter*/, RS_GraphicView* /*view*/,
double& /*patternOffset*/) {}
friend std::ostream& operator << (std::ostream& os,
const RS_ConstructionLine& l);
virtual void calculateBorders();
protected:
RS_ConstructionLineData data;
};
#endif

View File

@@ -0,0 +1,380 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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_dimaligned.h"
#include "rs_line.h"
#include "rs_graphic.h"
#include "rs_units.h"
#include "rs_constructionline.h"
#include "rs_math.h"
#include "rs_debug.h"
RS_DimAlignedData::RS_DimAlignedData():
extensionPoint1(false),
extensionPoint2(false)
{}
/**
* Constructor with initialisation.
*
* @para extensionPoint1 Definition point. Startpoint of the
* first extension line.
* @para extensionPoint2 Definition point. Startpoint of the
* second extension line.
*/
RS_DimAlignedData::RS_DimAlignedData(const RS_Vector& _extensionPoint1,
const RS_Vector& _extensionPoint2):
extensionPoint1(_extensionPoint1)
,extensionPoint2(_extensionPoint2)
{
}
std::ostream& operator << (std::ostream& os,
const RS_DimAlignedData& dd) {
os << "(" << dd.extensionPoint1 << "/" << dd.extensionPoint1 << ")";
return os;
}
/**
* Constructor.
*
* @para parent Parent Entity Container.
* @para d Common dimension geometrical data.
* @para ed Extended geometrical data for aligned dimension.
*/
RS_DimAligned::RS_DimAligned(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimAlignedData& ed)
: RS_Dimension(parent, d), edata(ed) {
calculateBorders();
}
/**
* Sets a new text. The entities representing the
* text are updated.
*/
//void RS_DimAligned::setText(const QString& t) {
// data.text = t;
// update();
//}
RS_Entity* RS_DimAligned::clone() const{
RS_DimAligned* d = new RS_DimAligned(*this);
d->setOwner(isOwner());
d->initId();
d->detach();
return d;
}
RS_VectorSolutions RS_DimAligned::getRefPoints() const
{
return RS_VectorSolutions({edata.extensionPoint1, edata.extensionPoint2,
data.definitionPoint, data.middleOfText});
}
/**
* @return Automatically created label for the default
* measurement of this dimension.
*/
QString RS_DimAligned::getMeasuredLabel() {
double dist = edata.extensionPoint1.distanceTo(edata.extensionPoint2) * getGeneralFactor();
RS_Graphic* graphic = getGraphic();
QString ret;
if (graphic) {
int dimlunit = getGraphicVariableInt("$DIMLUNIT", 2);
int dimdec = getGraphicVariableInt("$DIMDEC", 4);
int dimzin = getGraphicVariableInt("$DIMZIN", 1);
RS2::LinearFormat format = graphic->getLinearFormat(dimlunit);
ret = RS_Units::formatLinear(dist, RS2::None, format, dimdec);
if (format == RS2::Decimal)
ret = stripZerosLinear(ret, dimzin);
//verify if units are decimal and comma separator
if (format == RS2::Decimal || format == RS2::ArchitecturalMetric){
if (getGraphicVariableInt("$DIMDSEP", 0) == 44)
ret.replace(QChar('.'), QChar(','));
}
}
else {
ret = QString("%1").arg(dist);
}
return ret;
}
RS_DimAlignedData const& RS_DimAligned::getEData() const {
return edata;
}
RS_Vector const& RS_DimAligned::getExtensionPoint1() const {
return edata.extensionPoint1;
}
RS_Vector const& RS_DimAligned::getExtensionPoint2() const {
return edata.extensionPoint2;
}
/**
* Updates the sub entities of this dimension. Called when the
* text or the position, alignment, .. changes.
*
* @param autoText Automatically reposition the text label
*/
void RS_DimAligned::updateDim(bool autoText) {
RS_DEBUG->print("RS_DimAligned::update");
clear();
if (isUndone()) {
return;
}
// general scale (DIMSCALE)
double dimscale = getGeneralScale();
// distance from entities (DIMEXO)
double dimexo = getExtensionLineOffset()*dimscale;
// definition line definition (DIMEXE)
double dimexe = getExtensionLineExtension()*dimscale;
// text height (DIMTXT)
//double dimtxt = getTextHeight();
// text distance to line (DIMGAP)
//double dimgap = getDimensionLineGap();
// Angle from extension endpoints towards dimension line
double extAngle = edata.extensionPoint2.angleTo(data.definitionPoint);
// extension lines length
double extLength = edata.extensionPoint2.distanceTo(data.definitionPoint);
if (getFixedLengthOn()){
double dimfxl = getFixedLength()*dimscale;
if (extLength-dimexo > dimfxl)
dimexo = extLength - dimfxl;
}
RS_Vector v1 = RS_Vector::polar(dimexo, extAngle);
RS_Vector v2 = RS_Vector::polar(dimexe, extAngle);
RS_Vector e1 = RS_Vector::polar(1.0, extAngle);
RS_Pen pen(getExtensionLineColor(),
getExtensionLineWidth(),
RS2::LineByBlock);
// Extension line 1:
RS_Line* line = new RS_Line{this,
edata.extensionPoint1 + v1,
edata.extensionPoint1 + e1*extLength + v2};
//line->setLayerToActive();
//line->setPenToActive();
// line->setPen(RS_Pen(RS2::FlagInvalid));
line->setPen(pen);
line->setLayer(nullptr);
addEntity(line);
// Extension line 2:
line = new RS_Line{this,
edata.extensionPoint2 + v1,
edata.extensionPoint2 + e1*extLength + v2};
//line->setLayerToActive();
//line->setPenToActive();
// line->setPen(RS_Pen(RS2::FlagInvalid));
line->setPen(pen);
line->setLayer(nullptr);
addEntity(line);
// Dimension line:
updateCreateDimensionLine(edata.extensionPoint1 + e1*extLength,
edata.extensionPoint2 + e1*extLength,
true, true, autoText);
calculateBorders();
}
void RS_DimAligned::updateDimPoint(){
// temporary construction line
RS_ConstructionLine tmpLine( nullptr,
RS_ConstructionLineData(edata.extensionPoint1, edata.extensionPoint2));
RS_Vector tmpP1 = tmpLine.getNearestPointOnEntity(data.definitionPoint);
data.definitionPoint += edata.extensionPoint2 - tmpP1;
}
bool RS_DimAligned::hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2) {
return (edata.extensionPoint1.isInWindow(v1, v2) ||
edata.extensionPoint2.isInWindow(v1, v2));
}
void RS_DimAligned::move(const RS_Vector& offset) {
RS_Dimension::move(offset);
edata.extensionPoint1.move(offset);
edata.extensionPoint2.move(offset);
update();
}
void RS_DimAligned::rotate(const RS_Vector& center, const double& angle) {
rotate(center,RS_Vector(angle));
}
void RS_DimAligned::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_Dimension::rotate(center, angleVector);
edata.extensionPoint1.rotate(center, angleVector);
edata.extensionPoint2.rotate(center, angleVector);
update();
}
void RS_DimAligned::scale(const RS_Vector& center, const RS_Vector& factor) {
RS_Dimension::scale(center, factor);
edata.extensionPoint1.scale(center, factor);
edata.extensionPoint2.scale(center, factor);
update();
}
void RS_DimAligned::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
RS_Dimension::mirror(axisPoint1, axisPoint2);
edata.extensionPoint1.mirror(axisPoint1, axisPoint2);
edata.extensionPoint2.mirror(axisPoint1, axisPoint2);
update();
}
void RS_DimAligned::stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) {
//e->calculateBorders();
if (getMin().isInWindow(firstCorner, secondCorner) &&
getMax().isInWindow(firstCorner, secondCorner)) {
move(offset);
}
else {
//RS_Vector v = data.definitionPoint - edata.extensionPoint2;
double len = edata.extensionPoint2.distanceTo(data.definitionPoint);
double ang1 = edata.extensionPoint1.angleTo(edata.extensionPoint2)
+ M_PI_2;
if (edata.extensionPoint1.isInWindow(firstCorner,
secondCorner)) {
edata.extensionPoint1.move(offset);
}
if (edata.extensionPoint2.isInWindow(firstCorner,
secondCorner)) {
edata.extensionPoint2.move(offset);
}
double ang2 = edata.extensionPoint1.angleTo(edata.extensionPoint2)
+ M_PI_2;
double diff = RS_Math::getAngleDifference(ang1, ang2);
if (diff>M_PI) {
diff-=2*M_PI;
}
if (fabs(diff)>M_PI_2) {
ang2 = RS_Math::correctAngle(ang2+M_PI);
}
RS_Vector v = RS_Vector::polar(len, ang2);
data.definitionPoint = edata.extensionPoint2 + v;
}
updateDim(true);
}
void RS_DimAligned::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
if (ref.distanceTo(data.definitionPoint)<1.0e-4) {
RS_ConstructionLine l(nullptr,
RS_ConstructionLineData(edata.extensionPoint1,
edata.extensionPoint2));
double d = l.getDistanceToPoint(data.definitionPoint+offset);
double a = edata.extensionPoint2.angleTo(data.definitionPoint);
double ad = RS_Math::getAngleDifference(a,
edata.extensionPoint2.angleTo(data.definitionPoint+offset));
if (fabs(ad)>M_PI_2 && fabs(ad)<3.0/2.0*M_PI) {
a = RS_Math::correctAngle(a+M_PI);
}
RS_Vector v = RS_Vector::polar(d, a);
data.definitionPoint = edata.extensionPoint2 + v;
updateDim(true);
}
else if (ref.distanceTo(data.middleOfText)<1.0e-4) {
data.middleOfText.move(offset);
updateDim(false);
}
else if (ref.distanceTo(edata.extensionPoint1)<1.0e-4) {
double a1 = edata.extensionPoint2.angleTo(edata.extensionPoint1);
double a2 = edata.extensionPoint2.angleTo(edata.extensionPoint1+offset);
double d1 = edata.extensionPoint2.distanceTo(edata.extensionPoint1);
double d2 = edata.extensionPoint2.distanceTo(edata.extensionPoint1+offset);
rotate(edata.extensionPoint2, a2-a1);
if (fabs(d1)>1.0e-4) {
scale(edata.extensionPoint2, RS_Vector(d2/d1, d2/d1));
}
updateDim(true);
}
else if (ref.distanceTo(edata.extensionPoint2)<1.0e-4) {
double a1 = edata.extensionPoint1.angleTo(edata.extensionPoint2);
double a2 = edata.extensionPoint1.angleTo(edata.extensionPoint2+offset);
double d1 = edata.extensionPoint1.distanceTo(edata.extensionPoint2);
double d2 = edata.extensionPoint1.distanceTo(edata.extensionPoint2+offset);
rotate(edata.extensionPoint1, a2-a1);
if (fabs(d1)>1.0e-4) {
scale(edata.extensionPoint1, RS_Vector(d2/d1, d2/d1));
}
updateDim(true);
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_DimAligned& d) {
os << " DimAligned: " << d.getData() << "\n" << d.getEData() << "\n";
return os;
}

119
lib/engine/rs_dimaligned.h Normal file
View File

@@ -0,0 +1,119 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_DIMALIGNED_H
#define RS_DIMALIGNED_H
#include "rs_dimension.h"
/**
* Holds the data that defines an aligned dimension entity.
*/
struct RS_DimAlignedData {
/**
* Default constructor
*/
RS_DimAlignedData();
/**
* Constructor with initialisation.
*
* @para extensionPoint1 Definition point. Startpoint of the
* first extension line.
* @para extensionPoint2 Definition point. Startpoint of the
* second extension line.
*/
RS_DimAlignedData(const RS_Vector& extensionPoint1,
const RS_Vector& extensionPoint2);
/** Definition point. Startpoint of the first extension line. */
RS_Vector extensionPoint1;
/** Definition point. Startpoint of the second extension line. */
RS_Vector extensionPoint2;
};
std::ostream& operator << (std::ostream& os, const RS_DimAlignedData& dd);
/**
* Class for aligned dimension entities.
*
* @author Andrew Mustun
*/
class RS_DimAligned : public RS_Dimension {
public:
RS_DimAligned(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimAlignedData& ed);
RS_Entity* clone() const override;
/** @return RS2::EntityDimAligned */
RS2::EntityType rtti() const override{
return RS2::EntityDimAligned;
}
/**
* @return Copy of data that defines the aligned dimension.
* @see getData()
*/
RS_DimAlignedData const& getEData() const;
RS_VectorSolutions getRefPoints() const override;
QString getMeasuredLabel() override;
void updateDim(bool autoText=false) override;
RS_Vector const& getExtensionPoint1() const;
RS_Vector const& getExtensionPoint2() const;
/**
* Recalculate the original Dimension Point to remove Dim oblique angle.
* @author Rallaz
*/
void updateDimPoint();
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
bool hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2) override;
void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
friend std::ostream& operator << (std::ostream& os,
const RS_DimAligned& d);
protected:
/** Extended data. */
RS_DimAlignedData edata;
};
#endif

View File

@@ -0,0 +1,580 @@
/****************************************************************************
**
** 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;
}

193
lib/engine/rs_dimangular.h Normal file
View File

@@ -0,0 +1,193 @@
/****************************************************************************
**
** 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!
**
**********************************************************************/
#ifndef RS_DIMANGULAR_H
#define RS_DIMANGULAR_H
#include "rs_dimension.h"
#include "rs_constructionline.h"
/**
* Holds the data that defines a angular dimension entity.
*/
struct RS_DimAngularData
{
RS_DimAngularData();
RS_DimAngularData(const RS_DimAngularData& ed);
/**
* Constructor with initialisation.
*
* @param definitionPoint Definition point of the angular dimension.
* @param leader Leader length.
*/
RS_DimAngularData(const RS_Vector& definitionPoint1,
const RS_Vector& definitionPoint2,
const RS_Vector& definitionPoint3,
const RS_Vector& definitionPoint4);
RS_Vector definitionPoint1; ///< 1st line start point, DXF codes 13,23,33
RS_Vector definitionPoint2; ///< 1st line end point, DXF codes 14,24,34
RS_Vector definitionPoint3; ///< 2nd line start point, DXF codes 15,25,35
///< 2nd line end point is in common dim data, DXF codes 10,20,30
RS_Vector definitionPoint4; ///< dim arc radius point, DXF codes 16,26,36
};
std::ostream& operator << (std::ostream& os, const RS_DimAngularData& dd);
/**
* Holds the DXF variables that defines a angular dimension entity.
*/
struct LC_DimAngularVars
{
explicit LC_DimAngularVars(const double _dimscale,
const double _dimexo,
const double _dimexe,
const double _dimtxt,
const double _dimgap,
const double _arrowSize);
explicit LC_DimAngularVars(const LC_DimAngularVars& av);
double scale(void) const {
return dimscale;
}
double exo(void) const {
return dimexo;
}
double exe(void) const {
return dimexe;
}
double txt(void) const {
return dimtxt;
}
double gap(void) const {
return dimgap;
}
double arrow(void) const {
return arrowSize;
}
private:
double dimscale {1.0}; ///< general scale (DIMSCALE)
double dimexo {0.0}; ///< distance from entities (DIMEXO)
double dimexe {0.0}; ///< extension line extension (DIMEXE)
double dimtxt {0.0}; ///< text height (DIMTXT)
double dimgap {0.0}; ///< text distance to line (DIMGAP)
double arrowSize {0.0}; ///< arrow length
};
std::ostream& operator << (std::ostream& os, const LC_DimAngularVars& dd);
/**
* Class for angular dimension entities.
*
* @author Andrew Mustun
*/
class RS_DimAngular : public RS_Dimension
{
friend std::ostream& operator << (std::ostream& os, const RS_DimAngular& d);
public:
RS_DimAngular(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimAngularData& ed);
RS_Entity* clone() const override;
/** @return RS2::EntityDimAngular */
RS2::EntityType rtti() const override {
return RS2::EntityDimAngular;
}
/**
* @return Copy of data that defines the angular dimension.
* @see getData()
*/
RS_DimAngularData getEData() const {
return edata;
}
QString getMeasuredLabel() override;
RS_Vector getCenter() const override;
void updateDim(bool autoText = false) override;
RS_Vector getDefinitionPoint1() {
return edata.definitionPoint1;
}
RS_Vector getDefinitionPoint2() {
return edata.definitionPoint2;
}
RS_Vector getDefinitionPoint3() {
return edata.definitionPoint3;
}
RS_Vector getDefinitionPoint4() {
return edata.definitionPoint4;
}
void update() override;
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
protected:
/** Extended data. */
RS_DimAngularData edata;
private:
void calcDimension(void);
void fixDimension(void);
void 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);
void arrow(const RS_Vector& point,
const double angle,
const double direction,
const bool outsideArrows,
const LC_DimAngularVars& av,
const RS_Pen& pen);
RS_Vector dimDir1s;
RS_Vector dimDir1e;
RS_Vector dimDir2s;
RS_Vector dimDir2e;
RS_Vector dimDirRad;
RS_ConstructionLine dimLine1;
RS_ConstructionLine dimLine2;
double dimRadius {0.0};
double dimAngleL1 {0.0};
double dimAngleL2 {0.0};
double dimAngle {0.0}; ///< angle to dimension in rad
RS_Vector dimCenter; ///< intersection point of the dimension lines
};
#endif

View File

@@ -0,0 +1,225 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_dimdiametric.h"
#include "rs_mtext.h"
#include "rs_solid.h"
#include "rs_graphic.h"
#include "rs_units.h"
#include "rs_debug.h"
RS_DimDiametricData::RS_DimDiametricData():
definitionPoint(false),
leader(0.0)
{}
/**
* Constructor with initialisation.
*
* @param definitionPoint Definition point of the diametric dimension.
* @param leader Leader length.
*/
RS_DimDiametricData::RS_DimDiametricData(const RS_Vector& _definitionPoint,
double _leader):
definitionPoint(_definitionPoint)
,leader(_leader)
{
}
std::ostream& operator << (std::ostream& os,
const RS_DimDiametricData& dd) {
os << "(" << dd.definitionPoint << "," << dd.leader << ")";
return os;
}
/**
* Constructor.
*
* @para parent Parent Entity Container.
* @para d Common dimension geometrical data.
* @para ed Extended geometrical data for diametric dimension.
*/
RS_DimDiametric::RS_DimDiametric(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimDiametricData& ed)
: RS_Dimension(parent, d), edata(ed) {
calculateBorders();
}
RS_Entity* RS_DimDiametric::clone() const {
RS_DimDiametric* d = new RS_DimDiametric(*this);
d->setOwner(isOwner());
d->initId();
d->detach();
return d;
}
/**
* @return Automatically created label for the default
* measurement of this dimension.
*/
QString RS_DimDiametric::getMeasuredLabel() {
// Definitive dimension line:
double dist = data.definitionPoint.distanceTo(edata.definitionPoint) * getGeneralFactor();
RS_Graphic* graphic = getGraphic();
QString ret;
if (graphic) {
int dimlunit = getGraphicVariableInt("$DIMLUNIT", 2);
int dimdec = getGraphicVariableInt("$DIMDEC", 4);
int dimzin = getGraphicVariableInt("$DIMZIN", 1);
RS2::LinearFormat format = graphic->getLinearFormat(dimlunit);
ret = RS_Units::formatLinear(dist, RS2::None, format, dimdec);
if (format == RS2::Decimal)
ret = stripZerosLinear(ret, dimzin);
//verify if units are decimal and comma separator
if (format == RS2::Decimal || format == RS2::ArchitecturalMetric){
if (getGraphicVariableInt("$DIMDSEP", 0) == 44)
ret.replace(QChar('.'), QChar(','));
}
}
else {
ret = QString("%1").arg(dist);
}
return ret;
}
RS_VectorSolutions RS_DimDiametric::getRefPoints() const
{
return RS_VectorSolutions({edata.definitionPoint,
data.definitionPoint, data.middleOfText});
}
/**
* 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_DimDiametric::updateDim(bool autoText) {
RS_DEBUG->print("RS_DimDiametric::update");
clear();
if (isUndone()) {
return;
}
// dimension line:
updateCreateDimensionLine(data.definitionPoint, edata.definitionPoint,
true, true, autoText);
calculateBorders();
}
void RS_DimDiametric::move(const RS_Vector& offset) {
RS_Dimension::move(offset);
edata.definitionPoint.move(offset);
update();
}
void RS_DimDiametric::rotate(const RS_Vector& center, const double& angle) {
rotate(center,RS_Vector(angle));
}
void RS_DimDiametric::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_Dimension::rotate(center, angleVector);
edata.definitionPoint.rotate(center, angleVector);
update();
}
void RS_DimDiametric::scale(const RS_Vector& center, const RS_Vector& factor) {
RS_Dimension::scale(center, factor);
edata.definitionPoint.scale(center, factor);
edata.leader*=factor.x;
update();
}
void RS_DimDiametric::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
RS_Dimension::mirror(axisPoint1, axisPoint2);
edata.definitionPoint.mirror(axisPoint1, axisPoint2);
update();
}
void RS_DimDiametric::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
if (ref.distanceTo(edata.definitionPoint)<1.0e-4) {
RS_Vector c = (edata.definitionPoint + data.definitionPoint)/2.0;
double d = c.distanceTo(edata.definitionPoint);
double a = c.angleTo(edata.definitionPoint + offset);
RS_Vector v = RS_Vector::polar(d, a);
edata.definitionPoint = c + v;
data.definitionPoint = c - v;
updateDim(true);
}
else if (ref.distanceTo(data.definitionPoint)<1.0e-4) {
RS_Vector c = (edata.definitionPoint + data.definitionPoint)/2.0;
double d = c.distanceTo(data.definitionPoint);
double a = c.angleTo(data.definitionPoint + offset);
RS_Vector v = RS_Vector::polar(d, a);
data.definitionPoint = c + v;
edata.definitionPoint = c - v;
updateDim(true);
}
else if (ref.distanceTo(data.middleOfText)<1.0e-4) {
data.middleOfText.move(offset);
updateDim(false);
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_DimDiametric& d) {
os << " DimDiametric: " << d.getData() << "\n" << d.getEData() << "\n";
return os;
}

View File

@@ -0,0 +1,114 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_DIMDIAMETER_H
#define RS_DIMDIAMETER_H
#include "rs_dimension.h"
/**
* Holds the data that defines a diametric dimension entity.
*/
struct RS_DimDiametricData {
/**
* Default constructor
*/
RS_DimDiametricData();
/**
* Constructor with initialisation.
*
* @param definitionPoint Definition point of the diametric dimension.
* @param leader Leader length.
*/
RS_DimDiametricData(const RS_Vector& definitionPoint,
double leader);
/** Definition point. */
RS_Vector definitionPoint;
/** Leader length. */
double leader;
};
std::ostream& operator << (std::ostream& os, const RS_DimDiametricData& dd);
/**
* Class for diametric dimension entities.
*
* @author Andrew Mustun
*/
class RS_DimDiametric : public RS_Dimension {
public:
RS_DimDiametric(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimDiametricData& ed);
RS_Entity* clone() const override;
/** @return RS2::EntityDimDiametric */
RS2::EntityType rtti() const override{
return RS2::EntityDimDiametric;
}
/**
* @return Copy of data that defines the diametric dimension.
* @see getData()
*/
RS_DimDiametricData getEData() const {
return edata;
}
RS_VectorSolutions getRefPoints() const override;
QString getMeasuredLabel() override;
void updateDim(bool autoText=false) override;
RS_Vector getDefinitionPoint() {
return edata.definitionPoint;
}
double getLeader() {
return edata.leader;
}
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
friend std::ostream& operator << (std::ostream& os,
const RS_DimDiametric& d);
protected:
/** Extended data. */
RS_DimDiametricData edata;
};
#endif

892
lib/engine/rs_dimension.cpp Normal file
View File

@@ -0,0 +1,892 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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<string>
#include "rs_information.h"
#include "rs_line.h"
#include "rs_dimension.h"
#include "rs_solid.h"
#include "rs_units.h"
#include "rs_math.h"
#include "rs_filterdxfrw.h" //for int <-> rs_color conversion
#include "rs_debug.h"
RS_DimensionData::RS_DimensionData():
definitionPoint(false),
middleOfText(false),
valign(RS_MTextData::VABottom),
halign(RS_MTextData::HALeft),
lineSpacingStyle(RS_MTextData::Exact),
lineSpacingFactor(0.0),
text(""),
style(""),
angle(0.0)
{}
/**
* Constructor with initialisation.
*
* @param definitionPoint Definition point.
* @param middleOfText Middle point of dimension text.
* @param valign Vertical alignment.
* @param halign Horizontal alignment.
* @param lineSpacingStyle Line spacing style.
* @param lineSpacingFactor Line spacing factor.
* @param text Text string entered explicitly by user or null
* or "<>" for the actual measurement or " " (one blank space).
* for suppressing the text.
* @param style Dimension style name.
* @param angle Rotation angle of dimension text away from
* default orientation.
*/
RS_DimensionData::RS_DimensionData(const RS_Vector& _definitionPoint,
const RS_Vector& _middleOfText,
RS_MTextData::VAlign _valign,
RS_MTextData::HAlign _halign,
RS_MTextData::MTextLineSpacingStyle _lineSpacingStyle,
double _lineSpacingFactor,
QString _text,
QString _style,
double _angle):
definitionPoint(_definitionPoint)
,middleOfText(_middleOfText)
,valign(_valign)
,halign(_halign)
,lineSpacingStyle(_lineSpacingStyle)
,lineSpacingFactor(_lineSpacingFactor)
,text(_text)
,style(_style)
,angle(_angle)
{
}
std::ostream& operator << (std::ostream& os,
const RS_DimensionData& dd) {
os << "("
<< dd.definitionPoint<<','
<<dd.middleOfText<<','
<<dd.valign<<','
<<dd.halign<<','
<<dd.lineSpacingStyle<<','
<<dd.lineSpacingFactor<<','
<<dd.text.toLatin1().data() <<','
<<dd.style.toLatin1().data()<<','
<<dd.angle
<< ")";
return os;
}
/**
* Constructor.
*/
RS_Dimension::RS_Dimension(RS_EntityContainer* parent,
const RS_DimensionData& d)
: RS_EntityContainer(parent), data(d) {
}
RS_Vector RS_Dimension::getNearestRef( const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
// override the RS_EntityContainer method
// use RS_Entity instead for refpoint dragging
return RS_Entity::getNearestRef( coord, dist);
}
RS_Vector RS_Dimension::getNearestSelectedRef( const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
// override the RS_EntityContainer method
// use RS_Entity instead for refpoint dragging
return RS_Entity::getNearestSelectedRef( coord, dist);
}
/**
* @return Dimension text. Either a text the user defined or
* the measured text.
*
* @param resolve false: return plain value. true: return measured
* label if appropriate.
* @see getMeasuredLabel
*/
QString RS_Dimension::getLabel(bool resolve) {
if (!resolve) {
return data.text;
}
QString ret="";
// One space suppresses the text:
if (data.text==" ") {
ret = "";
}
// No text prints actual measurement:
else if (data.text=="") {
ret = getMeasuredLabel();
}
// Others print the text (<> is replaced by the measurement)
else {
ret = data.text;
ret = ret.replace(QString("<>"), getMeasuredLabel());
}
return ret;
}
/**
* Sets a new text for the label.
*/
void RS_Dimension::setLabel(const QString& l) {
data.text = l;
}
/**
* Find intersections between a line and an EntityContainer. Solutions are
* sorted along the line before returning.
*
* @param infiniteLine Treat the line as infinitely long in both directions.
*/
RS_VectorSolutions RS_Dimension::getIntersectionsLineContainer(
const RS_Line* l, const RS_EntityContainer* c, bool infiniteLine)
{
RS_VectorSolutions solutions_initial;
RS_VectorSolutions solutions_filtered;
const double tol = 1.0e-4;
// Find all intersections, including those beyond limits of container
// entities.
for(RS_Entity* e: *c) {
solutions_initial.push_back(
RS_Information::getIntersection(l, e, false)
);
}
// Filter solutions based on whether they are actually on any entities.
for(const RS_Vector& vp: solutions_initial){
for(RS_Entity* e: *c) {
if (e->isConstruction(true) || e->isPointOnEntity(vp, tol)) {
// the intersection is at least on the container, now check the line:
if (infiniteLine) {
// The line is treated as infinitely long so we don't need to
// check if the intersection is on the line.
solutions_filtered.push_back(vp);
break;
} else if (l->isConstruction(true) || l->isPointOnEntity(vp, tol)) {
solutions_filtered.push_back(vp);
break;
}
}
}
}
/**
* We cannot sort the solutions in place because getVector() returns a
* const vector, so first construct a copy:
*/
std::vector<RS_Vector> solutions_sorted(solutions_filtered.getVector());
std::sort(solutions_sorted.begin(), solutions_sorted.end(),
[l](const RS_Vector& lhs, const RS_Vector& rhs)
{
return l->getProjectionValueAlongLine(lhs)
< l->getProjectionValueAlongLine(rhs);
});
return RS_VectorSolutions(solutions_sorted);
}
/**
* Creates a horizontal-text dimensioning line (line with one, two or no arrows
* and "inside horizontal" text).
*
* @param forceAutoText Automatically reposition the text label.
*/
void RS_Dimension::updateCreateHorizontalTextDimensionLine(const RS_Vector& p1,
const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
// general scale (DIMSCALE)
double dimscale = getGeneralScale();
// text height (DIMTXT)
double dimtxt = getTextHeight()*dimscale;
// text distance to line (DIMGAP)
double dimgap = getDimensionLineGap()*dimscale;
// length of dimension line:
double distance = p1.distanceTo(p2);
// arrow size:
double arrowSize = getArrowSize()*dimscale;
// arrow angles:
double arrowAngle1, arrowAngle2;
RS_Pen pen(getDimensionLineColor(),
getDimensionLineWidth(),
RS2::LineByBlock);
// Create dimension line:
RS_Line* dimensionLine {new RS_Line{this, p1, p2}};
RS_Line* dimensionLineInside1 {nullptr};
RS_Line* dimensionLineInside2 {nullptr};
RS_Line* dimensionLineOutside1 {nullptr};
RS_Line* dimensionLineOutside2 {nullptr};
dimensionLine->setPen(pen);
dimensionLine->setLayer(nullptr);
// Text label:
RS_MTextData textData;
RS_Vector textPos;
double textAngle = 0.0;
bool autoText = !data.middleOfText.valid || forceAutoText;
if (autoText) {
textPos = dimensionLine->getMiddlePoint();
//// the next update should still be able to adjust this
//// auto text position. leave it invalid
data.middleOfText = textPos;
} else {
textPos = data.middleOfText;
}
textData = RS_MTextData(textPos,
dimtxt, 30.0,
RS_MTextData::VAMiddle,
RS_MTextData::HACenter,
RS_MTextData::LeftToRight,
RS_MTextData::Exact,
1.0,
getLabel(),
getTextStyle(),
textAngle);
RS_MText* text = new RS_MText(this, textData);
text->setPen(RS_Pen(getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
text->setLayer(nullptr);
// evaluate intersection between dim line and text
double textIntersectionLength = 0.0;
double w = text->getUsedTextWidth()/2+dimgap;
double h = text->getUsedTextHeight()/2+dimgap;
// textCorner variables correspond to the corners of the text bounding box
// if the text were to be positioned in the center of the dimensionLine.
RS_Vector textCorner1 = dimensionLine->getMiddlePoint() - RS_Vector{w, h};
RS_Vector textCorner2 = dimensionLine->getMiddlePoint() + RS_Vector{w, h};
RS_EntityContainer c;
c.addRectangle(textCorner1, textCorner2);
RS_VectorSolutions sol1 = getIntersectionsLineContainer(
dimensionLine, &c,
true // treat line as infinitely long in both directions
);
textIntersectionLength = sol1.get(0).distanceTo(sol1.get(1));
// determine if we should use outside arrows
bool outsideArrows = (textIntersectionLength+3*arrowSize) > distance;
// add arrows
if (outsideArrows==false) {
arrowAngle1 = dimensionLine->getAngle2();
arrowAngle2 = dimensionLine->getAngle1();
} else {
arrowAngle1 = dimensionLine->getAngle1();
arrowAngle2 = dimensionLine->getAngle2();
// extend dimension line outside arrows
RS_Vector dir = RS_Vector::polar(arrowSize*2, dimensionLine->getAngle1());
dimensionLineOutside1 = new RS_Line{this, p1 - dir, p1};
dimensionLineOutside2 = new RS_Line{this, p2 + dir, p2};
// move text to the side if it won't fit either
RS_Vector distH;
if (textIntersectionLength>distance && autoText) {
distH.setPolar(textIntersectionLength/2.0+arrowSize*2+distance/2.0,
arrowAngle1);
text->move(distH);
textPos = text->getInsertionPoint();
data.middleOfText = textPos;
}
}
double dimtsz=getTickSize()*dimscale;
bool displayArrows = dimtsz < 0.01;
if(displayArrows) {
//display arrow
// Arrows:
RS_SolidData sd;
RS_Solid* arrow;
if (arrow1) {
// arrow 1
arrow = new RS_Solid(this, sd);
arrow->shapeArrow(p1,
arrowAngle1,
arrowSize);
arrow->setPen(pen);
arrow->setLayer(nullptr);
addEntity(arrow);
}
if (arrow2) {
// arrow 2:
arrow = new RS_Solid(this, sd);
arrow->shapeArrow(p2,
arrowAngle2,
arrowSize);
arrow->setPen(pen);
arrow->setLayer(nullptr);
addEntity(arrow);
}
} else {
//display ticks
// Arrows:
RS_Line* tick;
RS_Vector tickVector = RS_Vector::polar(dimtsz,arrowAngle1 + M_PI*0.25); //tick is 45 degree away
if (arrow1) {
// tick 1
tick = new RS_Line(this, p1-tickVector, p1+tickVector);
tick->setPen(pen);
tick->setLayer(nullptr);
addEntity(tick);
}
if (arrow2) {
// tick 2:
tick = new RS_Line(this, p2-tickVector, p2+tickVector);
tick->setPen(pen);
tick->setLayer(nullptr);
addEntity(tick);
}
}
// calculate split dimension lines
bool splitDimensionLine = false;
if (!outsideArrows) {
w = text->getUsedTextWidth()/2+dimgap;
h = text->getUsedTextHeight()/2+dimgap;
RS_Vector s1 = text->getInsertionPoint() - RS_Vector{w, h};
RS_Vector s2 = text->getInsertionPoint() + RS_Vector{w, h};
c = RS_EntityContainer();
c.addRectangle(s1, s2);
sol1 = getIntersectionsLineContainer(dimensionLine, &c);
if (sol1.size()>1) {
// the text bounding box intersects dimensionLine on two sides
splitDimensionLine = true;
s1 = sol1.get(0);
s2 = sol1.get(1);
} else if (sol1.size()==1) {
// the text bounding box intersects dimensionLine on one side
splitDimensionLine = true;
if (RS_Information::isPointInsideContour(p1, &c)) {
// the dimension line begins inside the text bounds
s1 = p1;
s2 = sol1.get(0);
} else {
// the dimension line ends inside the text bounds
s1 = sol1.get(0);
s2 = p2;
}
} else {
// the text bounding box does not intersect with dimensionLine, but we
// should still check if dimensionLine endpoints are completely inside
// the bounding box.
if (RS_Information::isPointInsideContour(p1, &c)) {
splitDimensionLine = true;
s1 = p1;
s2 = p2;
}
}
if (splitDimensionLine) {
dimensionLineInside1 = new RS_Line{this, p1, s1};
dimensionLineInside2 = new RS_Line{this, s2, p2};
}
}
// finally, add the dimension line(s) and text to the drawing
if (outsideArrows && dimensionLineOutside1) {
addEntity(dimensionLineOutside1);
addEntity(dimensionLineOutside2);
} else if (splitDimensionLine && dimensionLineInside1) {
addEntity(dimensionLineInside1);
addEntity(dimensionLineInside2);
} else {
addEntity(dimensionLine);
}
addEntity(text);
}
/**
* Creates an aligned-text dimensioning line (line with one, two or no arrows
* and aligned text).
*
* @param forceAutoText Automatically reposition the text label.
*/
void RS_Dimension::updateCreateAlignedTextDimensionLine(const RS_Vector& p1,
const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
// general scale (DIMSCALE)
double dimscale = getGeneralScale();
// text height (DIMTXT)
double dimtxt = getTextHeight()*dimscale;
// text distance to line (DIMGAP)
double dimgap = getDimensionLineGap()*dimscale;
// length of dimension line:
double distance = p1.distanceTo(p2);
// arrow size:
double arrowSize = getArrowSize()*dimscale;
// do we have to put the arrows outside of the line?
bool outsideArrows = (distance<arrowSize*2.5);
// arrow angles:
double arrowAngle1, arrowAngle2;
RS_Pen pen(getDimensionLineColor(),
getDimensionLineWidth(),
RS2::LineByBlock);
// Create dimension line:
RS_Line* dimensionLine = new RS_Line{this, p1, p2};
dimensionLine->setPen(pen);
dimensionLine->setLayer(nullptr);
addEntity(dimensionLine);
// Text label:
RS_MTextData textData;
RS_Vector textPos;
double dimAngle1 = dimensionLine->getAngle1();
bool corrected = false;
double textAngle = RS_Math::makeAngleReadable(dimAngle1, true, &corrected);
if (data.middleOfText.valid && !forceAutoText) {
textPos = data.middleOfText;
} else {
textPos = dimensionLine->getMiddlePoint();
// rotate text so it's readable from the bottom or right (ISO)
// quadrant 1 & 4
double const a = corrected?-M_PI_2:M_PI_2;
RS_Vector distV = RS_Vector::polar(dimgap + dimtxt/2.0, dimAngle1+a);
// move text away from dimension line:
textPos+=distV;
//// the next update should still be able to adjust this
//// auto text position. leave it invalid
data.middleOfText = textPos;
}
textData = RS_MTextData(textPos,
dimtxt, 30.0,
RS_MTextData::VAMiddle,
RS_MTextData::HACenter,
RS_MTextData::LeftToRight,
RS_MTextData::Exact,
1.0,
getLabel(),
getTextStyle(),
textAngle);
RS_MText* text = new RS_MText(this, textData);
text->setPen(RS_Pen(getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
text->setLayer(nullptr);
// move text to the side:
RS_Vector distH;
if (text->getUsedTextWidth()>distance) {
distH.setPolar(text->getUsedTextWidth()/2.0
+distance/2.0+dimgap, textAngle);
text->move(distH);
}
addEntity(text);
// add arrows
if (outsideArrows==false) {
arrowAngle1 = dimensionLine->getAngle2();
arrowAngle2 = dimensionLine->getAngle1();
} else {
arrowAngle1 = dimensionLine->getAngle1();
arrowAngle2 = dimensionLine->getAngle2();
// extend dimension line outside arrows
RS_Vector dir = RS_Vector::polar(arrowSize*2, arrowAngle2);
dimensionLine->setStartpoint(p1 + dir);
dimensionLine->setEndpoint(p2 - dir);
}
double dimtsz=getTickSize()*dimscale;
if(dimtsz < 0.01) {
//display arrow
// Arrows:
RS_SolidData sd;
RS_Solid* arrow;
if (arrow1) {
// arrow 1
arrow = new RS_Solid(this, sd);
arrow->shapeArrow(p1,
arrowAngle1,
arrowSize);
arrow->setPen(pen);
arrow->setLayer(nullptr);
addEntity(arrow);
}
if (arrow2) {
// arrow 2:
arrow = new RS_Solid(this, sd);
arrow->shapeArrow(p2,
arrowAngle2,
arrowSize);
arrow->setPen(pen);
arrow->setLayer(nullptr);
addEntity(arrow);
}
}else{
//display ticks
// Arrows:
RS_Line* tick;
RS_Vector tickVector = RS_Vector::polar(dimtsz,arrowAngle1 + M_PI*0.25); //tick is 45 degree away
if (arrow1) {
// tick 1
tick = new RS_Line(this, p1-tickVector, p1+tickVector);
tick->setPen(pen);
tick->setLayer(nullptr);
addEntity(tick);
}
if (arrow2) {
// tick 2:
tick = new RS_Line(this, p2-tickVector, p2+tickVector);
tick->setPen(pen);
tick->setLayer(nullptr);
addEntity(tick);
}
}
}
/**
* Creates a dimensioning line (line with one, two or no arrows and a text).
*
* @param forceAutoText Automatically reposition the text label.
*/
void RS_Dimension::updateCreateDimensionLine(const RS_Vector& p1,
const RS_Vector& p2, bool arrow1, bool arrow2, bool forceAutoText) {
if (getInsideHorizontalText())
updateCreateHorizontalTextDimensionLine(p1, p2, arrow1, arrow2, forceAutoText);
else
updateCreateAlignedTextDimensionLine(p1, p2, arrow1, arrow2, forceAutoText);
}
/**
* @return general factor for linear dimensions.
*/
double RS_Dimension::getGeneralFactor() {
return getGraphicVariable("$DIMLFAC", 1.0, 40);
}
/**
* @return general scale for dimensions.
*/
double RS_Dimension::getGeneralScale() {
return getGraphicVariable("$DIMSCALE", 1.0, 40);
}
/**
* @return arrow size in drawing units.
*/
double RS_Dimension::getArrowSize() {
return getGraphicVariable("$DIMASZ", 2.5, 40);
}
/**
* @return tick size in drawing units.
*/
double RS_Dimension::getTickSize() {
return getGraphicVariable("$DIMTSZ", 0., 40);
}
/**
* @return extension line overlength in drawing units.
*/
double RS_Dimension::getExtensionLineExtension() {
return getGraphicVariable("$DIMEXE", 1.25, 40);
}
/**
* @return extension line offset from entities in drawing units.
*/
double RS_Dimension::getExtensionLineOffset() {
return getGraphicVariable("$DIMEXO", 0.625, 40);
}
/**
* @return extension line gap to text in drawing units.
*/
double RS_Dimension::getDimensionLineGap() {
return getGraphicVariable("$DIMGAP", 0.625, 40);
}
/**
* @return Dimension labels text height.
*/
double RS_Dimension::getTextHeight() {
return getGraphicVariable("$DIMTXT", 2.5, 40);
}
/**
* @return Dimension labels alignment text true= horizontal, false= aligned.
*/
bool RS_Dimension::getInsideHorizontalText() {
int v = getGraphicVariableInt("$DIMTIH", 1);
if (v>0) {
addGraphicVariable("$DIMTIH", 1, 70);
getGraphicVariableInt("$DIMTIH", 1);
return true;
}
return false;
}
/**
* @return Dimension fixed length for extension lines true= fixed, false= not fixed.
*/
bool RS_Dimension::getFixedLengthOn() {
int v = getGraphicVariableInt("$DIMFXLON", 0);
if (v == 1) {
addGraphicVariable("$DIMFXLON", 1, 70);
getGraphicVariableInt("$DIMFXLON", 0);
return true;
}
return false;
}
/**
* @return Dimension fixed length for extension lines.
*/
double RS_Dimension::getFixedLength() {
return getGraphicVariable("$DIMFXL", 1.0, 40);
}
/**
* @return extension line Width.
*/
RS2::LineWidth RS_Dimension::getExtensionLineWidth() {
return RS2::intToLineWidth( getGraphicVariableInt("$DIMLWE", -2) ); //default -2 (RS2::WidthByBlock)
}
/**
* @return dimension line Width.
*/
RS2::LineWidth RS_Dimension::getDimensionLineWidth() {
return RS2::intToLineWidth( getGraphicVariableInt("$DIMLWD", -2) ); //default -2 (RS2::WidthByBlock)
}
/**
* @return dimension line Color.
*/
RS_Color RS_Dimension::getDimensionLineColor() {
return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRD", 0));
}
/**
* @return extension line Color.
*/
RS_Color RS_Dimension::getExtensionLineColor() {
return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRE", 0));
}
/**
* @return dimension text Color.
*/
RS_Color RS_Dimension::getTextColor() {
return RS_FilterDXFRW::numberToColor(getGraphicVariableInt("$DIMCLRT", 0));
}
/**
* @return text style for dimensions.
*/
QString RS_Dimension::getTextStyle() {
return getGraphicVariableString("$DIMTXSTY", "standard");
}
/**
* @return the given graphic variable or the default value given in mm
* converted to the graphic unit.
* If the variable is not found it is added with the given default
* value converted to the local unit.
*/
double RS_Dimension::getGraphicVariable(const QString& key, double defMM,
int code) {
double v = getGraphicVariableDouble(key, RS_MINDOUBLE);
if (v<=RS_MINDOUBLE) {
addGraphicVariable(
key,
RS_Units::convert(defMM, RS2::Millimeter, getGraphicUnit()),
code);
v = getGraphicVariableDouble(key, 1.0);
}
return v;
}
/**
* Removes zeros from angle string.
*
* @param angle The string representing angle.
* @param zeros Zeros suppression (0 none, 1 suppress leading, 2 suppress trailing, 3 both)
* Decimal separator are '.'
*
* @ret String with the formatted angle.
*/
QString RS_Dimension::stripZerosAngle(QString angle, int zeros){
if (zeros == 0) //do nothing
return angle;
if (zeros & 2 && (angle.contains(QString('.')) || angle.contains(QString(',')))) {
int end = angle.size() - 1;
QChar format = angle[end--]; //stores & skip format char
while (end > 0 && angle[end] == QChar('0')) // locate first 0 from end
end--;
if (angle[end] == QChar('.'))
end--;
angle.truncate(end+1);
angle.append(format);
}
if (zeros & 1){
if (angle[0] == QChar('0') && angle[1] == QChar('.'))
angle = angle.remove(0, 1);
}
return angle;
}
/**
* Removes zeros from linear string.
*
* @param linear The string representing linear measure.
* @param zeros Zeros suppression (see dimzin)
*
* @ret String with the formatted linear measure.
*/
QString RS_Dimension::stripZerosLinear(QString linear, int zeros){
//do nothing
if (zeros == 1)
return linear;
// return at least 1 character in string
int ls = linear.size();
if (ls <= 1) {
return linear;
}
// if removing of trailing zeroes is needed
if (zeros & 8 && (linear.contains(QString('.')) || linear.contains(QString(',')))) {
// search index
int i = ls - 1;
// locate first 0 in row from right
while (i > 0 && linear[i] == QChar('0')) {
i--;
}
// strip decimal point
if ((linear[i] == QChar('.') || linear[i] == QChar(',')) && i > 0)
i--;
// strip zeros. Leave at least one character at the beginning
linear = linear.remove(i+1, ls-i);
}
// if removing of initial zeroes is needed
if (zeros & 4) {
int i = 0;
// locate last 0 in row from left
while (i < ls-1 && linear[i] == QChar('0')) {
i++;
}
linear = linear.remove(0, i);
}
return linear;
}
void RS_Dimension::move(const RS_Vector& offset) {
data.definitionPoint.move(offset);
data.middleOfText.move(offset);
}
void RS_Dimension::rotate(const RS_Vector& center, const double& angle) {
RS_Vector angleVector(angle);
data.definitionPoint.rotate(center, angleVector);
data.middleOfText.rotate(center, angleVector);
data.angle = RS_Math::correctAngle(data.angle+angle);
}
void RS_Dimension::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
data.definitionPoint.rotate(center, angleVector);
data.middleOfText.rotate(center, angleVector);
data.angle = RS_Math::correctAngle(data.angle+angleVector.angle());
}
void RS_Dimension::scale(const RS_Vector& center, const RS_Vector& factor) {
data.definitionPoint.scale(center, factor);
data.middleOfText.scale(center, factor);
}
void RS_Dimension::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
data.definitionPoint.mirror(axisPoint1, axisPoint2);
data.middleOfText.mirror(axisPoint1, axisPoint2);
}
// EOF

219
lib/engine/rs_dimension.h Normal file
View File

@@ -0,0 +1,219 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_DIMENSION_H
#define RS_DIMENSION_H
#include "rs_entitycontainer.h"
#include "rs_mtext.h"
/**
* Holds the data that is common to all dimension entities.
*/
struct RS_DimensionData : public RS_Flags {
/**
* Default constructor
*/
RS_DimensionData();
/**
* Constructor with initialisation.
*
* @param definitionPoint Definition point.
* @param middleOfText Middle point of dimension text.
* @param valign Vertical alignment.
* @param halign Horizontal alignment.
* @param lineSpacingStyle Line spacing style.
* @param lineSpacingFactor Line spacing factor.
* @param text Text string entered explicitly by user or null
* or "<>" for the actual measurement or " " (one blank space).
* for suppressing the text.
* @param style Dimension style name.
* @param angle Rotation angle of dimension text away from
* default orientation.
*/
RS_DimensionData(const RS_Vector& definitionPoint,
const RS_Vector& middleOfText,
RS_MTextData::VAlign valign,
RS_MTextData::HAlign halign,
RS_MTextData::MTextLineSpacingStyle lineSpacingStyle,
double lineSpacingFactor,
QString text,
QString style,
double angle);
/** Definition point */
RS_Vector definitionPoint;
/** Middle point of dimension text */
RS_Vector middleOfText;
/** Vertical alignment */
RS_MTextData::VAlign valign;
/** Horizontal alignment */
RS_MTextData::HAlign halign;
/** Line spacing style */
RS_MTextData::MTextLineSpacingStyle lineSpacingStyle;
/** Line spacing factor */
double lineSpacingFactor;
/**
* Text string entered explicitly by user or null
* or "<>" for the actual measurement or " " (one blank space)
* for suppressing the text.
*/
QString text;
/** Dimension style name */
QString style;
/** Rotation angle of dimension text away from default orientation */
double angle;
};
std::ostream& operator << (std::ostream& os,
const RS_DimensionData& dd);
/**
* Abstract base class for dimension entity classes.
*
* @author Andrew Mustun
*/
class RS_Dimension : public RS_EntityContainer {
public:
RS_Dimension(RS_EntityContainer* parent,
const RS_DimensionData& d);
RS_Vector getNearestRef( const RS_Vector& coord, double* dist = nullptr) const override;
RS_Vector getNearestSelectedRef( const RS_Vector& coord, double* dist = nullptr) const override;
/** @return Copy of data that defines the dimension. */
RS_DimensionData getData() const {
return data;
}
QString getLabel(bool resolve=true);
void setLabel(const QString& l);
/**
* Needs to be implemented by the dimension class to return the
* measurement of the dimension (e.g. 10.5 or 15'14").
*/
virtual QString getMeasuredLabel() = 0;
/**
* Must be overwritten by implementing dimension entity class
* to update the subentities which make up the dimension entity.
*/
void update() override{
updateDim();
}
virtual void updateDim(bool autoText=false) = 0;
void updateCreateDimensionLine(const RS_Vector& p1, const RS_Vector& p2,
bool arrow1=true, bool arrow2=true, bool autoText=false);
RS_Vector getDefinitionPoint() {
return data.definitionPoint;
}
RS_Vector getMiddleOfText() {
return data.middleOfText;
}
RS_MTextData::VAlign getVAlign() {
return data.valign;
}
RS_MTextData::HAlign getHAlign() {
return data.halign;
}
RS_MTextData::MTextLineSpacingStyle getLineSpacingStyle() {
return data.lineSpacingStyle;
}
double getLineSpacingFactor() {
return data.lineSpacingFactor;
}
QString getText() {
return data.text;
}
QString getStyle() {
return data.style;
}
double getAngle() {
return data.angle;
}
double getGeneralFactor();
double getGeneralScale();
double getArrowSize();
double getTickSize();
double getExtensionLineExtension();
double getExtensionLineOffset();
double getDimensionLineGap();
double getTextHeight();
bool getInsideHorizontalText();
bool getFixedLengthOn();
double getFixedLength();
RS2::LineWidth getExtensionLineWidth();
RS2::LineWidth getDimensionLineWidth();
RS_Color getDimensionLineColor();
RS_Color getExtensionLineColor();
RS_Color getTextColor();
QString getTextStyle();
double getGraphicVariable(const QString& key, double defMM, int code);
static QString stripZerosAngle(QString angle, int zeros=0);
static QString stripZerosLinear(QString linear, int zeros=1);
// virtual double getLength() {
// return -1.0;
// }
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
private:
static RS_VectorSolutions getIntersectionsLineContainer(
const RS_Line* l, const RS_EntityContainer* c, bool infiniteLine=false);
void updateCreateHorizontalTextDimensionLine(
const RS_Vector& p1, const RS_Vector& p2,
bool arrow1=true, bool arrow2=true, bool autoText=false);
void updateCreateAlignedTextDimensionLine(
const RS_Vector& p1, const RS_Vector& p2,
bool arrow1=true, bool arrow2=true, bool autoText=false);
protected:
/** Data common to all dimension entities. */
RS_DimensionData data;
};
#endif

384
lib/engine/rs_dimlinear.cpp Normal file
View File

@@ -0,0 +1,384 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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_dimlinear.h"
#include "rs_line.h"
#include "rs_constructionline.h"
#include "rs_mtext.h"
#include "rs_solid.h"
#include "rs_graphic.h"
#include "rs_math.h"
#include "rs_debug.h"
RS_DimLinearData::RS_DimLinearData():
extensionPoint1(false),
extensionPoint2(false),
angle(0.0),
oblique(0.0)
{}
RS_DimLinearData::RS_DimLinearData(const RS_Vector& _extensionPoint1,
const RS_Vector& _extensionPoint2,
double _angle, double _oblique):
extensionPoint1(_extensionPoint1)
,extensionPoint2(_extensionPoint2)
,angle(_angle)
,oblique(_oblique)
{
}
std::ostream& operator << (std::ostream& os,
const RS_DimLinearData& dd) {
os << "(" << dd.extensionPoint1 << ","
<< dd.extensionPoint1 <<','
<< dd.angle <<','
<< dd.oblique <<','
<<")";
return os;
}
/**
* Constructor.
*
* @para parent Parent Entity Container.
* @para d Common dimension geometrical data.
* @para ed Extended geometrical data for linear dimension.
*/
RS_DimLinear::RS_DimLinear(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimLinearData& ed)
: RS_Dimension(parent, d), edata(ed) {
calculateBorders();
}
RS_Entity* RS_DimLinear::clone() const {
RS_DimLinear* d = new RS_DimLinear(*this);
d->setOwner(isOwner());
d->initId();
d->detach();
return d;
}
RS_VectorSolutions RS_DimLinear::getRefPoints() const
{
return RS_VectorSolutions({edata.extensionPoint1, edata.extensionPoint2,
data.definitionPoint, data.middleOfText});
}
void RS_DimLinear::setAngle(double a) {
edata.angle = RS_Math::correctAngle(a);
}
/**
* @return Automatically created label for the default
* measurement of this dimension.
*/
QString RS_DimLinear::getMeasuredLabel() {
// direction of dimension line
RS_Vector dirDim = RS_Vector::polar(100.0, edata.angle);
// construction line for dimension line
RS_ConstructionLine dimLine(nullptr,
RS_ConstructionLineData(data.definitionPoint,
data.definitionPoint + dirDim));
RS_Vector dimP1 = dimLine.getNearestPointOnEntity(edata.extensionPoint1);
RS_Vector dimP2 = dimLine.getNearestPointOnEntity(edata.extensionPoint2);
// Definitive dimension line:
double dist = dimP1.distanceTo(dimP2) * getGeneralFactor();
RS_Graphic* graphic = getGraphic();
QString ret;
if (graphic) {
int dimlunit = getGraphicVariableInt("$DIMLUNIT", 2);
int dimdec = getGraphicVariableInt("$DIMDEC", 4);
int dimzin = getGraphicVariableInt("$DIMZIN", 1);
RS2::LinearFormat format = graphic->getLinearFormat(dimlunit);
ret = RS_Units::formatLinear(dist, RS2::None, format, dimdec);
if (format == RS2::Decimal)
ret = stripZerosLinear(ret, dimzin);
//verify if units are decimal and comma separator
if (format == RS2::Decimal || format == RS2::ArchitecturalMetric){
if (getGraphicVariableInt("$DIMDSEP", 0) == 44)
ret.replace(QChar('.'), QChar(','));
}
}
else {
ret = QString("%1").arg(dist);
}
return ret;
}
bool RS_DimLinear::hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2) {
return (edata.extensionPoint1.isInWindow(v1, v2) ||
edata.extensionPoint2.isInWindow(v1, v2));
}
/**
* Updates the sub entities of this dimension. Called when the
* text or the position, alignment, .. changes.
*
* @param autoText Automatically reposition the text label
*/
void RS_DimLinear::updateDim(bool autoText) {
RS_DEBUG->print("RS_DimLinear::update");
clear();
if (isUndone()) {
return;
}
// general scale (DIMSCALE)
double dimscale = getGeneralScale();
// distance from entities (DIMEXO)
double dimexo = getExtensionLineOffset()*dimscale;
// extension line extension (DIMEXE)
double dimexe = getExtensionLineExtension()*dimscale;
// direction of dimension line
RS_Vector dirDim = RS_Vector::polar(100.0, edata.angle);
// construction line for dimension line
RS_ConstructionLine dimLine(
nullptr,
RS_ConstructionLineData(data.definitionPoint,
data.definitionPoint + dirDim));
RS_Vector dimP1 = dimLine.getNearestPointOnEntity(edata.extensionPoint1);
RS_Vector dimP2 = dimLine.getNearestPointOnEntity(edata.extensionPoint2);
// Definitive dimension line:
updateCreateDimensionLine(dimP1, dimP2, true, true, autoText);
/*
ld = RS_LineData(data.definitionPoint, dimP1);
RS_Line* dimensionLine = new RS_Line(this, ld);
addEntity(dimensionLine);
*/
double extAngle1, extAngle2;
if ((edata.extensionPoint1-dimP1).magnitude()<1e-6) {
if ((edata.extensionPoint2-dimP2).magnitude()<1e-6) {
//boot extension points are in dimension line only rotate 90
extAngle2 = edata.angle + (M_PI_2);
} else {
//first extension point are in dimension line use second
extAngle2 = edata.extensionPoint2.angleTo(dimP2);
}
extAngle1 = extAngle2;
} else {
//first extension point not are in dimension line use it
extAngle1 = edata.extensionPoint1.angleTo(dimP1);
if ((edata.extensionPoint2-dimP2).magnitude()<1e-6)
extAngle2 = extAngle1;
else
extAngle2 = edata.extensionPoint2.angleTo(dimP2);
}
RS_Vector vDimexe1 = RS_Vector::polar(dimexe, extAngle1);
RS_Vector vDimexe2 = RS_Vector::polar(dimexe, extAngle2);
RS_Vector vDimexo1, vDimexo2;
if (getFixedLengthOn()){
double dimfxl = getFixedLength()*dimscale;
double extLength = (edata.extensionPoint1-dimP1).magnitude();
if (extLength-dimexo > dimfxl)
vDimexo1.setPolar(extLength - dimfxl, extAngle1);
extLength = (edata.extensionPoint2-dimP2).magnitude();
if (extLength-dimexo > dimfxl)
vDimexo2.setPolar(extLength - dimfxl, extAngle2);
} else {
vDimexo1.setPolar(dimexo, extAngle1);
vDimexo2.setPolar(dimexo, extAngle2);
}
RS_Pen pen(getExtensionLineColor(),
getExtensionLineWidth(),
RS2::LineByBlock);
// extension lines:
RS_Line* line = new RS_Line{this,
edata.extensionPoint1+vDimexo1, dimP1+vDimexe1};
line->setPen(pen);
// line->setPen(RS_Pen(RS2::FlagInvalid));
line->setLayer(nullptr);
addEntity(line);
//data.definitionPoint+vDimexe2);
line = new RS_Line{this,
edata.extensionPoint2+vDimexo2, dimP2+vDimexe2};
line->setPen(pen);
// line->setPen(RS_Pen(RS2::FlagInvalid));
line->setLayer(nullptr);
addEntity(line);
calculateBorders();
}
void RS_DimLinear::move(const RS_Vector& offset) {
RS_Dimension::move(offset);
edata.extensionPoint1.move(offset);
edata.extensionPoint2.move(offset);
update();
}
void RS_DimLinear::rotate(const RS_Vector& center, const double& angle) {
RS_Vector angleVector(angle);
RS_Dimension::rotate(center, angleVector);
edata.extensionPoint1.rotate(center, angleVector);
edata.extensionPoint2.rotate(center, angleVector);
edata.angle = RS_Math::correctAngle(edata.angle+angle);
update();
}
void RS_DimLinear::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_Dimension::rotate(center, angleVector);
edata.extensionPoint1.rotate(center, angleVector);
edata.extensionPoint2.rotate(center, angleVector);
edata.angle = RS_Math::correctAngle(edata.angle+angleVector.angle());
update();
}
void RS_DimLinear::scale(const RS_Vector& center, const RS_Vector& factor) {
RS_Dimension::scale(center, factor);
edata.extensionPoint1.scale(center, factor);
edata.extensionPoint2.scale(center, factor);
update();
}
void RS_DimLinear::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
RS_Dimension::mirror(axisPoint1, axisPoint2);
edata.extensionPoint1.mirror(axisPoint1, axisPoint2);
edata.extensionPoint2.mirror(axisPoint1, axisPoint2);
RS_Vector vec;
vec.setPolar(1.0, edata.angle);
vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1);
edata.angle = vec.angle();
update();
}
void RS_DimLinear::stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) {
//e->calculateBorders();
if (getMin().isInWindow(firstCorner, secondCorner) &&
getMax().isInWindow(firstCorner, secondCorner)) {
move(offset);
} else {
//RS_Vector v = data.definitionPoint - edata.extensionPoint2;
//double len = edata.extensionPoint2.distanceTo(data.definitionPoint);
//double ang1 = edata.extensionPoint1.angleTo(edata.extensionPoint2)
// + M_PI_2;
if (edata.extensionPoint1.isInWindow(firstCorner,
secondCorner)) {
edata.extensionPoint1.move(offset);
}
if (edata.extensionPoint2.isInWindow(firstCorner,
secondCorner)) {
edata.extensionPoint2.move(offset);
}
/*
double ang2 = edata.extensionPoint1.angleTo(edata.extensionPoint2)
+ M_PI_2;
double diff = RS_Math::getAngleDifference(ang1, ang2);
if (diff>M_PI) {
diff-=2*M_PI;
}
if (fabs(diff)>M_PI_2) {
ang2 = RS_Math::correctAngle(ang2+M_PI);
}
RS_Vector v;
v.setPolar(len, ang2);
data.definitionPoint = edata.extensionPoint2 + v;
*/
}
updateDim(true);
}
void RS_DimLinear::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
if (ref.distanceTo(data.definitionPoint)<1.0e-4) {
data.definitionPoint += offset;
updateDim(true);
}
else if (ref.distanceTo(data.middleOfText)<1.0e-4) {
data.middleOfText += offset;
updateDim(false);
}
else if (ref.distanceTo(edata.extensionPoint1)<1.0e-4) {
edata.extensionPoint1 += offset;
updateDim(true);
}
else if (ref.distanceTo(edata.extensionPoint2)<1.0e-4) {
edata.extensionPoint2 += offset;
updateDim(true);
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_DimLinear& d) {
os << " DimLinear: " << d.getData() << "\n" << d.getEData() << "\n";
return os;
}

137
lib/engine/rs_dimlinear.h Normal file
View File

@@ -0,0 +1,137 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_DIMLINEAR_H
#define RS_DIMLINEAR_H
#include "rs_dimension.h"
/**
* Holds the data that defines a linear dimension entity.
*/
struct RS_DimLinearData {
/**
* Default constructor
*/
RS_DimLinearData();
/**
* Constructor with initialisation.
*
* @para extensionPoint1 Startpoint of the first extension line.
* @para extensionPoint2 Startpoint of the second extension line.
* @param angle Rotation angle in rad.
* @param oblique Oblique angle in rad.
*/
RS_DimLinearData(const RS_Vector& extensionPoint1,
const RS_Vector& extensionPoint2,
double angle, double oblique);
/** Definition point. Startpoint of the first definition line. */
RS_Vector extensionPoint1;
/** Definition point. Startpoint of the second definition line. */
RS_Vector extensionPoint2;
/** Rotation angle in rad. */
double angle;
/** Oblique angle in rad. */
double oblique;
};
std::ostream& operator << (std::ostream& os,
const RS_DimLinearData& dd);
/**
* Class for aligned dimension entities.
*
* @author Andrew Mustun
*/
class RS_DimLinear : public RS_Dimension {
public:
RS_DimLinear(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimLinearData& ed);
virtual ~RS_DimLinear() = default;
virtual RS_Entity* clone() const;
/** @return RS2::EntityDimLinear */
virtual RS2::EntityType rtti() const {
return RS2::EntityDimLinear;
}
/**
* @return Copy of data that defines the linear dimension.
* @see getData()
*/
RS_DimLinearData getEData() const {
return edata;
}
virtual RS_VectorSolutions getRefPoints() const;
virtual QString getMeasuredLabel();
virtual void updateDim(bool autoText=false);
RS_Vector getExtensionPoint1() const{
return edata.extensionPoint1;
}
RS_Vector getExtensionPoint2() const{
return edata.extensionPoint2;
}
double getAngle() const{
return edata.angle;
}
void setAngle(double a);
double getOblique() const{
return edata.oblique;
}
virtual void move(const RS_Vector& offset);
virtual void rotate(const RS_Vector& center, const double& angle);
virtual void rotate(const RS_Vector& center, const RS_Vector& angleVector);
virtual void scale(const RS_Vector& center, const RS_Vector& factor);
virtual void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2);
virtual bool hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2);
virtual void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset);
virtual void moveRef(const RS_Vector& ref, const RS_Vector& offset);
friend std::ostream& operator << (std::ostream& os,
const RS_DimLinear& d);
protected:
/** Extended data. */
RS_DimLinearData edata;
};
#endif

318
lib/engine/rs_dimradial.cpp Normal file
View File

@@ -0,0 +1,318 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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_dimradial.h"
#include "rs_line.h"
#include "rs_mtext.h"
#include "rs_solid.h"
#include "rs_graphic.h"
#include "rs_debug.h"
RS_DimRadialData::RS_DimRadialData():
definitionPoint(false),
leader(0.0)
{}
/**
* Constructor with initialisation.
*
* @param definitionPoint Definition point of the radial dimension.
* @param leader Leader length.
*/
RS_DimRadialData::RS_DimRadialData(const RS_Vector& _definitionPoint,
double _leader):
definitionPoint(_definitionPoint)
,leader(_leader)
{
}
std::ostream& operator << (std::ostream& os,
const RS_DimRadialData& dd) {
os << "(" << dd.definitionPoint << "/" << dd.leader << ")";
return os;
}
/**
* Constructor.
*
* @para parent Parent Entity Container.
* @para d Common dimension geometrical data.
* @para ed Extended geometrical data for radial dimension.
*/
RS_DimRadial::RS_DimRadial(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimRadialData& ed)
: RS_Dimension(parent, d), edata(ed) {}
RS_Entity* RS_DimRadial::clone() const {
RS_DimRadial* d = new RS_DimRadial(*this);
d->setOwner(isOwner());
d->initId();
d->detach();
return d;
}
/**
* @return Automatically created label for the default
* measurement of this dimension.
*/
QString RS_DimRadial::getMeasuredLabel() {
// Definitive dimension line:
double dist = data.definitionPoint.distanceTo(edata.definitionPoint) * getGeneralFactor();
RS_Graphic* graphic = getGraphic();
QString ret;
if (graphic) {
int dimlunit = getGraphicVariableInt("$DIMLUNIT", 2);
int dimdec = getGraphicVariableInt("$DIMDEC", 4);
int dimzin = getGraphicVariableInt("$DIMZIN", 1);
RS2::LinearFormat format = graphic->getLinearFormat(dimlunit);
ret = RS_Units::formatLinear(dist, RS2::None, format, dimdec);
if (format == RS2::Decimal)
ret = stripZerosLinear(ret, dimzin);
//verify if units are decimal and comma separator
if (format == RS2::Decimal || format == RS2::ArchitecturalMetric){
if (getGraphicVariableInt("$DIMDSEP", 0) == 44)
ret.replace(QChar('.'), QChar(','));
}
} else {
ret = QString("%1").arg(dist);
}
return ret;
}
RS_VectorSolutions RS_DimRadial::getRefPoints() const
{
return RS_VectorSolutions({edata.definitionPoint,
data.definitionPoint, data.middleOfText});
}
/**
* 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_DimRadial::updateDim(bool autoText) {
RS_DEBUG->print("RS_DimRadial::update");
clear();
if (isUndone()) {
return;
}
// general scale (DIMSCALE)
double dimscale = getGeneralScale();
RS_Vector p1 = data.definitionPoint;
RS_Vector p2 = edata.definitionPoint;
double angle = p1.angleTo(p2);
// text height (DIMTXT)
double dimtxt = getTextHeight()*dimscale;
RS_Pen pen(getDimensionLineColor(),
getDimensionLineWidth(),
RS2::LineByBlock);
RS_MTextData textData;
textData = RS_MTextData(RS_Vector(0.0,0.0),
dimtxt, 30.0,
RS_MTextData::VAMiddle,
RS_MTextData::HACenter,
RS_MTextData::LeftToRight,
RS_MTextData::Exact,
1.0,
getLabel(),
getTextStyle(),
0.0);
RS_MText* text = new RS_MText(this, textData);
double textWidth = text->getSize().x;
double tick_size = getTickSize()*dimscale;
double arrow_size = getArrowSize()*dimscale;
double length = p1.distanceTo(p2); // line length
bool outsideArrow = false;
if (tick_size == 0 && arrow_size != 0)
{
// do we have to put the arrow / text outside of the arc?
outsideArrow = (length < arrow_size*2+textWidth);
double arrowAngle;
if (outsideArrow) {
length += arrow_size*2 + textWidth;
arrowAngle = angle+M_PI;
} else {
arrowAngle = angle;
}
// create arrow:
RS_SolidData sd;
RS_Solid* arrow;
arrow = new RS_Solid(this, sd);
arrow->shapeArrow(p2, arrowAngle, arrow_size);
arrow->setPen(pen);
arrow->setLayer(nullptr);
addEntity(arrow);
}
RS_Vector p3 = RS_Vector::polar(length, angle);
p3 += p1;
// Create dimension line:
RS_Line* dimensionLine = new RS_Line{this, p1, p3};
dimensionLine->setPen(pen);
dimensionLine->setLayer(nullptr);
addEntity(dimensionLine);
RS_Vector distV;
double textAngle;
// text distance to line (DIMGAP)
double dimgap = getDimensionLineGap()*dimscale;
// rotate text so it's readable from the bottom or right (ISO)
// quadrant 1 & 4
if (angle > M_PI_2*3.0+0.001 || angle < M_PI_2+0.001)
{
distV.setPolar(dimgap + dimtxt/2.0, angle+M_PI_2);
textAngle = angle;
}
// quadrant 2 & 3
else
{
distV.setPolar(dimgap + dimtxt/2.0, angle-M_PI_2);
textAngle = angle+M_PI;
}
// move text label:
RS_Vector textPos;
if (data.middleOfText.valid && !autoText) {
textPos = data.middleOfText;
} else {
if (outsideArrow) {
textPos.setPolar(length-textWidth/2.0-arrow_size, angle);
} else {
textPos.setPolar(length/2.0, angle);
}
textPos += p1;
// move text away from dimension line:
textPos += distV;
data.middleOfText = textPos;
}
text->rotate({0., 0.}, textAngle);
text->move(textPos);
text->setPen(RS_Pen(getTextColor(), RS2::WidthByBlock, RS2::SolidLine));
text->setLayer(nullptr);
addEntity(text);
calculateBorders();
}
void RS_DimRadial::move(const RS_Vector& offset) {
RS_Dimension::move(offset);
edata.definitionPoint.move(offset);
update();
}
void RS_DimRadial::rotate(const RS_Vector& center, const double& angle) {
rotate(center,RS_Vector(angle));
}
void RS_DimRadial::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_Dimension::rotate(center, angleVector);
edata.definitionPoint.rotate(center, angleVector);
update();
}
void RS_DimRadial::scale(const RS_Vector& center, const RS_Vector& factor) {
RS_Dimension::scale(center, factor);
edata.definitionPoint.scale(center, factor);
edata.leader*=factor.x;
update();
}
void RS_DimRadial::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
RS_Dimension::mirror(axisPoint1, axisPoint2);
edata.definitionPoint.mirror(axisPoint1, axisPoint2);
update();
}
void RS_DimRadial::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
if (ref.distanceTo(edata.definitionPoint)<1.0e-4) {
double d = data.definitionPoint.distanceTo(edata.definitionPoint);
double a = data.definitionPoint.angleTo(edata.definitionPoint + offset);
RS_Vector v = RS_Vector::polar(d, a);
edata.definitionPoint = data.definitionPoint + v;
updateDim(true);
}
else if (ref.distanceTo(data.middleOfText)<1.0e-4) {
data.middleOfText.move(offset);
updateDim(false);
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_DimRadial& d) {
os << " DimRadial: " << d.getData() << "\n" << d.getEData() << "\n";
return os;
}

114
lib/engine/rs_dimradial.h Normal file
View File

@@ -0,0 +1,114 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_DIMRADIAL_H
#define RS_DIMRADIAL_H
#include "rs_dimension.h"
/**
* Holds the data that defines a radial dimension entity.
*/
struct RS_DimRadialData {
/**
* Default constructor. Leaves the data object uninitialized.
*/
RS_DimRadialData();
/**
* Constructor with initialisation.
*
* @param definitionPoint Definition point of the radial dimension.
* @param leader Leader length.
*/
RS_DimRadialData(const RS_Vector& definitionPoint,
double leader);
/** Definition point. */
RS_Vector definitionPoint;
/** Leader length. */
double leader;
};
std::ostream& operator << (std::ostream& os,
const RS_DimRadialData& dd);
/**
* Class for radial dimension entities.
*
* @author Andrew Mustun
*/
class RS_DimRadial : public RS_Dimension {
public:
RS_DimRadial(RS_EntityContainer* parent,
const RS_DimensionData& d,
const RS_DimRadialData& ed);
RS_Entity* clone() const override;
/** @return RS2::EntityDimRadial */
RS2::EntityType rtti() const override{
return RS2::EntityDimRadial;
}
/**
* @return Copy of data that defines the radial dimension.
* @see getData()
*/
RS_DimRadialData getEData() const {
return edata;
}
RS_VectorSolutions getRefPoints() const override;
QString getMeasuredLabel() override;
void updateDim(bool autoText=false) override;
RS_Vector getDefinitionPoint() {
return edata.definitionPoint;
}
double getLeader() {
return edata.leader;
}
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
friend std::ostream& operator << (std::ostream& os,
const RS_DimRadial& d);
protected:
/** Extended data. */
RS_DimRadialData edata;
};
#endif

View File

@@ -0,0 +1,65 @@
/****************************************************************************
**
** 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 "rs_document.h"
#include "rs_debug.h"
/**
* Constructor.
*
* @param parent Parent of the document. Often that's NULL but
* for blocks it's the blocklist.
*/
RS_Document::RS_Document(RS_EntityContainer* parent)
: RS_EntityContainer(parent), RS_Undo() {
RS_DEBUG->print("RS_Document::RS_Document() ");
filename = "";
autosaveFilename = "Unnamed";
formatType = RS2::FormatUnknown;
setModified(false);
RS_Color col(RS2::FlagByLayer);
activePen = RS_Pen(col, RS2::WidthByLayer, RS2::LineByLayer);
gv = NULL;//used to read/save current view
}
/**
* Overwritten to set modified flag when undo cycle finished with undoable(s).
*/
void RS_Document::endUndoCycle()
{
if (hasUndoable()) {
setModified(true);
}
RS_Undo::endUndoCycle();
}

155
lib/engine/rs_document.h Normal file
View File

@@ -0,0 +1,155 @@
/****************************************************************************
**
** 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!
**
**********************************************************************/
#ifndef RS_DOCUMENT_H
#define RS_DOCUMENT_H
#include "rs_layerlist.h"
#include "rs_entitycontainer.h"
#include "rs_undo.h"
class RS_BlockList;
/**
* Base class for documents. Documents can be either graphics or
* blocks and are typically shown in graphic views. Documents hold
* an active pen for drawing in the Document, a file name and they
* know whether they have been modified or not.
*
* @author Andrew Mustun
*/
class RS_Document : public RS_EntityContainer,
public RS_Undo {
public:
RS_Document(RS_EntityContainer* parent=nullptr);
virtual ~RS_Document() = default;
virtual RS_LayerList* getLayerList() = 0;
virtual RS_BlockList* getBlockList() = 0;
virtual void newDoc() = 0;
virtual bool save(bool isAutoSave = false) = 0;
virtual bool saveAs(const QString &filename, RS2::FormatType type, bool force) = 0;
virtual bool open(const QString &filename, RS2::FormatType type) = 0;
virtual bool loadTemplate(const QString &filename, RS2::FormatType type) = 0;
/**
* @return true for all document entities (e.g. Graphics or Blocks).
*/
virtual bool isDocument() const {
return true;
}
/**
* Removes an entity from the entity container. Implementation
* from RS_Undo.
*/
virtual void removeUndoable(RS_Undoable* u) {
if (u && u->undoRtti()==RS2::UndoableEntity && u->isUndone()) {
removeEntity(static_cast<RS_Entity*>(u));
}
}
/**
* @return Currently active drawing pen.
*/
RS_Pen getActivePen() const {
return activePen;
}
/**
* Sets the currently active drawing pen to p.
*/
void setActivePen(RS_Pen p) {
activePen = p;
}
/**
* @return File name of the document currently loaded.
* Note, that the default file name is empty.
*/
QString getFilename() const {
return filename;
}
/**
* @return Auto-save file name of the document currently loaded.
*/
QString getAutoSaveFilename() const {
return autosaveFilename;
}
/**
* Sets file name for the document currently loaded.
*/
void setFilename(const QString& fn) {
filename = fn;
}
/**
* Sets the documents modified status to 'm'.
*/
virtual void setModified(bool m) {
//std::cout << "RS_Document::setModified: %d" << (int)m << std::endl;
modified = m;
}
/**
* @retval true The document has been modified since it was last saved.
* @retval false The document has not been modified since it was last saved.
*/
virtual bool isModified() const {
return modified;
}
/**
* Overwritten to set modified flag when undo cycle finished with undoable(s).
*/
virtual void endUndoCycle() override;
void setGraphicView(RS_GraphicView * g) {gv = g;}
RS_GraphicView* getGraphicView() {return gv;}
protected:
/** Flag set if the document was modified and not yet saved. */
bool modified;
/** Active pen. */
RS_Pen activePen;
/** File name of the document or empty for a new document. */
QString filename;
/** Auto-save file name of document. */
QString autosaveFilename;
/** Format type */
RS2::FormatType formatType;
RS_GraphicView * gv;//used to read/save current view
};
#endif

1866
lib/engine/rs_ellipse.cpp Normal file

File diff suppressed because it is too large Load Diff

247
lib/engine/rs_ellipse.h Normal file
View File

@@ -0,0 +1,247 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2011-2015 Dongxu Li (dongxuli2011@gmail.com)
** 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!
**
**********************************************************************/
#ifndef RS_ELLIPSE_H
#define RS_ELLIPSE_H
#include "rs_atomicentity.h"
class LC_Quadratic;
/**
* Holds the data that defines an ellipse.
* angle1=angle2=0.0 is reserved for whole ellipses
* add 2*M_PI to angle1 or angle2 to make whole range ellipse arcs
*/
struct RS_EllipseData {
//! Ellipse center
RS_Vector center;
//! Endpoint of major axis relative to center.
RS_Vector majorP;
//! Ratio of minor axis to major axis.
double ratio;
//! Start angle
double angle1;
//! End angle
double angle2;
//! Reversed (cw) flag
bool reversed;
};
std::ostream& operator << (std::ostream& os, const RS_EllipseData& ed);
/**
* Class for an ellipse entity. All angles are in Rad.
*
* @author Andrew Mustun
*/
class RS_Ellipse : public RS_AtomicEntity {
public:
RS_Ellipse()=default;
RS_Ellipse(RS_EntityContainer* parent, const RS_EllipseData& d);
RS_Entity* clone() const override;
/** @return RS2::EntityEllipse */
RS2::EntityType rtti() const override{
return RS2::EntityEllipse;
}
/**
* @return Start point of the entity.
*/
RS_Vector getStartpoint() const override;
RS_VectorSolutions getFoci() const;
/**
* @return End point of the entity.
*/
RS_Vector getEndpoint() const override;
RS_Vector getEllipsePoint(const double& a) const; //find the point according to ellipse angle
void moveStartpoint(const RS_Vector& pos) override;
void moveEndpoint(const RS_Vector& pos) override;
double getLength() const override;
/**
//Ellipse must have ratio<1, and not reversed
*@ x1, ellipse angle
*@ x2, ellipse angle
//@return the arc length between ellipse angle x1, x2
**/
double getEllipseLength(double a1, double a2) const;
double getEllipseLength(double a2) const;
RS_VectorSolutions getTangentPoint(const RS_Vector& point) const override;//find the tangential points seeing from given point
RS_Vector getTangentDirection(const RS_Vector& point)const override;
RS2::Ending getTrimPoint(const RS_Vector& trimCoord,
const RS_Vector& trimPoint) override;
RS_Vector prepareTrim(const RS_Vector& trimCoord,
const RS_VectorSolutions& trimSol) override;
double getEllipseAngle (const RS_Vector& pos) const;
/** @return Copy of data that defines the ellipse. **/
const RS_EllipseData& getData() const;
RS_VectorSolutions getRefPoints() const override;
/**
* @retval true if the arc is reversed (clockwise),
* @retval false otherwise
*/
bool isReversed() const;
/** sets the reversed status. */
void setReversed(bool r);
/** @return The rotation angle of this ellipse */
double getAngle() const;
/** @return The start angle of this arc */
double getAngle1() const;
/** Sets new start angle. */
void setAngle1(double a1);
/** @return The end angle of this arc */
double getAngle2() const;
/** Sets new end angle. */
void setAngle2(double a2);
/** @return The center point (x) of this arc */
RS_Vector getCenter() const override;
/** Sets new center. */
void setCenter(const RS_Vector& c);
/** @return The endpoint of the major axis (relative to center). */
const RS_Vector& getMajorP() const;
/** Sets new major point (relative to center). */
void setMajorP(const RS_Vector& p);
/** @return The ratio of minor to major axis */
double getRatio() const;
/** Sets new ratio. */
void setRatio(double r);
/**
* @return Angle length in rad.
*/
double getAngleLength() const;
/** @return The major radius of this ellipse. Same as getRadius() */
double getMajorRadius() const;
/** @return the point by major minor radius directions */
RS_Vector getMajorPoint() const;
RS_Vector getMinorPoint() const;
/** @return The minor radius of this ellipse */
double getMinorRadius() const;
//! \brief isEllipticArc the ellipse an Arc, if angle1/angle2 are not both 0
bool isEllipticArc() const;
bool isEdge() const override{
return true;
}
bool createFrom4P(const RS_VectorSolutions& sol);
bool createFromCenter3Points(const RS_VectorSolutions& sol);
//! \{ \brief from quadratic form
/** : dn[0] x^2 + dn[1] xy + dn[2] y^2 =1 */
bool createFromQuadratic(const std::vector<double>& dn);
/** : generic quadratic: A x^2 + C xy + B y^2 + D x + E y + F =0 */
bool createFromQuadratic(const LC_Quadratic& q);
//! \}
bool createInscribeQuadrilateral(const std::vector<RS_Line*>& lines);
RS_Vector getMiddlePoint(void)const override;
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true, double* dist = nullptr, RS_Entity** entity=nullptr) const override;
RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = nullptr)const override;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1
)const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = nullptr)const override;
RS_Vector getNearestOrthTan(const RS_Vector& coord,
const RS_Line& normal,
bool onEntity = false) const override;
bool switchMajorMinor(void); //switch major minor axes to keep major the longer ellipse radius
void correctAngles();//make sure angleLength() is not more than 2*M_PI
bool isPointOnEntity(const RS_Vector& coord,
double tolerance=RS_TOLERANCE) const override;
void move(const RS_Vector& offset) override;
void rotate(const double& angle);
void rotate(const RS_Vector& angleVector);
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angle) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
/** whether the entity's bounding box intersects with visible portion of graphic view
*/
bool isVisibleInWindow(RS_GraphicView* view) const override;
//! \{ \brief find visible segments of entity and draw only those visible portion
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
void drawVisible(RS_Painter* painter, RS_GraphicView* view, double& patternOffset);
//! \}
friend std::ostream& operator << (std::ostream& os, const RS_Ellipse& a);
//void calculateEndpoints() override;
void calculateBorders() override;
//direction of tangent at endpoints
double getDirection1() const override;
double getDirection2() const override;
/** \brief return the equation of the entity
a quadratic contains coefficients for quadratic:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
LC_Quadratic getQuadratic() const override;
/**
* @brief areaLineIntegral, line integral for contour area calculation by Green's Theorem
* Contour Area =\oint x dy
* @return line integral \oint x dy along the entity
* \oint x dy = Cx y + \frac{1}{4}((a^{2}+b^{2})sin(2a)cos^{2}(t)-ab(2sin^{2}(a)sin(2t)-2t-sin(2t)))
*/
double areaLineIntegral() const override;
protected:
RS_EllipseData data;
};
#endif
//EOF

1129
lib/engine/rs_entity.cpp Normal file

File diff suppressed because it is too large Load Diff

585
lib/engine/rs_entity.h Normal file
View File

@@ -0,0 +1,585 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 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!
**
**********************************************************************/
#ifndef RS_ENTITY_H
#define RS_ENTITY_H
#include <map>
#include "rs_vector.h"
#include "rs_pen.h"
#include "rs_undoable.h"
class RS_Arc;
class RS_Block;
class RS_Circle;
class RS_Document;
class RS_EntityContainer;
class RS_Graphic;
class RS_GraphicView;
class RS_Insert;
class RS_Line;
class RS_Painter;
class RS_Point;
class RS_Polyline;
class RS_Text;
class RS_Layer;
class LC_Quadratic;
class RS_Vector;
class RS_VectorSolutions;
class QString;
/**
* Base class for an entity (line, arc, circle, ...)
*
* @author Andrew Mustun
*/
class RS_Entity : public RS_Undoable {
public:
RS_Entity(RS_EntityContainer* parent=nullptr);
virtual ~RS_Entity() = default;
void init();
virtual void initId();
virtual RS_Entity* clone() const = 0;
virtual void reparent(RS_EntityContainer* parent) {
this->parent = parent;
}
void resetBorders();
void moveBorders(const RS_Vector& offset);
void scaleBorders(const RS_Vector& center, const RS_Vector& factor);
/**
* Must be overwritten to return the rtti of this entity
* (e.g. RS2::EntityArc).
*/
virtual RS2::EntityType rtti() const{
return RS2::EntityUnknown;
}
/**
* Identify all entities as undoable entities.
* @return RS2::UndoableEntity
*/
virtual RS2::UndoableType undoRtti() const override {
return RS2::UndoableEntity;
}
/**
* @return Unique Id of this entity.
*/
unsigned long int getId() const {
return id;
}
/**
* This method must be overwritten in subclasses and return the
* number of <b>atomic</b> entities in this entity.
*/
virtual unsigned int count() const= 0;
/**
* This method must be overwritten in subclasses and return the
* number of <b>atomic</b> entities in this entity including sub containers.
*/
virtual unsigned int countDeep() const= 0;
/**
* Implementations must return the total length of the entity
* or a negative number if the entity has no length (e.g. a text or hatch).
*/
virtual double getLength() const {
return -1.0;
}
/**
* @return Parent of this entity or nullptr if this is a root entity.
*/
RS_EntityContainer* getParent() const {
return parent;
}
/**
* Reparents this entity.
*/
void setParent(RS_EntityContainer* p) {
parent = p;
}
/** @return The center point (x) of this arc */
//get center for entities: arc, circle and ellipse
virtual RS_Vector getCenter() const;
virtual double getRadius() const;
RS_Graphic* getGraphic() const;
RS_Block* getBlock() const;
RS_Insert* getInsert() const;
RS_Entity* getBlockOrInsert() const;
RS_Document* getDocument() const;
void setLayer(const QString& name);
void setLayer(RS_Layer* l);
void setLayerToActive();
RS_Layer* getLayer(bool resolve = true) const;
/**
* Sets the explicit pen for this entity or a pen with special
* attributes such as BY_LAYER, ..
*/
void setPen(const RS_Pen& pen) {
this->pen = pen;
}
void setPenToActive();
RS_Pen getPen(bool resolve = true) const;
/**
* Must be overwritten to return true if an entity type
* is a container for other entities (e.g. polyline, group, ...).
*/
virtual bool isContainer() const = 0;
/**
* Must be overwritten to return true if an entity type
* is an atomic entity.
*/
virtual bool isAtomic() const = 0;
/**
* Must be overwritten to return true if an entity type
* is a potential edge entity of a contour. By default
* this returns false.
*/
virtual bool isEdge() const {
return false;
}
/**
* @return true for all document entities (e.g. Graphics or Blocks).
* false otherwise.
*/
virtual bool isDocument() const {
return false;
}
virtual bool setSelected(bool select);
virtual bool toggleSelected();
virtual bool isSelected() const;
bool isParentSelected() const;
virtual bool isProcessed() const;
virtual void setProcessed(bool on);
bool isInWindow(RS_Vector v1, RS_Vector v2) const;
virtual bool hasEndpointsWithinWindow(const RS_Vector& /*v1*/, const RS_Vector& /*v2*/) {
return false;
}
virtual bool isVisible() const;
virtual void setVisible(bool v);
virtual void setHighlighted(bool on);
virtual bool isHighlighted() const;
bool isLocked() const;
void undoStateChanged(bool undone) override;
virtual bool isUndone() const;
/**
* Can be implemented by child classes to update the entities
* temporary subentities. update() is called if the entity's
* parameters or undo state changed.
*/
virtual void update() {}
virtual void setUpdateEnabled(bool on) {
updateEnabled = on;
}
/**
* This method doesn't do any calculations.
* @return minimum coordinate of the entity.
* @see calculateBorders()
*/
RS_Vector getMin() const {
return minV;
}
/**
* This method doesn't do any calculations.
* @return minimum coordinate of the entity.
* @see calculateBorders()
*/
RS_Vector getMax() const {
return maxV;
}
/**
* This method returns the difference of max and min returned
* by the above functions.
* @return size of the entity.
* @see calculateBorders()
* @see getMin()
* @see getMax()
*/
RS_Vector getSize() const;
void addGraphicVariable(const QString& key, double val, int code);
void addGraphicVariable(const QString& key, int val, int code);
void addGraphicVariable(const QString& key, const QString& val, int code);
double getGraphicVariableDouble(const QString& key, double def);
int getGraphicVariableInt(const QString& key, int def) const;
QString getGraphicVariableString(const QString& key,
const QString& def) const;
virtual RS_Vector getStartpoint() const;
virtual RS_Vector getEndpoint() const;
//find the local direction at end points, derived entities
// must implement this if direction is supported by the entity type
virtual double getDirection1() const {
return 0.;
}
virtual double getDirection2() const {
return 0.;
}
//find the tangential points seeing from given point
virtual RS_VectorSolutions getTangentPoint(const RS_Vector& /*point*/) const;
virtual RS_Vector getTangentDirection(const RS_Vector& /*point*/)const;
RS2::Unit getGraphicUnit() const;
/**
* Must be overwritten to get all reference points of the entity.
*/
virtual RS_VectorSolutions getRefPoints() const;
/**
* Must be overwritten to get the closest endpoint to the
* given coordinate for this entity.
*
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between 'coord' and the closest endpoint. The passed
* pointer can also be nullptr in which case the distance will be
* lost.
*
* @return The closest endpoint.
*/
virtual RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr)const = 0;
/**
* Must be overwritten to get the closest coordinate to the
* given coordinate which is on this entity.
*
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between \p coord and the point. The passed pointer can
* also be \p nullptr in which case the distance will be lost.
*
* @return The closest coordinate.
*/
virtual RS_Vector getNearestPointOnEntity(const RS_Vector& /*coord*/,
bool onEntity = true, double* dist = nullptr,
RS_Entity** entity = nullptr) const = 0;
/**
* Must be overwritten to get the (nearest) center point to the
* given coordinate for this entity.
*
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between 'coord' and the closest center point. The passed
* pointer can also be nullptr in which case the distance will be
* lost.
*
* @return The closest center point.
*/
virtual RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = nullptr) const= 0;
/**
* Must be overwritten to get the (nearest) middle point to the
* given coordinate for this entity.
*
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between 'coord' and the closest middle point. The passed
* pointer can also be nullptr in which case the distance will be
* lost.
*
* @return The closest middle point.
*/
virtual RS_Vector getMiddlePoint(void)const{
return RS_Vector(false);
}
virtual RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1
) const= 0;
/**
* Must be overwritten to get the nearest point with a given
* distance to the endpoint to the given coordinate for this entity.
*
* @param distance Distance to endpoint.
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between 'coord' and the closest point. The passed
* pointer can also be nullptr in which case the distance will be
* lost.
*
* @return The closest point with the given distance to the endpoint.
*/
virtual RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = nullptr) const= 0;
/**
* Must be overwritten to get the point with a given
* distance to the start- or endpoint to the given coordinate for this entity.
*
* @param distance Distance to endpoint.
* @param startp true = measured from Startpoint, false = measured from Endpoint
*
* @return The point with the given distance to the start- or endpoint.
*/
virtual RS_Vector getNearestDist(double /*distance*/,
bool /*startp*/) const{
return RS_Vector(false);
}
/**
* Must be overwritten to get the nearest reference point for this entity.
*
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between 'coord' and the closest point. The passed
* pointer can also be nullptr in which case the distance will be
* lost.
*
* @return The closest point with the given distance to the endpoint.
*/
virtual RS_Vector getNearestRef(const RS_Vector& coord,
double* dist = nullptr) const;
/**
* Gets the nearest reference point of this entity if it is selected.
* Containers re-implement this method to return the nearest reference
* point of a selected sub entity.
*
* @param coord Coordinate (typically a mouse coordinate)
* @param dist Pointer to a value which will contain the measured
* distance between 'coord' and the closest point. The passed
* pointer can also be nullptr in which case the distance will be
* lost.
*
* @return The closest point with the given distance to the endpoint.
*/
virtual RS_Vector getNearestSelectedRef(const RS_Vector& coord,
double* dist = nullptr) const;
/**
* Must be overwritten to get the shortest distance between this
* entity and a coordinate.
*
* @param coord Coordinate (typically a mouse coordinate)
* @param entity Pointer which will contain the (sub-)entity which is
* closest to the given point or nullptr if the caller is not
* interested in this information.
* @param level The resolve level.
*
* @sa RS2::ResolveLevel
*
* @return The measured distance between \p coord and the entity.
*/
virtual RS_Vector getNearestOrthTan(const RS_Vector& /*coord*/,
const RS_Line& /*normal*/,
bool onEntity = false) const;
virtual double getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity = nullptr,
RS2::ResolveLevel level = RS2::ResolveNone,
double solidDist = RS_MAXDOUBLE) const;
virtual bool isPointOnEntity(const RS_Vector& coord,
double tolerance=20.*RS_TOLERANCE) const;
/**
* Implementations must offset the entity by the given direction and distance.
*/
virtual bool offset(const RS_Vector& /*coord*/, const double& /*distance*/) {return false;}
/**
* Implementations must offset the entity by the distance at both directions
* used to generate tangential circles
*/
virtual std::vector<RS_Entity* > offsetTwoSides(const double& /*distance*/) const
{
return std::vector<RS_Entity* >();
}
/**
* implementations must revert the direction of an atomic entity
*/
virtual void revertDirection(){}
/**
* Implementations must move the entity by the given vector.
*/
virtual void move(const RS_Vector& offset) = 0;
/**
* Implementations must rotate the entity by the given angle around
* the given center.
*/
virtual void rotate(const RS_Vector& center, const double& angle) = 0;
virtual void rotate(const RS_Vector& center, const RS_Vector& angleVector) = 0;
/**
* Implementations must scale the entity by the given factors.
*/
virtual void scale(const RS_Vector& center, const RS_Vector& factor) = 0;
/**
* Acts like scale(RS_Vector) but with equal factors.
* Equal to scale(center, RS_Vector(factor, factor)).
*/
virtual void scale(const RS_Vector& center, const double& factor) {
scale(center, RS_Vector(factor, factor));
}
virtual void scale(const RS_Vector& factor) {
scale(RS_Vector(0.,0.), factor);
}
/**
* Implementations must mirror the entity by the given axis.
*/
virtual void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) = 0;
virtual void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset);
/**
* Implementations must drag the reference point(s) of all
* (sub-)entities that are very close to ref by offset.
*/
virtual void moveRef(const RS_Vector& /*ref*/,
const RS_Vector& /*offset*/) {
return;
}
/**
* Implementations must drag the reference point(s) of selected
* (sub-)entities that are very close to ref by offset.
*/
virtual void moveSelectedRef(const RS_Vector& /*ref*/,
const RS_Vector& /*offset*/) {
return;
}
/** whether the entity's bounding box intersects with visible portion of graphic view */
virtual bool isVisibleInWindow(RS_GraphicView* view) const;
/**
* Implementations must draw the entity on the given device.
*/
virtual void draw(RS_Painter* painter, RS_GraphicView* view,
double& patternOffset ) = 0;
double getStyleFactor(RS_GraphicView* view);
QString getUserDefVar(const QString& key) const;
std::vector<QString> getAllKeys() const;
void setUserDefVar(QString key, QString val);
void delUserDefVar(QString key);
friend std::ostream& operator << (std::ostream& os, RS_Entity& e);
/** Recalculates the borders of this entity. */
virtual void calculateBorders() = 0;
/** whether the entity is on a constructionLayer */
//! constructionLayer contains entities of infinite length, constructionLayer doesn't show up in print
bool isConstruction(bool typeCheck = false) const; // ignore certain entity types for constructionLayer check
//! whether printing is enabled or disabled for the entity's layer
bool isPrint(void) const;
/** return the equation of the entity
for quadratic,
return a vector contains:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
virtual LC_Quadratic getQuadratic() const;
/**
* @brief areaLineIntegral, line integral for contour area calculation by Green's Theorem
* Contour Area =\oint x dy
* @return line integral \oint x dy along the entity
*/
virtual double areaLineIntegral() const;
/**
* @brief trimmable, whether the entity type can be trimmed
* @return true, for trimmable entity types
* currently, trimmable types are: RS_Line, RS_Circle, RS_Arc, RS_Ellipse
*/
bool trimmable() const;
/**
* @brief isArc is the entity of type Arc, Circle, or Ellipse
* @return true for Arc, Circle, or Ellipse
*/
virtual bool isArc() const;
/**
* @brief isArcLine determine the entity is either Arc, Circle, or Line
* @return true if entity is Arc, Circle, or Line
*/
virtual bool isArcCircleLine() const;
protected:
//! Entity's parent entity or nullptr is this entity has no parent.
RS_EntityContainer* parent = nullptr;
//! minimum coordinates
RS_Vector minV;
//! maximum coordinates
RS_Vector maxV;
//! Pointer to layer
RS_Layer* layer;
//! Entity id
unsigned long int id;
//! pen (attributes) for this entity
RS_Pen pen;
//! auto updating enabled?
bool updateEnabled;
private:
std::map<QString, QString> varList;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_ENTITYCONTAINER_H
#define RS_ENTITYCONTAINER_H
#include <vector>
#include "rs_entity.h"
/**
* Class representing a tree of entities.
* Typical entity containers are graphics, polylines, groups, texts, ...)
*
* @author Andrew Mustun
*/
class RS_EntityContainer : public RS_Entity {
typedef RS_Entity * value_type;
public:
RS_EntityContainer(RS_EntityContainer* parent=nullptr, bool owner=true);
//RS_EntityContainer(const RS_EntityContainer& ec);
~RS_EntityContainer() override;
RS_Entity* clone() const override;
virtual void detach();
/** @return RS2::EntityContainer */
RS2::EntityType rtti() const override{
return RS2::EntityContainer;
}
void reparent(RS_EntityContainer* parent) override;
/**
* @return true: because entities made from this class
* and subclasses are containers for other entities.
*/
bool isContainer() const override{
return true;
}
/**
* @return false: because entities made from this class
* and subclasses are containers for other entities.
*/
bool isAtomic() const override{
return false;
}
double getLength() const override;
void setVisible(bool v) override;
bool setSelected(bool select=true) override;
bool toggleSelected() override;
virtual void selectWindow(RS_Vector v1, RS_Vector v2,
bool select=true, bool cross=false);
virtual void addEntity(RS_Entity* entity);
virtual void appendEntity(RS_Entity* entity);
virtual void prependEntity(RS_Entity* entity);
virtual void moveEntity(int index, QList<RS_Entity *>& entList);
virtual void insertEntity(int index, RS_Entity* entity);
virtual bool removeEntity(RS_Entity* entity);
//!
//! \brief addRectangle add four lines to form a rectangle by
//! the diagonal vertices v0,v1
//! \param v0,v1 diagonal vertices of the rectangle
//!
void addRectangle(RS_Vector const& v0, RS_Vector const& v1);
virtual RS_Entity* firstEntity(RS2::ResolveLevel level=RS2::ResolveNone);
virtual RS_Entity* lastEntity(RS2::ResolveLevel level=RS2::ResolveNone);
virtual RS_Entity* nextEntity(RS2::ResolveLevel level=RS2::ResolveNone);
virtual RS_Entity* prevEntity(RS2::ResolveLevel level=RS2::ResolveNone);
virtual RS_Entity* entityAt(int index);
virtual void setEntityAt(int index,RS_Entity* en);
//RLZ unused virtual int entityAt();
virtual int findEntity(RS_Entity const* const entity);
virtual void clear();
//virtual unsigned long int count() {
// return count(false);
//}
virtual bool isEmpty() const{
return count()==0;
}
unsigned count() const override;
unsigned countDeep() const override;
//virtual unsigned long int countLayerEntities(RS_Layer* layer);
/** \brief countSelected number of selected
* @param deep count sub-containers, if true
* @param types if is not empty, only counts by types listed
*/
virtual unsigned countSelected(bool deep=true, std::initializer_list<RS2::EntityType> const& types = {});
virtual double totalSelectedLength();
/**
* Enables / disables automatic update of borders on entity removals
* and additions. By default this is turned on.
*/
virtual void setAutoUpdateBorders(bool enable) {
autoUpdateBorders = enable;
}
virtual void adjustBorders(RS_Entity* entity);
void calculateBorders() override;
void forcedCalculateBorders();
void updateDimensions( bool autoText=true);
virtual void updateInserts();
virtual void updateSplines();
void update() override;
virtual void renameInserts(const QString& oldName,
const QString& newName);
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr)const override;
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist, RS_Entity** pEntity ) const;
RS_Entity* getNearestEntity(const RS_Vector& point,
double* dist = nullptr,
RS2::ResolveLevel level=RS2::ResolveAll) const;
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true,
double* dist = nullptr,
RS_Entity** entity=nullptr)const override;
RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = nullptr)const override;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1
)const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestIntersection(const RS_Vector& coord,
double* dist = nullptr);
RS_Vector getNearestVirtualIntersection(const RS_Vector& coord,
const double& angle,
double* dist);
RS_Vector getNearestRef(const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestSelectedRef(const RS_Vector& coord,
double* dist = nullptr) const override;
double getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity,
RS2::ResolveLevel level=RS2::ResolveNone,
double solidDist = RS_MAXDOUBLE) const override;
virtual bool optimizeContours();
bool hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2) override;
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2a) override;
void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
void moveSelectedRef(const RS_Vector& ref, const RS_Vector& offset) override;
void revertDirection() override;
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
friend std::ostream& operator << (std::ostream& os, RS_EntityContainer& ec);
bool isOwner() const {return autoDelete;}
void setOwner(bool owner) {autoDelete=owner;}
/**
* @brief areaLineIntegral, line integral for contour area calculation by Green's Theorem
* Contour Area =\oint x dy
* @return line integral \oint x dy along the entity
* returns absolute value
*/
virtual double areaLineIntegral() const override;
/**
* @brief ignoreForModification ignore this entity for entity catch for certain actions
* like catching circles to create tangent circles
* @return, true, indicate this entity container should be ignored
*/
bool ignoredOnModification() const;
/**
* @brief begin/end to support range based loop
* @return iterator
*/
QList<RS_Entity *>::const_iterator begin() const;
QList<RS_Entity *>::const_iterator end() const;
QList<RS_Entity *>::iterator begin() ;
QList<RS_Entity *>::iterator end() ;
//! \{
//! first and last without resolving into children, assume the container is
//! not empty
RS_Entity* last() const;
RS_Entity* first() const;
//! \}
const QList<RS_Entity*>& getEntityList();
protected:
/** entities in the container */
QList<RS_Entity *> entities;
/** sub container used only temporarily for iteration. */
RS_EntityContainer* subContainer;
/**
* Automatically update the borders of the container when entities
* are added or removed.
*/
static bool autoUpdateBorders;
private:
/**
* @brief ignoredSnap whether snapping is ignored
* @return true when entity of this container won't be considered for snapping points
*/
bool ignoredSnap() const;
int entIdx;
bool autoDelete;
};
#endif

64
lib/engine/rs_flags.cpp Normal file
View File

@@ -0,0 +1,64 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 Dongxu Li (dongxuli2011@gmail.com)
** 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 "rs_flags.h"
/** Constructor with initialisation to the given flags. */
RS_Flags::RS_Flags(unsigned f):
flags(f)
{
}
unsigned RS_Flags::getFlags() const {
return flags;
}
void RS_Flags::resetFlags() {
flags=0;
}
void RS_Flags::setFlags(unsigned f) {
flags=f;
}
void RS_Flags::setFlag(unsigned f) {
flags |= f;
}
void RS_Flags::delFlag(unsigned f) {
flags &= ~f;
}
void RS_Flags::toggleFlag(unsigned f) {
flags ^= f;
}
bool RS_Flags::getFlag(unsigned f) const {
return flags&f;
}

60
lib/engine/rs_flags.h Normal file
View File

@@ -0,0 +1,60 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_FLAGS_H
#define RS_FLAGS_H
/**
* Base class for objects which have flags.
*
* @author Andrew Mustun
*/
struct RS_Flags {
//! \{Constructor with initialisation to the given flags.
//! Default sets all flags to 0
RS_Flags(unsigned f = 0);
//! \}
virtual ~RS_Flags() = default;
unsigned getFlags() const;
void resetFlags();
void setFlags(unsigned f);
void setFlag(unsigned f);
void delFlag(unsigned f);
void toggleFlag(unsigned f);
bool getFlag(unsigned f) const;
private:
unsigned flags = 0;
};
#endif

463
lib/engine/rs_font.cpp Normal file
View File

@@ -0,0 +1,463 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 <QTextStream>
#include <QTextCodec>
#include "rs_font.h"
#include "rs_arc.h"
#include "rs_line.h"
#include "rs_polyline.h"
#include "rs_fontchar.h"
#include "rs_system.h"
#include "rs_math.h"
#include "rs_debug.h"
/**
* Constructor.
*
* @param owner true if the font owns the letters (blocks). Otherwise
* the letters will be deleted when the font is deleted.
*/
RS_Font::RS_Font(const QString& fileName, bool owner)
: letterList(owner), fileName(fileName), fileLicense("unknown") {
loaded = false;
letterSpacing = 3.0;
wordSpacing = 6.75;
lineSpacingFactor = 1.0;
rawLffFontList.clear();
}
/**
* Loads the font into memory.
*
* @retval true font was already loaded or is loaded now.
* @retval false font could not be loaded.
*/
bool RS_Font::loadFont() {
RS_DEBUG->print("RS_Font::loadFont");
if (loaded) {
return true;
}
QString path;
// Search for the appropriate font if we have only the name of the font:
if (!fileName.toLower().contains(".cxf") &&
!fileName.toLower().contains(".lff")) {
QStringList fonts = RS_SYSTEM->getNewFontList();
fonts.append(RS_SYSTEM->getFontList());
QFileInfo file;
for (QStringList::Iterator it = fonts.begin();
it!=fonts.end();
it++) {
if (QFileInfo(*it).baseName().toLower()==fileName.toLower()) {
path = *it;
break;
}
}
}
// We have the full path of the font:
else {
path = fileName;
}
// No font paths found:
if (path.isEmpty()) {
RS_DEBUG->print(RS_Debug::D_WARNING,
"RS_Font::loadFont: No fonts available.");
return false;
}
// Open cxf file:
QFile f(path);
if (!f.open(QIODevice::ReadOnly)) {
RS_DEBUG->print(RS_Debug::D_WARNING,
"RS_Font::loadFont: Cannot open font file: %s",
path.toLatin1().data());
return false;
} else {
RS_DEBUG->print("RS_Font::loadFont: "
"Successfully opened font file: %s",
path.toLatin1().data());
}
f.close();
if (path.contains(".cxf"))
readCXF(path);
if (path.contains(".lff"))
readLFF(path);
RS_Block* bk = letterList.find(QChar(0xfffd));
if (!bk) {
// create new letter:
RS_FontChar* letter = new RS_FontChar(nullptr, QChar(0xfffd), RS_Vector(0.0, 0.0));
RS_Polyline* pline = new RS_Polyline(letter, RS_PolylineData());
pline->setPen(RS_Pen(RS2::FlagInvalid));
pline->setLayer(nullptr);
pline->addVertex(RS_Vector(1, 0), 0);
pline->addVertex(RS_Vector(0, 2), 0);
pline->addVertex(RS_Vector(1, 4), 0);
pline->addVertex(RS_Vector(2, 2), 0);
pline->addVertex(RS_Vector(1, 0), 0);
letter->addEntity(pline);
letter->calculateBorders();
letterList.add(letter);
}
loaded = true;
RS_DEBUG->print("RS_Font::loadFont OK");
return true;
}
void RS_Font::readCXF(QString path) {
QString line;
QFile f(path);
f.open(QIODevice::ReadOnly);
QTextStream ts(&f);
// Read line by line until we find a new letter:
while (!ts.atEnd()) {
line = ts.readLine();
if (line.isEmpty())
continue;
// Read font settings:
if (line.at(0)=='#') {
QStringList lst =
( line.right(line.length()-1) ).split(':', QString::SkipEmptyParts);
QStringList::Iterator it3 = lst.begin();
// RVT_PORT sometimes it happens that the size is < 2
if (lst.size()<2)
continue;
QString identifier = (*it3).trimmed();
it3++;
QString value = (*it3).trimmed();
if (identifier.toLower()=="letterspacing") {
letterSpacing = value.toDouble();
} else if (identifier.toLower()=="wordspacing") {
wordSpacing = value.toDouble();
} else if (identifier.toLower()=="linespacingfactor") {
lineSpacingFactor = value.toDouble();
} else if (identifier.toLower()=="author") {
authors.append(value);
} else if (identifier.toLower()=="name") {
names.append(value);
} else if (identifier.toLower()=="encoding") {
ts.setCodec(QTextCodec::codecForName(value.toLatin1()));
encoding = value;
}
}
// Add another letter to this font:
else if (line.at(0)=='[') {
// uniode character:
QChar ch;
// read unicode:
QRegExp regexp("[0-9A-Fa-f]{4,4}");
regexp.indexIn(line);
QString cap = regexp.cap();
if (!cap.isNull()) {
int uCode = cap.toInt(nullptr, 16);
ch = QChar(uCode);
}
// read UTF8 (LibreCAD 1 compatibility)
else if (line.indexOf(']')>=3) {
int i = line.indexOf(']');
QString mid = line.mid(1, i-1);
ch = QString::fromUtf8(mid.toLatin1()).at(0);
}
// read normal ascii character:
else {
ch = line.at(1);
}
// create new letter:
RS_FontChar* letter =
new RS_FontChar(nullptr, ch, RS_Vector(0.0, 0.0));
// Read entities of this letter:
QString coordsStr;
QStringList coords;
QStringList::Iterator it2;
do {
line = ts.readLine();
if (line.isEmpty()) {
continue;
}
coordsStr = line.right(line.length()-2);
// coords = QStringList::split(',', coordsStr);
coords = coordsStr.split(',', QString::SkipEmptyParts);
it2 = coords.begin();
// Line:
if (line.at(0)=='L') {
double x1 = (*it2++).toDouble();
double y1 = (*it2++).toDouble();
double x2 = (*it2++).toDouble();
double y2 = (*it2).toDouble();
RS_Line* line = new RS_Line{letter, {{x1, y1}, {x2, y2}}};
line->setPen(RS_Pen(RS2::FlagInvalid));
line->setLayer(nullptr);
letter->addEntity(line);
}
// Arc:
else if (line.at(0)=='A') {
double cx = (*it2++).toDouble();
double cy = (*it2++).toDouble();
double r = (*it2++).toDouble();
double a1 = RS_Math::deg2rad((*it2++).toDouble());
double a2 = RS_Math::deg2rad((*it2).toDouble());
bool reversed = (line.at(1)=='R');
RS_ArcData ad(RS_Vector(cx,cy),
r, a1, a2, reversed);
RS_Arc* arc = new RS_Arc(letter, ad);
arc->setPen(RS_Pen(RS2::FlagInvalid));
arc->setLayer(nullptr);
letter->addEntity(arc);
}
} while (!line.isEmpty());
if (letter->isEmpty()) {
delete letter;
} else {
letter->calculateBorders();
letterList.add(letter);
}
}
}
f.close();
}
void RS_Font::readLFF(QString path) {
QString line;
QFile f(path);
encoding = "UTF-8";
f.open(QIODevice::ReadOnly);
QTextStream ts(&f);
// Read line by line until we find a new letter:
while (!ts.atEnd()) {
line = ts.readLine();
if (line.isEmpty())
continue;
// Read font settings:
if (line.at(0)=='#') {
QStringList lst =line.remove(0,1).split(':', QString::SkipEmptyParts);
//if size is < 2 is a comentary not parameter
if (lst.size()<2)
continue;
QString identifier = lst.at(0).trimmed();
QString value = lst.at(1).trimmed();
if (identifier.toLower()=="letterspacing") {
letterSpacing = value.toDouble();
} else if (identifier.toLower()=="wordspacing") {
wordSpacing = value.toDouble();
} else if (identifier.toLower()=="linespacingfactor") {
lineSpacingFactor = value.toDouble();
} else if (identifier.toLower()=="author") {
authors.append(value);
} else if (identifier.toLower()=="name") {
names.append(value);
} else if (identifier.toLower()=="license") {
fileLicense = value;
} else if (identifier.toLower()=="encoding") {
ts.setCodec(QTextCodec::codecForName(value.toLatin1()));
encoding = value;
} else if (identifier.toLower()=="created") {
fileCreate = value;
}
}
// Add another letter to this font:
else if (line.at(0)=='[') {
// uniode character:
QChar ch;
// read unicode:
QRegExp regexp("[0-9A-Fa-f]{1,5}");
regexp.indexIn(line);
QString cap = regexp.cap();
if (!cap.isNull()) {
int uCode = cap.toInt(nullptr, 16);
ch = QChar(uCode);
}
// only unicode allowed
else {
RS_DEBUG->print(RS_Debug::D_WARNING,"Ignoring code from LFF font file: %s",qPrintable(line));
continue;
}
QStringList fontData;
do {
line = ts.readLine();
if(line.isEmpty()) break;
fontData.push_back(line);
} while(true);
if(fontData.size()>0) {
rawLffFontList[QString(ch)]=fontData;
}
}
}
f.close();
}
void RS_Font::generateAllFonts(){
QMap<QString, QStringList>::const_iterator i = rawLffFontList.constBegin();
while (i != rawLffFontList.constEnd()) {
generateLffFont(i.key());
++i;
}
}
RS_Block* RS_Font::generateLffFont(const QString& ch){
if(rawLffFontList.contains(ch) == false ){
RS_DEBUG->print("RS_Font::generateLffFont(QChar %s ) : can not find the letter in given lff font file",qPrintable(ch));
return nullptr;
}
// create new letter:
RS_FontChar* letter =
new RS_FontChar(nullptr, ch, RS_Vector(0.0, 0.0));
// Read entities of this letter:
QStringList vertex;
QStringList coords;
QStringList fontData=rawLffFontList[ch];
QString line;
while(fontData.isEmpty() == false) {
line = fontData.takeFirst();
if (line.isEmpty()) {
continue;
}
// Defined char:
if (line.at(0)=='C') {
line.remove(0,1);
int uCode = line.toInt(nullptr, 16);
QChar ch = QChar(uCode);
RS_Block* bk = letterList.find(ch);
if (!bk && rawLffFontList.contains(ch)) {
generateLffFont(ch);
bk = letterList.find(ch);
}
if (bk) {
RS_Entity* bk2 = bk->clone();
bk2->setPen(RS_Pen(RS2::FlagInvalid));
bk2->setLayer(nullptr);
letter->addEntity(bk2);
}
}
//sequence:
else {
vertex = line.split(';', QString::SkipEmptyParts);
//at least is required two vertex
if (vertex.size()<2)
continue;
RS_Polyline* pline = new RS_Polyline(letter, RS_PolylineData());
pline->setPen(RS_Pen(RS2::FlagInvalid));
pline->setLayer(nullptr);
for (int i = 0; i < vertex.size(); ++i) {
double x1, y1;
double bulge = 0;
coords = vertex.at(i).split(',', QString::SkipEmptyParts);
//at least X,Y is required
if (coords.size()<2)
continue;
x1 = coords.at(0).toDouble();
y1 = coords.at(1).toDouble();
//check presence of bulge
if (coords.size() == 3 && coords.at(2).at(0) == QChar('A')){
QString bulgeStr = coords.at(2);
bulge = bulgeStr.remove(0,1).toDouble();
}
pline->setNextBulge(bulge);
pline->addVertex(RS_Vector(x1, y1), bulge);
}
letter->addEntity(pline);
}
}
if (letter->isEmpty()) {
delete letter;
return nullptr;
} else {
letter->calculateBorders();
letterList.add(letter);
return letter;
}
}
RS_Block* RS_Font::findLetter(const QString& name) {
RS_Block* ret= letterList.find(name);
if (ret) return ret;
return generateLffFont(name);
}
/**
* Dumps the fonts data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Font& f) {
os << " Font file name: " << f.getFileName().toLatin1().data() << "\n";
//<< (RS_BlockList&)f << "\n";
return os;
}

160
lib/engine/rs_font.h Normal file
View File

@@ -0,0 +1,160 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_FONT_H
#define RS_FONT_H
#include <iosfwd>
#include <QStringList>
#include <QMap>
#include "rs_blocklist.h"
/**
* Class for representing a font. This is implemented as a RS_Graphic
* with a name (the font name) and several blocks, one for each letter
* in the font.
*
* @author Andrew Mustun
*/
class RS_Font {
public:
RS_Font(const QString& name, bool owner=true);
//RS_Font(const char* name);
/** @return the fileName of this font. */
QString getFileName() const {
return fileName;
}
/** @return the fileLicense of this font. */
QString getFileLicense() const {
return fileLicense;
}
/** @return the creation date of this font. */
QString getFileCreate() const {
return fileCreate;
}
/** @return the encoding of this font. */
QString getEncoding() const {
return encoding;
}
/** @return the alternative names of this font. */
const QStringList& getNames() const {
return names;
}
/** @return the author(s) of this font. */
const QStringList& getAuthors() const {
return authors;
}
/** @return Default letter spacing for this font */
double getLetterSpacing() {
return letterSpacing;
}
/** @return Default word spacing for this font */
double getWordSpacing() {
return wordSpacing;
}
/** @return Default line spacing factor for this font */
double getLineSpacingFactor() {
return lineSpacingFactor;
}
bool loadFont();
void generateAllFonts();
// Wrappers for block list (letters) functions
RS_BlockList* getLetterList() {
return &letterList;
}
RS_Block* findLetter(const QString& name);
// RS_Block* findLetter(const QString& name) {
// return letterList.find(name);
// }
unsigned countLetters() {
return letterList.count();
}
RS_Block* letterAt(unsigned i) {
return letterList.at(i);
}
friend std::ostream& operator << (std::ostream& os, const RS_Font& l);
friend class RS_FontList;
private:
void readCXF(QString path);
void readLFF(QString path);
RS_Block* generateLffFont(const QString& ch);
private:
//raw lff font file list, not processed into blocks yet
QMap<QString, QStringList> rawLffFontList;
//! block list (letters)
RS_BlockList letterList;
//! Font file name
QString fileName;
//! Font file license
QString fileLicense;
//! Font file license
QString fileCreate;
//! Font encoding (see docu for QTextCodec)
QString encoding;
//! Font names
QStringList names;
//! Authors
QStringList authors;
//! Is this font currently loaded into memory?
bool loaded;
//! Default letter spacing for this font
double letterSpacing;
//! Default word spacing for this font
double wordSpacing;
//! Default line spacing factor for this font
double lineSpacingFactor;
};
#endif

71
lib/engine/rs_fontchar.h Normal file
View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_FONTCHAR_H
#define RS_FONTCHAR_H
#include "rs_block.h"
/**
* A character in a font is represented by this special block class.
*
* @author Andrew Mustun
*/
class RS_FontChar : public RS_Block {
public:
/**
* @param parent The font this block belongs to.
* @param name The name of the letter (a unicode char) used as
* an identifier.
* @param basePoint Base point (offset) of the letter (usually 0/0).
*/
RS_FontChar(RS_EntityContainer* parent,
const QString& name,
RS_Vector basePoint)
: RS_Block(parent, RS_BlockData(name, basePoint, false)) {}
virtual ~RS_FontChar() {}
/** @return RS2::EntityFontChar */
virtual RS2::EntityType rtti() const {
return RS2::EntityFontChar;
}
/*friend std::ostream& operator << (std::ostream& os, const RS_FontChar& b) {
os << " name: " << b.getName().latin1() << "\n";
os << " entities: " << (RS_EntityContainer&)b << "\n";
return os;
}*/
protected:
};
#endif

142
lib/engine/rs_fontlist.cpp Normal file
View File

@@ -0,0 +1,142 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 <QHash>
#include "rs_fontlist.h"
#include "rs_debug.h"
#include "rs_font.h"
#include "rs_system.h"
RS_FontList* RS_FontList::uniqueInstance = nullptr;
RS_FontList* RS_FontList::instance() {
if (!uniqueInstance) {
uniqueInstance = new RS_FontList();
}
return uniqueInstance;
}
/**
* Initializes the font list by creating empty RS_Font
* objects, one for each font that could be found.
*/
void RS_FontList::init() {
RS_DEBUG->print("RS_FontList::initFonts");
QStringList list = RS_SYSTEM->getNewFontList();
list.append(RS_SYSTEM->getFontList());
QHash<QString, int> added; //used to remember added fonts (avoid duplication)
for (int i = 0; i < list.size(); ++i) {
RS_DEBUG->print("font: %s:", list.at(i).toLatin1().data());
QFileInfo fi( list.at(i) );
if ( !added.contains(fi.baseName()) ) {
fonts.emplace_back(new RS_Font(fi.baseName()));
added.insert(fi.baseName(), 1);
}
RS_DEBUG->print("base: %s", fi.baseName().toLatin1().data());
}
}
size_t RS_FontList::countFonts() const{
return fonts.size();
}
std::vector<std::unique_ptr<RS_Font> >::const_iterator RS_FontList::begin() const
{
return fonts.begin();
}
std::vector<std::unique_ptr<RS_Font> >::const_iterator RS_FontList::end() const
{
return fonts.end();
}
/**
* Removes all fonts in the fontlist.
*/
void RS_FontList::clearFonts() {
fonts.clear();
}
/**
* @return Pointer to the font with the given name or
* \p NULL if no such font was found. The font will be loaded into
* memory if it's not already.
*/
RS_Font* RS_FontList::requestFont(const QString& name) {
RS_DEBUG->print("RS_FontList::requestFont %s", name.toLatin1().data());
QString name2 = name.toLower();
RS_Font* foundFont = NULL;
// QCAD 1 compatibility:
if (name2.contains('#') && name2.contains('_')) {
name2 = name2.left(name2.indexOf('_'));
} else if (name2.contains('#')) {
name2 = name2.left(name2.indexOf('#'));
}
RS_DEBUG->print("name2: %s", name2.toLatin1().data());
// Search our list of available fonts:
for( auto const& f: fonts){
if (f->getFileName().toLower() == name2) {
// Make sure this font is loaded into memory:
f->loadFont();
foundFont = f.get();
break;
}
}
if (!foundFont && name!="standard") {
foundFont = requestFont("standard");
}
return foundFont;
}
/**
* Dumps the fonts to stdout.
*/
std::ostream& operator << (std::ostream& os, RS_FontList& l) {
os << "Fontlist: \n";
for(auto const& f: l.fonts){
os << *f << "\n";
}
return os;
}

70
lib/engine/rs_fontlist.h Normal file
View File

@@ -0,0 +1,70 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_FONTLIST_H
#define RS_FONTLIST_H
#include <memory>
#include <vector>
class RS_Font;
#define RS_FONTLIST RS_FontList::instance()
/**
* The global list of fonts. This is implemented as a singleton.
* Use RS_FontList::instance() to get a pointer to the object.
*
* @author Andrew Mustun
*/
class RS_FontList {
public:
/**
* @return Instance to the unique font list.
*/
static RS_FontList* instance();
virtual ~RS_FontList() = default;
void init();
void clearFonts();
size_t countFonts() const;
RS_Font* requestFont(const QString& name);
std::vector<std::unique_ptr<RS_Font> >::const_iterator begin() const;
std::vector<std::unique_ptr<RS_Font> >::const_iterator end() const;
friend std::ostream& operator << (std::ostream& os, RS_FontList& l);
private:
RS_FontList()=default;
RS_FontList(RS_FontList const&)=delete;
RS_FontList& operator = (RS_FontList const&)=delete;
static RS_FontList* uniqueInstance;
//! fonts in the graphic
std::vector<std::unique_ptr<RS_Font>> fonts;
};
#endif

1047
lib/engine/rs_graphic.cpp Normal file

File diff suppressed because it is too large Load Diff

383
lib/engine/rs_graphic.h Normal file
View File

@@ -0,0 +1,383 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 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!
**
**********************************************************************/
#ifndef RS_GRAPHIC_H
#define RS_GRAPHIC_H
#include <QDateTime>
#include "rs_blocklist.h"
#include "rs_layerlist.h"
#include "rs_variabledict.h"
#include "rs_document.h"
#include "rs_units.h"
class RS_VariableDict;
class QG_LayerWidget;
/**
* A graphic document which can contain entities layers and blocks.
*
* @author Andrew Mustun
*/
class RS_Graphic : public RS_Document {
public:
RS_Graphic(RS_EntityContainer* parent=NULL);
virtual ~RS_Graphic();
//virtual RS_Entity* clone() {
// return new RS_Graphic(*this);
//}
/** @return RS2::EntityGraphic */
virtual RS2::EntityType rtti() const {
return RS2::EntityGraphic;
}
virtual unsigned long int countLayerEntities(RS_Layer* layer);
virtual RS_LayerList* getLayerList() {
return &layerList;
}
virtual RS_BlockList* getBlockList() {
return &blockList;
}
virtual void newDoc();
virtual bool save(bool isAutoSave = false);
virtual bool saveAs(const QString& filename, RS2::FormatType type, bool force = false);
virtual bool open(const QString& filename, RS2::FormatType type);
bool loadTemplate(const QString &filename, RS2::FormatType type);
// Wrappers for Layer functions:
void clearLayers() {
layerList.clear();
}
unsigned countLayers() const {
return layerList.count();
}
RS_Layer* layerAt(unsigned i) {
return layerList.at(i);
}
void activateLayer(const QString& name) {
layerList.activate(name);
}
void activateLayer(RS_Layer* layer) {
layerList.activate(layer);
}
RS_Layer* getActiveLayer() {
return layerList.getActive();
}
virtual void addLayer(RS_Layer* layer) {
layerList.add(layer);
}
virtual void addEntity(RS_Entity* entity);
virtual void removeLayer(RS_Layer* layer);
virtual void editLayer(RS_Layer* layer, const RS_Layer& source) {
layerList.edit(layer, source);
}
RS_Layer* findLayer(const QString& name) {
return layerList.find(name);
}
void toggleLayer(const QString& name) {
layerList.toggle(name);
}
void toggleLayer(RS_Layer* layer) {
layerList.toggle(layer);
}
void toggleLayerLock(RS_Layer* layer) {
layerList.toggleLock(layer);
}
void toggleLayerPrint(RS_Layer* layer) {
layerList.togglePrint(layer);
}
void toggleLayerConstruction(RS_Layer* layer) {
layerList.toggleConstruction(layer);
}
void freezeAllLayers(bool freeze) {
layerList.freezeAll(freeze);
}
void lockAllLayers(bool lock) {
layerList.lockAll(lock);
}
void addLayerListListener(RS_LayerListListener* listener) {
layerList.addListener(listener);
}
void removeLayerListListener(RS_LayerListListener* listener) {
layerList.removeListener(listener);
}
// Wrapper for block functions:
void clearBlocks() {
blockList.clear();
}
unsigned countBlocks() {
return blockList.count();
}
RS_Block* blockAt(unsigned i) {
return blockList.at(i);
}
void activateBlock(const QString& name) {
blockList.activate(name);
}
void activateBlock(RS_Block* block) {
blockList.activate(block);
}
RS_Block* getActiveBlock() {
return blockList.getActive();
}
virtual bool addBlock(RS_Block* block, bool notify=true) {
return blockList.add(block, notify);
}
virtual void addBlockNotification() {
blockList.addNotification();
}
virtual void removeBlock(RS_Block* block) {
blockList.remove(block);
}
RS_Block* findBlock(const QString& name) {
return blockList.find(name);
}
QString newBlockName() {
return blockList.newName();
}
void toggleBlock(const QString& name) {
blockList.toggle(name);
}
void toggleBlock(RS_Block* block) {
blockList.toggle(block);
}
void freezeAllBlocks(bool freeze) {
blockList.freezeAll(freeze);
}
void addBlockListListener(RS_BlockListListener* listener) {
blockList.addListener(listener);
}
void removeBlockListListener(RS_BlockListListener* listener) {
blockList.removeListener(listener);
}
// Wrappers for variable functions:
void clearVariables() {
variableDict.clear();
}
int countVariables() {
return variableDict.count();
}
void addVariable(const QString& key, const RS_Vector& value, int code) {
variableDict.add(key, value, code);
}
void addVariable(const QString& key, const QString& value, int code) {
variableDict.add(key, value, code);
}
void addVariable(const QString& key, int value, int code) {
variableDict.add(key, value, code);
}
void addVariable(const QString& key, double value, int code) {
variableDict.add(key, value, code);
}
RS_Vector getVariableVector(const QString& key, const RS_Vector& def) {
return variableDict.getVector(key, def);
}
QString getVariableString(const QString& key, const QString& def) {
return variableDict.getString(key, def);
}
int getVariableInt(const QString& key, int def) {
return variableDict.getInt(key, def);
}
double getVariableDouble(const QString& key, double def) {
return variableDict.getDouble(key, def);
}
void removeVariable(const QString& key) {
variableDict.remove(key);
}
QHash<QString, RS_Variable>& getVariableDict() {
return variableDict.getVariableDict();
}
RS2::LinearFormat getLinearFormat();
RS2::LinearFormat getLinearFormat(int f);
int getLinearPrecision();
RS2::AngleFormat getAngleFormat();
int getAnglePrecision();
RS_Vector getPaperSize();
void setPaperSize(const RS_Vector& s);
RS_Vector getPrintAreaSize(bool total=true);
RS_Vector getPaperInsertionBase();
void setPaperInsertionBase(const RS_Vector& p);
RS2::PaperFormat getPaperFormat(bool* landscape);
void setPaperFormat(RS2::PaperFormat f, bool landscape);
double getPaperScale();
void setPaperScale(double s);
virtual void setUnit(RS2::Unit u);
virtual RS2::Unit getUnit();
bool isGridOn();
void setGridOn(bool on);
bool isIsometricGrid();
void setIsometricGrid(bool on);
void setCrosshairType(RS2::CrosshairType chType);
RS2::CrosshairType getCrosshairType();
bool isDraftOn();
void setDraftOn(bool on);
/** Sets the unit of this graphic's dimensions to 'u' */
/*virtual void setDimensionUnit(RS2::Unit u) {
addVariable("$INSUNITS", (int)u, 70);
dimensionUnit = u;
}*/
/** Gets the unit of this graphic's dimension */
/*virtual RS2::Unit getDimensionUnit() {
return dimensionUnit;
}*/
void centerToPage();
bool fitToPage();
bool isBiggerThanPaper();
/**
* @retval true The document has been modified since it was last saved.
* @retval false The document has not been modified since it was last saved.
*/
virtual bool isModified() const {
return modified || layerList.isModified() || blockList.isModified();
}
/**
* Sets the documents modified status to 'm'.
*/
virtual void setModified(bool m) {
modified = m;
layerList.setModified(m);
blockList.setModified(m);
}
virtual QDateTime getModifyTime(void){
return modifiedTime;
}
//if set to true, will refuse to modify paper scale
void setPaperScaleFixed(bool fixed)
{
paperScaleFixed=fixed;
}
bool getPaperScaleFixed()
{
return paperScaleFixed;
}
/**
* Paper margins in millimeters
*/
void setMargins(double left, double top, double right, double bottom)
{
if (left >= 0.0) marginLeft = left;
if (top >= 0.0) marginTop = top;
if (right >= 0.0) marginRight = right;
if (bottom >= 0.0) marginBottom = bottom;
}
double getMarginLeft() const
{
return marginLeft;
}
double getMarginTop() const
{
return marginTop;
}
double getMarginRight() const
{
return marginRight;
}
double getMarginBottom() const
{
return marginBottom;
}
/**
* Paper margins in graphic units
*/
void setMarginsInUnits(double left, double top, double right, double bottom);
double getMarginLeftInUnits();
double getMarginTopInUnits();
double getMarginRightInUnits();
double getMarginBottomInUnits();
/**
* Number of pages drawing occupies
*/
void setPagesNum(int horiz, int vert);
void setPagesNum(const QString &horizXvert);
int getPagesNumHoriz() {
return pagesNumH;
}
int getPagesNumVert() {
return pagesNumV;
}
friend std::ostream& operator << (std::ostream& os, RS_Graphic& g);
int clean();
private:
bool BackupDrawingFile(const QString &filename);
QDateTime modifiedTime;
QString currentFileName; //keep a copy of filename for the modifiedTime
RS_LayerList layerList;
RS_BlockList blockList;
RS_VariableDict variableDict;
RS2::CrosshairType crosshairType; //crosshair type used by isometric grid
//if set to true, will refuse to modify paper scale
bool paperScaleFixed;
// Paper margins in millimeters
double marginLeft;
double marginTop;
double marginRight;
double marginBottom;
// Number of pages drawing occupies
int pagesNumH;
int pagesNumV;
};
#endif

788
lib/engine/rs_hatch.cpp Normal file
View File

@@ -0,0 +1,788 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 <memory>
#include <QPainterPath>
#include <QBrush>
#include <QString>
#include "rs_hatch.h"
#include "rs_arc.h"
#include "rs_circle.h"
#include "rs_ellipse.h"
#include "rs_line.h"
#include "rs_graphicview.h"
#include "rs_dialogfactory.h"
#include "rs_infoarea.h"
#include "rs_information.h"
#include "rs_painter.h"
#include "rs_pattern.h"
#include "rs_patternlist.h"
#include "rs_math.h"
#include "rs_debug.h"
RS_HatchData::RS_HatchData(bool _solid,
double _scale,
double _angle,
const QString& _pattern):
solid(_solid)
,scale(_scale)
,angle(_angle)
,pattern(_pattern)
{
//std::cout << "RS_HatchData: " << pattern.latin1() << "\n";
}
std::ostream& operator << (std::ostream& os, const RS_HatchData& td) {
os << "(" << td.pattern.toLatin1().data() << ")";
return os;
}
/**
* Constructor.
*/
RS_Hatch::RS_Hatch(RS_EntityContainer* parent,
const RS_HatchData& d)
: RS_EntityContainer(parent), data(d)
{
hatch = nullptr;
updateRunning = false;
needOptimization = true;
updateError = HATCH_UNDEFINED;
}
/**
* Validates the hatch.
*/
bool RS_Hatch::validate() {
bool ret = true;
// loops:
for(auto l: entities){
if (l->rtti()==RS2::EntityContainer) {
RS_EntityContainer* loop = (RS_EntityContainer*)l;
ret = loop->optimizeContours() && ret;
}
}
return ret;
}
RS_Entity* RS_Hatch::clone() const{
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::clone()");
RS_Hatch* t = new RS_Hatch(*this);
t->setOwner(isOwner());
t->initId();
t->detach();
t->update();
// t->hatch = nullptr;
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::clone(): OK");
return t;
}
/**
* @return Number of loops.
*/
int RS_Hatch::countLoops() const{
if (data.solid) {
return count();
} else {
return count() - 1;
}
}
bool RS_Hatch::isContainer() const {
return !isSolid();
}
/**
* Recalculates the borders of this hatch.
*/
void RS_Hatch::calculateBorders() {
RS_DEBUG->print("RS_Hatch::calculateBorders");
activateContour(true);
RS_EntityContainer::calculateBorders();
RS_DEBUG->print("RS_Hatch::calculateBorders: size: %f,%f",
getSize().x, getSize().y);
activateContour(false);
}
/**
* Updates the Hatch. Called when the
* hatch or it's data, position, alignment, .. changes.
*
* Refill hatch with pattern. Move, scale, rotate, trim, etc.
*/
void RS_Hatch::update() {
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update");
updateError = HATCH_OK;
if (updateRunning) {
RS_DEBUG->print(RS_Debug::D_NOTICE, "RS_Hatch::update: skip hatch in updating process");
return;
}
if (updateEnabled==false) {
RS_DEBUG->print(RS_Debug::D_NOTICE, "RS_Hatch::update: skip hatch forbidden to update");
return;
}
if (data.solid==true) {
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: processing solid hatch");
calculateBorders();
return;
}
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: contour has %d loops", count());
updateRunning = true;
// save attributes for the current hatch
RS_Layer* hatch_layer = this->getLayer();
RS_Pen hatch_pen = this->getPen();
// delete old hatch:
if (hatch) {
removeEntity(hatch);
hatch = nullptr;
}
if (isUndone()) {
RS_DEBUG->print(RS_Debug::D_NOTICE, "RS_Hatch::update: skip undone hatch");
updateRunning = false;
return;
}
if (!validate()) {
RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Hatch::update: invalid contour in hatch found");
updateRunning = false;
updateError = HATCH_INVALID_CONTOUR;
return;
}
// search for pattern
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: requesting pattern");
RS_Pattern* pat = RS_PATTERNLIST->requestPattern(data.pattern);
if (!pat) {
updateRunning = false;
RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Hatch::update: requesting pattern: not found");
updateError = HATCH_PATTERN_NOT_FOUND;
return;
} else {
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: requesting pattern: OK");
// make a working copy of hatch pattern
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: cloning pattern");
pat = (RS_Pattern*)pat->clone();
if (pat) {
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: cloning pattern: OK");
} else {
RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Hatch::update: error while cloning hatch pattern");
return;
}
}
// scale pattern
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: scaling pattern");
pat->scale(RS_Vector(0.0,0.0), RS_Vector(data.scale, data.scale));
pat->calculateBorders();
forcedCalculateBorders();
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: scaling pattern: OK");
// find out how many pattern-instances we need in x/y:
int px1, py1, px2, py2;
double f;
RS_Hatch* copy = (RS_Hatch*)this->clone();
copy->rotate(RS_Vector(0.0,0.0), -data.angle);
copy->forcedCalculateBorders();
// create a pattern over the whole contour.
RS_Vector pSize = pat->getSize();
RS_Vector rot_center=pat->getMin();
// RS_Vector cPos = getMin();
RS_Vector cSize = getSize();
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: pattern size: %f/%f", pSize.x, pSize.y);
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: contour size: %f/%f", cSize.x, cSize.y);
// check pattern sizes for sanity
if (cSize.x<1.0e-6 || cSize.y<1.0e-6 ||
pSize.x<1.0e-6 || pSize.y<1.0e-6 ||
cSize.x>RS_MAXDOUBLE-1 || cSize.y>RS_MAXDOUBLE-1 ||
pSize.x>RS_MAXDOUBLE-1 || pSize.y>RS_MAXDOUBLE-1) {
delete pat;
delete copy;
updateRunning = false;
RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Hatch::update: contour size or pattern size too small");
updateError = HATCH_TOO_SMALL;
return;
}
// avoid huge memory consumption:
else if ( cSize.x* cSize.y/(pSize.x*pSize.y)>1e4) {
RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Hatch::update: contour size too large or pattern size too small");
delete pat;
delete copy;
updateError = HATCH_AREA_TOO_BIG;
return;
}
// calculate pattern pieces quantity
f = copy->getMin().x/pSize.x;
px1 = (int)floor(f);
f = copy->getMin().y/pSize.y;
py1 = (int)floor(f);
f = copy->getMax().x/pSize.x;
px2 = (int)ceil(f);
f = copy->getMax().y/pSize.y;
py2 = (int)ceil(f);
RS_Vector dvx=RS_Vector(data.angle)*pSize.x;
RS_Vector dvy=RS_Vector(data.angle+M_PI*0.5)*pSize.y;
pat->rotate(rot_center, data.angle);
pat->move(-rot_center);
RS_EntityContainer tmp; // container for untrimmed lines
// adding array of patterns to tmp:
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: creating pattern carpet");
for (int px=px1; px<px2; px++) {
for (int py=py1; py<py2; py++) {
for(auto e: *pat){
RS_Entity* te=e->clone();
te->move(dvx*px + dvy*py);
tmp.addEntity(te);
}
}
}
// clean memory
delete pat;
pat = nullptr;
delete copy;
copy = nullptr;
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: creating pattern carpet: OK");
// cut pattern to contour shape
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: cutting pattern carpet");
RS_EntityContainer tmp2; // container for small cut lines
RS_Line* line = nullptr;
RS_Arc* arc = nullptr;
RS_Circle* circle = nullptr;
RS_Ellipse* ellipse = nullptr;
for(auto e: tmp) {
if (!e) {
RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Hatch::update: nullptr entity found");
continue;
}
line = nullptr;
arc = nullptr;
circle = nullptr;
ellipse = nullptr;
RS_Vector startPoint;
RS_Vector endPoint;
RS_Vector center = RS_Vector(false);
bool reversed=false;
switch(e->rtti()) {
case RS2::EntityLine:
line=static_cast<RS_Line*>(e);
startPoint = line->getStartpoint();
endPoint = line->getEndpoint();
break;
case RS2::EntityArc:
arc=static_cast<RS_Arc*>(e);
startPoint = arc->getStartpoint();
endPoint = arc->getEndpoint();
center = arc->getCenter();
reversed = arc->isReversed();
break;
case RS2::EntityCircle:
circle=static_cast<RS_Circle*>(e);
startPoint = circle->getCenter()
+ RS_Vector(circle->getRadius(), 0.0);
endPoint = startPoint;
center = circle->getCenter();
break;
case RS2::EntityEllipse:
ellipse = static_cast<RS_Ellipse*>(e);
startPoint = ellipse->getStartpoint();
endPoint = ellipse->getEndpoint();
center = ellipse->getCenter();
reversed = ellipse->isReversed();
break;
default:
continue;
}
// getting all intersections of this pattern line with the contour:
QList<RS_Vector> is;
for(auto loop: entities){
if (loop->isContainer()) {
for(auto p: * static_cast<RS_EntityContainer*>(loop)){
RS_VectorSolutions sol =
RS_Information::getIntersection(e, p, true);
for (const RS_Vector& vp: sol) {
if (vp.valid) {
is.append(vp);
RS_DEBUG->print(RS_Debug::D_DEBUGGING, " pattern line intersection: %f/%f", vp.x, vp.y);
}
}
}
}
}
QList<RS_Vector> is2; //to be filled with sorted intersections
is2.append(startPoint);
// sort the intersection points into is2 (only if there are intersections):
if(is.size() == 1) { //only one intersection
is2.append(is.first());
}
else if(is.size() > 1) {
RS_Vector sp = startPoint;
double sa = center.angleTo(sp);
if(ellipse ) sa=ellipse->getEllipseAngle(sp);
bool done;
double minDist;
double dist = 0.0;
RS_Vector av;
RS_Vector v;
RS_Vector last{};
do { // very long while(!done) loop
done = true;
minDist = RS_MAXDOUBLE;
av.valid = false;
for (int i = 0; i < is.size(); ++i) {
v = is.at(i);
double a;
switch(e->rtti()){
case RS2::EntityLine:
dist = sp.distanceTo(v);
break;
case RS2::EntityArc:
case RS2::EntityCircle:
a = center.angleTo(v);
dist = reversed?
fmod(sa - a + 2.*M_PI,2.*M_PI):
fmod(a - sa + 2.*M_PI,2.*M_PI);
break;
case RS2::EntityEllipse:
a = ellipse->getEllipseAngle(v);
dist = reversed?
fmod(sa - a + 2.*M_PI,2.*M_PI):
fmod(a - sa + 2.*M_PI,2.*M_PI);
break;
default:
break;
}
if (dist<minDist) {
minDist = dist;
done = false;
av = v;
}
}
// copy to sorted list, removing double points
if (!done && av) {
if (last.valid==false || last.distanceTo(av)>RS_TOLERANCE) {
is2.append(av);
last = av;
}
is.removeOne(av);
av.valid = false;
}
} while(!done);
}
is2.append(endPoint);
// add small cut lines / arcs to tmp2:
for (int i = 1; i < is2.size(); ++i) {
auto v1 = is2.at(i-1);
auto v2 = is2.at(i);
if (line) {
tmp2.addEntity(new RS_Line{&tmp2, v1, v2});
} else if (arc || circle) {
if(fabs(center.angleTo(v2)-center.angleTo(v1)) > RS_TOLERANCE_ANGLE)
{//don't create an arc with a too small angle
tmp2.addEntity(new RS_Arc(&tmp2,
RS_ArcData(center,
center.distanceTo(v1),
center.angleTo(v1),
center.angleTo(v2),
reversed)));
}
}
}
} // end for very very long for(auto e: tmp) loop
// updating hatch / adding entities that are inside
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: cutting pattern carpet: OK");
//RS_EntityContainer* rubbish = new RS_EntityContainer(getGraphic());
// add the hatch pattern entities
hatch = new RS_EntityContainer(this);
hatch->setPen(hatch_pen);
hatch->setLayer(hatch_layer);
hatch->setFlag(RS2::FlagTemp);
//calculateBorders();
for(auto e: tmp2){
RS_Vector middlePoint;
RS_Vector middlePoint2;
if (e->rtti()==RS2::EntityLine) {
RS_Line* line = static_cast<RS_Line*>(e);
middlePoint = line->getMiddlePoint();
middlePoint2 = line->getNearestDist(line->getLength()/2.1,
line->getStartpoint());
} else if (e->rtti()==RS2::EntityArc) {
RS_Arc* arc = static_cast<RS_Arc*>(e);
middlePoint = arc->getMiddlePoint();
middlePoint2 = arc->getNearestDist(arc->getLength()/2.1,
arc->getStartpoint());
} else {
middlePoint = RS_Vector{false};
middlePoint2 = RS_Vector{false};
}
if (middlePoint.valid) {
bool onContour=false;
if (RS_Information::isPointInsideContour(
middlePoint,
this, &onContour) ||
RS_Information::isPointInsideContour(middlePoint2, this)) {
RS_Entity* te = e->clone();
te->setPen(hatch_pen);
te->setLayer(hatch_layer);
te->reparent(hatch);
hatch->addEntity(te);
}
}
}
addEntity(hatch);
//getGraphic()->addEntity(rubbish);
forcedCalculateBorders();
// deactivate contour:
activateContour(false);
updateRunning = false;
RS_DEBUG->print(RS_Debug::D_DEBUGGING, "RS_Hatch::update: OK");
}
/**
* Activates of deactivates the hatch boundary.
*/
void RS_Hatch::activateContour(bool on) {
RS_DEBUG->print("RS_Hatch::activateContour: %d", (int)on);
for(auto e: entities){
if (!e->isUndone()) {
if (!e->getFlag(RS2::FlagTemp)) {
RS_DEBUG->print("RS_Hatch::activateContour: set visible");
e->setVisible(on);
}
else {
RS_DEBUG->print("RS_Hatch::activateContour: entity temp");
}
}
else {
RS_DEBUG->print("RS_Hatch::activateContour: entity undone");
}
}
RS_DEBUG->print("RS_Hatch::activateContour: OK");
}
/**
* Overrides drawing of subentities. This is only ever called for solid fills.
*/
void RS_Hatch::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) {
if (!data.solid) {
foreach (auto se, entities){
view->drawEntity(painter,se);
}
return;
}
//area of solid fill. Use polygon approximation, except trivial cases
QPainterPath path;
QList<QPolygon> paClosed;
QPolygon pa;
if (needOptimization==true) {
foreach (auto l, entities){
if (l->rtti()==RS2::EntityContainer) {
RS_EntityContainer* loop = (RS_EntityContainer*)l;
loop->optimizeContours();
}
}
needOptimization = false;
}
foreach (auto l, entities){
l->setLayer(getLayer());
if (l->rtti()==RS2::EntityContainer) {
RS_EntityContainer* loop = (RS_EntityContainer*)l;
// edges:
for(auto e: *loop){
e->setLayer(getLayer());
switch (e->rtti()) {
case RS2::EntityLine: {
QPoint pt1(RS_Math::round(view->toGuiX(e->getStartpoint().x)),
RS_Math::round(view->toGuiY(e->getStartpoint().y)));
QPoint pt2(RS_Math::round(view->toGuiX(e->getEndpoint().x)),
RS_Math::round(view->toGuiY(e->getEndpoint().y)));
if(!pa.size() || (pa.last() - pt1).manhattanLength() >= 1) {
pa << pt1;
}
pa << pt2;
}
break;
case RS2::EntityArc: {
QPolygon pa2;
RS_Arc* arc=static_cast<RS_Arc*>(e);
painter->createArc(pa2, view->toGui(arc->getCenter()),
view->toGuiDX(arc->getRadius())
,arc->getAngle1(),arc->getAngle2(),arc->isReversed());
if(pa.size() &&pa2.size()&&(pa.last()-pa2.first()).manhattanLength()<1)
pa2.remove(0,1);
pa<<pa2;
}
break;
case RS2::EntityCircle: {
RS_Circle* circle = static_cast<RS_Circle*>(e);
RS_Vector c=view->toGui(circle->getCenter());
double r=view->toGuiDX(circle->getRadius());
path.addEllipse(QPoint(c.x,c.y),r,r);
}
break;
case RS2::EntityEllipse:
if(static_cast<RS_Ellipse*>(e)->isArc()) {
QPolygon pa2;
auto ellipse=static_cast<RS_Ellipse*>(e);
painter->createEllipse(pa2,
view->toGui(ellipse->getCenter()),
view->toGuiDX(ellipse->getMajorRadius()),
view->toGuiDX(ellipse->getMinorRadius()),
ellipse->getAngle()
,ellipse->getAngle1(),ellipse->getAngle2(),ellipse->isReversed()
);
if(pa.size() && pa2.size()&&(pa.last()-pa2.first()).manhattanLength()<1)
pa2.remove(0,1);
pa<<pa2;
}else{
QPolygon pa2;
auto ellipse=static_cast<RS_Ellipse*>(e);
painter->createEllipse(pa2,
view->toGui(ellipse->getCenter()),
view->toGuiDX(ellipse->getMajorRadius()),
view->toGuiDX(ellipse->getMinorRadius()),
ellipse->getAngle(),
ellipse->getAngle1(), ellipse->getAngle2(),
ellipse->isReversed()
);
path.addPolygon(pa2);
}
break;
default:
break;
}
if( pa.size()>2 && pa.first() == pa.last()) {
paClosed<<pa;
pa.clear();
}
}
}
}
if(pa.size()>2){
pa<<pa.first();
paClosed<<pa;
}
for(auto& p:paClosed){
path.addPolygon(p);
}
//bug#474, restore brush after solid fill
const QBrush brush(painter->brush());
const RS_Pen pen=painter->getPen();
painter->setBrush(pen.getColor());
painter->disablePen();
painter->drawPath(path);
painter->setBrush(brush);
painter->setPen(pen);
}
//must be called after update()
double RS_Hatch::getTotalArea() {
double totalArea=0.;
// loops:
for(auto l: entities){
if (l!=hatch && l->rtti()==RS2::EntityContainer) {
totalArea += l->areaLineIntegral();
}
}
return totalArea;
}
double RS_Hatch::getDistanceToPoint(
const RS_Vector& coord,
RS_Entity** entity,
RS2::ResolveLevel level,
double solidDist) const {
if (data.solid==true) {
if (entity) {
*entity = const_cast<RS_Hatch*>(this);
}
bool onContour;
if (RS_Information::isPointInsideContour(
coord,
const_cast<RS_Hatch*>(this), &onContour)) {
// distance is the snap range:
return solidDist;
}
return RS_MAXDOUBLE;
} else {
return RS_EntityContainer::getDistanceToPoint(coord, entity,
level, solidDist);
}
}
void RS_Hatch::move(const RS_Vector& offset) {
RS_EntityContainer::move(offset);
update();
}
void RS_Hatch::rotate(const RS_Vector& center, const double& angle) {
RS_EntityContainer::rotate(center, angle);
data.angle = RS_Math::correctAngle(data.angle+angle);
update();
}
void RS_Hatch::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_EntityContainer::rotate(center, angleVector);
data.angle = RS_Math::correctAngle(data.angle+angleVector.angle());
update();
}
void RS_Hatch::scale(const RS_Vector& center, const RS_Vector& factor) {
RS_EntityContainer::scale(center, factor);
data.scale *= factor.x;
update();
}
void RS_Hatch::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
RS_EntityContainer::mirror(axisPoint1, axisPoint2);
double ang = axisPoint1.angleTo(axisPoint2);
data.angle = RS_Math::correctAngle(data.angle + ang*2.0);
update();
}
void RS_Hatch::stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) {
RS_EntityContainer::stretch(firstCorner, secondCorner, offset);
update();
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Hatch& p) {
os << " Hatch: " << p.getData() << "\n";
return os;
}

175
lib/engine/rs_hatch.h Normal file
View File

@@ -0,0 +1,175 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_HATCH_H
#define RS_HATCH_H
#include "rs_entity.h"
#include "rs_entitycontainer.h"
/**
* Holds the data that defines a hatch entity.
*/
struct RS_HatchData {
/**
* Default constructor. Leaves the data object uninitialized.
*/
RS_HatchData() = default;
~RS_HatchData() = default;
/**
* @param solid true: solid fill, false: pattern.
* @param scale Pattern scale or spacing.
* @param pattern Pattern name.
*/
RS_HatchData(bool solid,
double scale,
double angle,
const QString& pattern);
bool solid;
double scale;
double angle;
QString pattern;
};
std::ostream& operator << (std::ostream& os, const RS_HatchData& td);
/**
* Class for a hatch entity.
*
* @author Andrew Mustun
*/
class RS_Hatch : public RS_EntityContainer {
public:
enum RS_HatchError { HATCH_UNDEFINED = -1,
HATCH_OK,
HATCH_INVALID_CONTOUR,
HATCH_PATTERN_NOT_FOUND,
HATCH_TOO_SMALL,
HATCH_AREA_TOO_BIG };
RS_Hatch() = default;
RS_Hatch(RS_EntityContainer* parent,
const RS_HatchData& d);
RS_Entity* clone() const override;
/** @return RS2::EntityHatch */
RS2::EntityType rtti() const override{
return RS2::EntityHatch;
}
/**
* @return true: if this is a hatch with lines (hatch pattern),
* false: if this is filled with a solid color.
*/
bool isContainer() const override;
/** @return Copy of data that defines the hatch. */
RS_HatchData getData() const {
return data;
}
bool validate();
int countLoops() const;
/** @return true if this is a solid fill. false if it is a pattern hatch. */
bool isSolid() const {
return data.solid;
}
void setSolid(bool solid) {
data.solid = solid;
}
QString getPattern() {
return data.pattern;
}
void setPattern(const QString& pattern) {
data.pattern = pattern;
}
double getScale() {
return data.scale;
}
void setScale(double scale) {
data.scale = scale;
}
double getAngle() {
return data.angle;
}
void setAngle(double angle) {
data.angle = angle;
}
double getTotalArea();
void calculateBorders() override;
void update() override;
int getUpdateError() {
return updateError;
}
void activateContour(bool on);
void draw(RS_Painter* painter, RS_GraphicView* view,
double& patternOffset) override;
// double getLength() {
// return -1.0;
// }
double getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity = NULL,
RS2::ResolveLevel level = RS2::ResolveNone,
double solidDist = RS_MAXDOUBLE) const override;
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Hatch& p);
protected:
RS_HatchData data;
RS_EntityContainer* hatch;
bool updateRunning;
bool needOptimization;
int updateError;
};
#endif

418
lib/engine/rs_image.cpp Normal file
View File

@@ -0,0 +1,418 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 <QImage>
#include "rs_image.h"
#include "rs_line.h"
#include "rs_settings.h"
#include "rs_constructionline.h"
#include "rs_debug.h"
#include "rs_graphicview.h"
#include "rs_painterqt.h"
#include "rs_math.h"
RS_ImageData::RS_ImageData(int _handle,
const RS_Vector& _insertionPoint,
const RS_Vector& _uVector,
const RS_Vector& _vVector,
const RS_Vector& _size,
const QString& _file,
int _brightness,
int _contrast,
int _fade):
handle(_handle)
, insertionPoint(_insertionPoint)
, uVector(_uVector)
, vVector(_vVector)
, size(_size)
, file(_file)
, brightness(_brightness)
, contrast(_contrast)
, fade(_fade)
{
}
std::ostream& operator << (std::ostream& os, const RS_ImageData& ld) {
os << "(" << ld.insertionPoint << ")";
return os;
}
/**
* Constructor.
*/
RS_Image::RS_Image(RS_EntityContainer* parent,
const RS_ImageData& d)
:RS_AtomicEntity(parent), data(d) {
update();
calculateBorders();
}
RS_Image::RS_Image(const RS_Image& _image):
RS_AtomicEntity(_image.getParent())
,data(_image.data)
,img(_image.img.get()?new QImage(*_image.img):nullptr)
{
}
RS_Image& RS_Image::operator = (const RS_Image& _image)
{
data=_image.data;
if(_image.img.get()){
img.reset(new QImage(*_image.img));
}else{
img.reset();
}
return *this;
}
RS_Image::RS_Image(RS_Image&& _image):
RS_AtomicEntity(_image.getParent())
,data(std::move(_image.data))
,img(std::move(_image.img))
{
}
RS_Image& RS_Image::operator = (RS_Image&& _image)
{
data=_image.data;
img = std::move(_image.img);
return *this;
}
RS_Entity* RS_Image::clone() const {
RS_Image* i = new RS_Image(*this);
i->setHandle(getHandle());
i->initId();
i->update();
return i;
}
void RS_Image::updateData(RS_Vector size, RS_Vector Uv, RS_Vector Vv) {
data.size = size;
data.uVector = Uv;
data.vVector = Vv;
update();
calculateBorders();
}
void RS_Image::update() {
RS_DEBUG->print("RS_Image::update");
// the whole image:
//QImage image = QImage(data.file);
img.reset(new QImage(data.file));
if (!img->isNull()) {
data.size = RS_Vector(img->width(), img->height());
calculateBorders(); // image update need this.
}
RS_DEBUG->print("RS_Image::update: OK");
/*
// number of small images:
nx = image.width()/100;
ny = image.height()/100;
// create small images:
img = new QImage*[nx];
QPixmap pm;
int w,h;
for (int x = 0; x<nx; ++x) {
img[x] = new QImage[ny];
for (int y = 0; y<ny; ++y) {
if (x<nx-1) {
w = 100;
}
else {
w = image.width()%100;
}
if (y<ny-1) {
h = 100;
}
else {
h = image.height()%100;
}
pm = QPixmap(w, h);
RS_PainterQt painter(&pm);
painter.drawImage(-x*100, -y*100, image);
img[x][y] = pm.convertToImage();
}
}
*/
}
void RS_Image::calculateBorders() {
RS_VectorSolutions sol = getCorners();
minV = RS_Vector::minimum(
RS_Vector::minimum(sol.get(0), sol.get(1)),
RS_Vector::minimum(sol.get(2), sol.get(3))
);
maxV = RS_Vector::maximum(
RS_Vector::maximum(sol.get(0), sol.get(1)),
RS_Vector::maximum(sol.get(2), sol.get(3))
);
}
RS_VectorSolutions RS_Image::getCorners() const {
RS_VectorSolutions sol(4);
sol.set(0, data.insertionPoint);
sol.set(1,
data.insertionPoint + data.uVector*RS_Math::round(data.size.x));
sol.set(3,
data.insertionPoint + data.vVector*RS_Math::round(data.size.y));
sol.set(2, sol.get(3) + data.uVector*RS_Math::round(data.size.x));
return sol;
}
/**
* whether a given point is within image region
*@ coord, a point
*@ returns true, if the point is within borders of image
*/
bool RS_Image::containsPoint(const RS_Vector& coord) const{
QPolygonF paf;
RS_VectorSolutions corners =getCorners();
for(const RS_Vector& vp: corners){
paf.push_back(QPointF(vp.x, vp.y));
}
paf.push_back(paf.at(0));
return paf.containsPoint(QPointF(coord.x,coord.y),Qt::OddEvenFill);
}
RS_Vector RS_Image::getNearestEndpoint(const RS_Vector& coord,
double* dist) const {
RS_VectorSolutions corners =getCorners();
return corners.getClosest(coord, dist);
}
RS_Vector RS_Image::getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity, double* dist, RS_Entity** entity) const{
if (entity) {
*entity = const_cast<RS_Image*>(this);
}
RS_VectorSolutions const& corners =getCorners();
//allow selecting image by clicking within images, bug#3464626
if(containsPoint(coord)){
//if coord is within image
if(dist) *dist=0.;
return coord;
}
RS_VectorSolutions points;
for (size_t i=0; i < corners.size(); ++i){
size_t const j = (i+1)%corners.size();
RS_Line const l{corners.at(i), corners.at(j)};
RS_Vector const vp = l.getNearestPointOnEntity(coord, onEntity);
points.push_back(vp);
}
return points.getClosest(coord, dist);
}
RS_Vector RS_Image::getNearestCenter(const RS_Vector& coord,
double* dist) const{
RS_VectorSolutions const& corners{getCorners()};
//bug#485, there's no clear reason to ignore snapping to center within an image
// if(containsPoint(coord)){
// //if coord is within image
// if(dist) *dist=0.;
// return coord;
// }
RS_VectorSolutions points;
for (size_t i=0; i < corners.size(); ++i) {
size_t const j = (i+1)%corners.size();
points.push_back((corners.get(i) + corners.get(j))*0.5);
}
points.push_back((corners.get(0) + corners.get(2))*0.5);
return points.getClosest(coord, dist);
}
/*
* ToDo, implement middlePoints
*/
RS_Vector RS_Image::getNearestMiddle(const RS_Vector& coord,
double* dist,
const int /*middlePoints*/) const{
return getNearestCenter(coord, dist);
}
RS_Vector RS_Image::getNearestDist(double distance,
const RS_Vector& coord,
double* dist) const{
RS_VectorSolutions const& corners = getCorners();
RS_VectorSolutions points;
for (size_t i = 0; i < corners.size(); ++i){
size_t const j = (i+1)%corners.size();
RS_Line const l{corners.get(i), corners.get(j)};
RS_Vector const& vp = l.getNearestDist(distance, coord, dist);
points.push_back(vp);
}
return points.getClosest(coord, dist);
}
double RS_Image::getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity,
RS2::ResolveLevel /*level*/,
double /*solidDist*/) const{
if (entity) {
*entity = const_cast<RS_Image*>(this);
}
RS_VectorSolutions corners = getCorners();
//allow selecting image by clicking within images, bug#3464626
if(containsPoint(coord)){
//if coord is on image
RS_SETTINGS->beginGroup("/Appearance");
bool draftMode = (bool)RS_SETTINGS->readNumEntry("/DraftMode", 0);
RS_SETTINGS->endGroup();
if(!draftMode) return double(0.);
}
//continue to allow selecting by image edges
double minDist = RS_MAXDOUBLE;
for (size_t i = 0; i < corners.size(); ++i){
size_t const j = (i+1)%corners.size();
RS_Line const l{corners.get(i), corners.get(j)};
double const dist = l.getDistanceToPoint(coord, nullptr);
minDist = std::min(minDist, dist);
}
return minDist;
}
void RS_Image::move(const RS_Vector& offset) {
data.insertionPoint.move(offset);
moveBorders(offset);
}
void RS_Image::rotate(const RS_Vector& center, const double& angle) {
RS_Vector angleVector(angle);
data.insertionPoint.rotate(center, angleVector);
data.uVector.rotate(angleVector);
data.vVector.rotate(angleVector);
calculateBorders();
}
void RS_Image::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
data.insertionPoint.rotate(center, angleVector);
data.uVector.rotate(angleVector);
data.vVector.rotate(angleVector);
calculateBorders();
}
void RS_Image::scale(const RS_Vector& center, const RS_Vector& factor) {
data.insertionPoint.scale(center, factor);
data.uVector.scale(factor);
data.vVector.scale(factor);
scaleBorders(center,factor);
}
void RS_Image::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
data.insertionPoint.mirror(axisPoint1, axisPoint2);
RS_Vector vp0(0.,0.);
RS_Vector vp1( axisPoint2-axisPoint1 );
data.uVector.mirror(vp0,vp1);
data.vVector.mirror(vp0,vp1);
calculateBorders();
}
void RS_Image::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) {
if (!(painter && view) || !img.get() || img->isNull())
return;
// erase image:
//if (painter->getPen().getColor()==view->getBackground()) {
// RS_VectorSolutions sol = getCorners();
//
//}
RS_Vector scale{view->toGuiDX(data.uVector.magnitude()),
view->toGuiDY(data.vVector.magnitude())};
double angle = data.uVector.angle();
painter->drawImg(*img,
view->toGui(data.insertionPoint),
angle, scale);
if (isSelected() && !(view->isPrinting() || view->isPrintPreview())) {
RS_VectorSolutions sol = getCorners();
for (size_t i = 0; i < sol.size(); ++i){
size_t const j = (i+1)%sol.size();
painter->drawLine(view->toGui(sol.get(i)), view->toGui(sol.get(j)));
}
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Image& i) {
os << " Image: " << i.getData() << "\n";
return os;
}

229
lib/engine/rs_image.h Normal file
View File

@@ -0,0 +1,229 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_IMAGE_H
#define RS_IMAGE_H
#include <memory>
#include "rs_atomicentity.h"
class QImage;
/**
* Holds the data that defines a line.
*/
struct RS_ImageData {
/**
* Default constructor. Leaves the data object uninitialized.
*/
RS_ImageData() = default;
RS_ImageData(int handle,
const RS_Vector& insertionPoint,
const RS_Vector& uVector,
const RS_Vector& vVector,
const RS_Vector& size,
const QString& file,
int brightness,
int contrast,
int fade);
/** Handle of image definition. */
int handle;
/** Insertion point. */
RS_Vector insertionPoint;
/** u vector. Points along visual bottom of image. */
RS_Vector uVector;
/** v vector. Points along visual left of image. */
RS_Vector vVector;
/** Image size in pixel. */
RS_Vector size;
/** Path to image file. */
QString file;
/** Brightness (0..100, default: 50). */
int brightness;
/** Contrast (0..100, default: 50). */
int contrast;
/** Fade (0..100, default: 0). */
int fade;
};
/**
* Class for a line entity.
*
* @author Andrew Mustun
*/
class RS_Image : public RS_AtomicEntity {
public:
RS_Image(RS_EntityContainer* parent,
const RS_ImageData& d);
RS_Image(const RS_Image& _image);
RS_Image(RS_Image&& _image);
RS_Image& operator = (const RS_Image& _image);
RS_Image& operator = (RS_Image&& _image);
RS_Entity* clone() const override;
/** @return RS2::EntityImage */
RS2::EntityType rtti() const override{
return RS2::EntityImage;
}
void update() override;
/** @return Copy of data that defines the image. */
RS_ImageData getData() const {
return data;
}
/** @return Insertion point of the entity */
RS_Vector getInsertionPoint() const {
return data.insertionPoint;
}
/** Sets the insertion point for the image. */
void setInsertionPoint(RS_Vector ip) {
data.insertionPoint = ip;
calculateBorders();
}
/** Update image data ONLY for plugins. */
void updateData(RS_Vector size, RS_Vector Uv, RS_Vector Vv);
/** @return File name of the image. */
QString getFile() const {
return data.file;
}
/** Sets the file name of the image. */
void setFile(const QString& file) {
data.file = file;
}
/** @return u Vector. Points along bottom, 1 pixel long. */
RS_Vector getUVector() const {
return data.uVector;
}
/** @return v Vector. Points along left, 1 pixel long. */
RS_Vector getVVector() const {
return data.vVector;
}
/** @return Width of image in pixels. */
int getWidth() const {
return (int)data.size.x;
}
/** @return Height of image in pixels. */
int getHeight() const {
return (int)data.size.y;
}
/** @return Brightness. */
int getBrightness() const {
return data.brightness;
}
/** @return Contrast. */
int getContrast() const {
return data.contrast;
}
/** @return Fade. */
int getFade() const {
return data.fade;
}
/** @return Image definition handle. */
int getHandle() const {
return data.handle;
}
/** Sets the image definition handle. */
void setHandle(int h) {
data.handle = h;
}
/** @return The four corners. **/
RS_VectorSolutions getCorners() const;
/**
* @return image with in graphic units.
*/
double getImageWidth() {
return data.size.x * data.uVector.magnitude();
}
/**
* @return image height in graphic units.
*/
double getImageHeight() {
return data.size.y * data.vVector.magnitude();
}
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = NULL)const override;
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity=true, double* dist = NULL, RS_Entity** entity=NULL)const override;
RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = NULL)const override;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = NULL,
int middlePoints=1)const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = NULL)const override;
double getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity=NULL,
RS2::ResolveLevel level=RS2::ResolveNone,
double solidDist = RS_MAXDOUBLE) const override;
// double getLength() const {
// return -1.0;
// }
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
/*void stretch(RS_Vector firstCorner,
RS_Vector secondCorner,
RS_Vector offset);*/
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Image& l);
void calculateBorders() override;
protected:
// whether the point is within image
bool containsPoint(const RS_Vector& coord) const;
RS_ImageData data;
std::unique_ptr<QImage> img;
//QImage** img;
//int nx;
//int ny;
};
#endif

397
lib/engine/rs_insert.cpp Normal file
View File

@@ -0,0 +1,397 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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_insert.h"
#include "rs_arc.h"
#include "rs_circle.h"
#include "rs_ellipse.h"
#include "rs_block.h"
#include "rs_graphic.h"
#include "rs_layer.h"
#include "rs_math.h"
#include "rs_debug.h"
RS_InsertData::RS_InsertData(const QString& _name,
RS_Vector _insertionPoint,
RS_Vector _scaleFactor,
double _angle,
int _cols, int _rows, RS_Vector _spacing,
RS_BlockList* _blockSource ,
RS2::UpdateMode _updateMode ):
name(_name)
,insertionPoint(_insertionPoint)
,scaleFactor(_scaleFactor)
,angle(_angle)
,cols(_cols)
,rows(_rows)
,spacing(_spacing)
,blockSource(_blockSource)
,updateMode(_updateMode)
{
}
std::ostream& operator << (std::ostream& os,
const RS_InsertData& d) {
os << "(" << d.name.toLatin1().data() << ")";
return os;
}
/**
* @param parent The graphic this block belongs to.
*/
RS_Insert::RS_Insert(RS_EntityContainer* parent,
const RS_InsertData& d)
: RS_EntityContainer(parent), data(d) {
block = nullptr;
if (data.updateMode!=RS2::NoUpdate) {
update();
//calculateBorders();
}
}
RS_Entity* RS_Insert::clone() const{
RS_Insert* i = new RS_Insert(*this);
i->setOwner(isOwner());
i->initId();
i->detach();
return i;
}
/**
* Updates the entity buffer of this insert entity. This method
* needs to be called whenever the block this insert is based on changes.
*/
void RS_Insert::update() {
RS_DEBUG->print("RS_Insert::update");
RS_DEBUG->print("RS_Insert::update: name: %s", data.name.toLatin1().data());
// RS_DEBUG->print("RS_Insert::update: insertionPoint: %f/%f",
// data.insertionPoint.x, data.insertionPoint.y);
if (updateEnabled==false) {
return;
}
clear();
RS_Block* blk = getBlockForInsert();
if (!blk) {
//return nullptr;
RS_DEBUG->print("RS_Insert::update: Block is nullptr");
return;
}
if (isUndone()) {
RS_DEBUG->print("RS_Insert::update: Insert is in undo list");
return;
}
if (fabs(data.scaleFactor.x)<1.0e-6 || fabs(data.scaleFactor.y)<1.0e-6) {
RS_DEBUG->print("RS_Insert::update: scale factor is 0");
return;
}
RS_Pen tmpPen;
/*QListIterator<RS_Entity> it = createIterator();
RS_Entity* e;
while ( (e = it.current()) ) {
++it;*/
RS_DEBUG->print("RS_Insert::update: cols: %d, rows: %d",
data.cols, data.rows);
RS_DEBUG->print("RS_Insert::update: block has %d entities",
blk->count());
//int i_en_counts=0;
for(auto e: *blk){
for (int c=0; c<data.cols; ++c) {
// RS_DEBUG->print("RS_Insert::update: col %d", c);
for (int r=0; r<data.rows; ++r) {
// i_en_counts++;
// RS_DEBUG->print("RS_Insert::update: row %d", r);
if (e->rtti()==RS2::EntityInsert &&
data.updateMode!=RS2::PreviewUpdate) {
// RS_DEBUG->print("RS_Insert::update: updating sub-insert");
static_cast<RS_Insert*>(e)->update();
}
// RS_DEBUG->print("RS_Insert::update: cloning entity");
RS_Entity* ne;
if ( (data.scaleFactor.x - data.scaleFactor.y)>1.0e-6) {
if (e->rtti()== RS2::EntityArc) {
RS_Arc* a= static_cast<RS_Arc*>(e);
ne = new RS_Ellipse{this,
{a->getCenter(), {a->getRadius(), 0.},
1, a->getAngle1(), a->getAngle2(),
a->isReversed()}
};
ne->setLayer(e->getLayer());
ne->setPen(e->getPen(false));
} else if (e->rtti()== RS2::EntityCircle) {
RS_Circle* a= static_cast<RS_Circle*>(e);
ne = new RS_Ellipse{this,
{ a->getCenter(), {a->getRadius(), 0.}, 1, 0., 2.*M_PI, false}
};
ne->setLayer(e->getLayer());
ne->setPen(e->getPen(false));
} else
ne = e->clone();
} else
ne = e->clone();
ne->initId();
ne->setUpdateEnabled(false);
// if entity layer are 0 set to insert layer to allow "1 layer control" bug ID #3602152
RS_Layer *l= ne->getLayer();//special fontchar block don't have
if (l && ne->getLayer()->getName() == "0")
ne->setLayer(this->getLayer());
ne->setParent(this);
ne->setVisible(getFlag(RS2::FlagVisible));
// RS_DEBUG->print("RS_Insert::update: transforming entity");
// Move:
// RS_DEBUG->print("RS_Insert::update: move 1");
if (fabs(data.scaleFactor.x)>1.0e-6 &&
fabs(data.scaleFactor.y)>1.0e-6) {
ne->move(data.insertionPoint +
RS_Vector(data.spacing.x/data.scaleFactor.x*c,
data.spacing.y/data.scaleFactor.y*r));
}
else {
ne->move(data.insertionPoint);
}
// Move because of block base point:
// RS_DEBUG->print("RS_Insert::update: move 2");
ne->move(blk->getBasePoint()*-1);
// Scale:
// RS_DEBUG->print("RS_Insert::update: scale");
ne->scale(data.insertionPoint, data.scaleFactor);
// Rotate:
// RS_DEBUG->print("RS_Insert::update: rotate");
ne->rotate(data.insertionPoint, data.angle);
// Select:
ne->setSelected(isSelected());
// individual entities can be on indiv. layers
tmpPen = ne->getPen(false);
// color from block (free floating):
if (tmpPen.getColor()==RS_Color(RS2::FlagByBlock)) {
tmpPen.setColor(getPen().getColor());
}
// line width from block (free floating):
if (tmpPen.getWidth()==RS2::WidthByBlock) {
tmpPen.setWidth(getPen().getWidth());
}
// line type from block (free floating):
if (tmpPen.getLineType()==RS2::LineByBlock) {
tmpPen.setLineType(getPen().getLineType());
}
// now that we've evaluated all flags, let's strip them:
// TODO: strip all flags (width, line type)
//tmpPen.setColor(tmpPen.getColor().stripFlags());
ne->setPen(tmpPen);
ne->setUpdateEnabled(true);
// insert must be updated even in preview mode
if (data.updateMode != RS2::PreviewUpdate
|| ne->rtti() == RS2::EntityInsert) {
//RS_DEBUG->print("RS_Insert::update: updating new entity");
ne->update();
}
// RS_DEBUG->print("RS_Insert::update: adding new entity");
appendEntity(ne);
// std::cout<<"done # of entity: "<<i_en_counts<<std::endl;
}
}
}
calculateBorders();
RS_DEBUG->print("RS_Insert::update: OK");
}
/**
* @return Pointer to the block associated with this Insert or
* nullptr if the block couldn't be found. Blocks are requested
* from the blockSource if one was supplied and otherwise from
* the closest parent graphic.
*/
RS_Block* RS_Insert::getBlockForInsert() const{
RS_Block* blk = nullptr;
if (block) {
blk=block;
return blk;
}
RS_BlockList* blkList;
if (!data.blockSource) {
if (getGraphic()) {
blkList = getGraphic()->getBlockList();
} else {
blkList = nullptr;
}
} else {
blkList = data.blockSource;
}
if (blkList) {
blk = blkList->find(data.name);
}
if (blk) {
}
block = blk;
return blk;
}
/**
* Is this insert visible? (re-implementation from RS_Entity)
*
* @return true Only if the entity and the block and the layer it is on
* are visible.
* The Layer might also be nullptr. In that case the layer visibility
* is ignored.
* The Block might also be nullptr. In that case the block visibility
* is ignored.
*/
bool RS_Insert::isVisible() const
{
RS_Block* blk = getBlockForInsert();
if (blk) {
if (blk->isFrozen()) {
return false;
}
}
return RS_Entity::isVisible();
}
RS_VectorSolutions RS_Insert::getRefPoints() const
{
return RS_VectorSolutions{data.insertionPoint};
}
RS_Vector RS_Insert::getNearestRef(const RS_Vector& coord,
double* dist) const{
return getRefPoints().getClosest(coord, dist);
}
void RS_Insert::move(const RS_Vector& offset) {
RS_DEBUG->print("RS_Insert::move: offset: %f/%f",
offset.x, offset.y);
RS_DEBUG->print("RS_Insert::move1: insertionPoint: %f/%f",
data.insertionPoint.x, data.insertionPoint.y);
data.insertionPoint.move(offset);
RS_DEBUG->print("RS_Insert::move2: insertionPoint: %f/%f",
data.insertionPoint.x, data.insertionPoint.y);
update();
}
void RS_Insert::rotate(const RS_Vector& center, const double& angle) {
RS_DEBUG->print("RS_Insert::rotate1: insertionPoint: %f/%f "
"/ center: %f/%f",
data.insertionPoint.x, data.insertionPoint.y,
center.x, center.y);
data.insertionPoint.rotate(center, angle);
data.angle = RS_Math::correctAngle(data.angle+angle);
RS_DEBUG->print("RS_Insert::rotate2: insertionPoint: %f/%f",
data.insertionPoint.x, data.insertionPoint.y);
update();
}
void RS_Insert::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_DEBUG->print("RS_Insert::rotate1: insertionPoint: %f/%f "
"/ center: %f/%f",
data.insertionPoint.x, data.insertionPoint.y,
center.x, center.y);
data.insertionPoint.rotate(center, angleVector);
data.angle = RS_Math::correctAngle(data.angle+angleVector.angle());
RS_DEBUG->print("RS_Insert::rotate2: insertionPoint: %f/%f",
data.insertionPoint.x, data.insertionPoint.y);
update();
}
void RS_Insert::scale(const RS_Vector& center, const RS_Vector& factor) {
RS_DEBUG->print("RS_Insert::scale1: insertionPoint: %f/%f",
data.insertionPoint.x, data.insertionPoint.y);
data.insertionPoint.scale(center, factor);
data.scaleFactor.scale(RS_Vector(0.0, 0.0), factor);
data.spacing.scale(RS_Vector(0.0, 0.0), factor);
RS_DEBUG->print("RS_Insert::scale2: insertionPoint: %f/%f",
data.insertionPoint.x, data.insertionPoint.y);
update();
}
void RS_Insert::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
data.insertionPoint.mirror(axisPoint1, axisPoint2);
RS_Vector vec = RS_Vector::polar(1.0, data.angle);
vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1);
data.angle = RS_Math::correctAngle(vec.angle()-M_PI);
data.scaleFactor.x*=-1;
update();
}
std::ostream& operator << (std::ostream& os, const RS_Insert& i) {
os << " Insert: " << i.getData() << std::endl;
return os;
}

197
lib/engine/rs_insert.h Normal file
View File

@@ -0,0 +1,197 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_INSERT_H
#define RS_INSERT_H
#include "rs_entitycontainer.h"
class RS_BlockList;
/**
* Holds the data that defines an insert.
*/
struct RS_InsertData {
/**
* Default constructor.
*/
RS_InsertData() = default;
~RS_InsertData() = default;
/**
* @param name The name of the block used as an identifier.
* @param insertionPoint Insertion point of the block.
* @param scaleFactor Scale factor in x / y.
* @param angle Rotation angle.
* @param cols Number of cols if we insert a whole array.
* @param rows Number of rows if we insert a whole array.
* @param spacing Spacing between rows and cols.
* @param blockSource Source for the block to insert if other than parent.
* Normally blocks are requested from the entity's parent but the
* block can also come from another resource. RS_Text uses that
* to share the blocks (letters) from a font.
* @param updateMode RS2::Update will update the insert entity instantly
* RS2::NoUpdate will not update the insert. You can update
* it later manually using the update() method. This is
* often the case since you might want to adjust attributes
* after creating an insert entity.
*/
RS_InsertData(const QString& name,
RS_Vector insertionPoint,
RS_Vector scaleFactor,
double angle,
int cols, int rows, RS_Vector spacing,
RS_BlockList* blockSource = NULL,
RS2::UpdateMode updateMode = RS2::Update);
QString name;
RS_Vector insertionPoint;
RS_Vector scaleFactor;
double angle;
int cols, rows;
RS_Vector spacing;
RS_BlockList* blockSource;
RS2::UpdateMode updateMode;
};
std::ostream& operator << (std::ostream& os, const RS_InsertData& d);
/**
* An insert inserts a block into the drawing at a certain location
* with certain attributes (angle, scale, ...).
* Inserts don't really contain other entities internally. They just
* refer to a block. However, to the outside world they act exactly
* like EntityContainer.
*
* @author Andrew Mustun
*/
class RS_Insert : public RS_EntityContainer {
public:
RS_Insert(RS_EntityContainer* parent,
const RS_InsertData& d);
virtual ~RS_Insert() = default;
virtual RS_Entity* clone() const;
/** @return RS2::EntityInsert */
virtual RS2::EntityType rtti() const {
return RS2::EntityInsert;
}
/** @return Copy of data that defines the insert. **/
RS_InsertData getData() const {
return data;
}
/**
* Reimplementation of reparent. Invalidates block cache pointer.
*/
virtual void reparent(RS_EntityContainer* parent) {
RS_Entity::reparent(parent);
block = NULL;
}
RS_Block* getBlockForInsert() const;
virtual void update();
QString getName() const {
return data.name;
}
void setName(const QString& newName) {
data.name = newName;
update();
}
RS_Vector getInsertionPoint() const {
return data.insertionPoint;
}
void setInsertionPoint(const RS_Vector& i) {
data.insertionPoint = i;
}
RS_Vector getScale() const {
return data.scaleFactor;
}
void setScale(const RS_Vector& s) {
data.scaleFactor = s;
}
double getAngle() const {
return data.angle;
}
void setAngle(double a) {
data.angle = a;
}
int getCols() const {
return data.cols;
}
void setCols(int c) {
data.cols = c;
}
int getRows() const {
return data.rows;
}
void setRows(int r) {
data.rows = r;
}
RS_Vector getSpacing() const {
return data.spacing;
}
void setSpacing(const RS_Vector& s) {
data.spacing = s;
}
virtual bool isVisible() const;
virtual RS_VectorSolutions getRefPoints() const;
virtual RS_Vector getMiddlePoint(void) const{
return RS_Vector(false);
}
virtual RS_Vector getNearestRef(const RS_Vector& coord,
double* dist = nullptr) const;
virtual void move(const RS_Vector& offset);
virtual void rotate(const RS_Vector& center, const double& angle);
virtual void rotate(const RS_Vector& center, const RS_Vector& angleVector);
virtual void scale(const RS_Vector& center, const RS_Vector& factor);
virtual void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2);
friend std::ostream& operator << (std::ostream& os, const RS_Insert& i);
protected:
RS_InsertData data;
mutable RS_Block* block;
};
#endif

233
lib/engine/rs_layer.cpp Normal file
View File

@@ -0,0 +1,233 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 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 <QString>
#include "rs_layer.h"
RS_LayerData::RS_LayerData(const QString& name,
const RS_Pen& pen,
bool frozen,
bool locked):
name(name)
,pen(pen)
,frozen(frozen)
,locked(locked)
{
}
/**
* Constructor.
*/
RS_Layer::RS_Layer(const QString& name):
data(name, RS_Pen(Qt::black, RS2::Width00,RS2::SolidLine), false, false)
{
}
RS_Layer* RS_Layer::clone() const{
return new RS_Layer(*this);
}
/** sets a new name for this layer. */
void RS_Layer::setName(const QString& name) {
data.name = name;
}
/** @return the name of this layer. */
QString RS_Layer::getName() const {
return data.name;
}
/** sets the default pen for this layer. */
void RS_Layer::setPen(const RS_Pen& pen) {
data.pen = pen;
}
/** @return default pen for this layer. */
RS_Pen RS_Layer::getPen() const {
return data.pen;
}
/**
* @retval true if this layer is frozen (invisible)
* @retval false if this layer isn't frozen (visible)
*/
bool RS_Layer::isFrozen() const {
return data.frozen;
//getFlag(RS2::FlagFrozen);
}
/**
* @retval true the layer has been converted already
* @retval false the layer still needs to be converted
*/
bool RS_Layer::isConverted() const {
return data.converted;
}
/**
* Sets the converted flag
*/
void RS_Layer::setConverted(bool c) {
data.converted = c;
}
/**
* Toggles the visibility of this layer.
* Freezes the layer if it's not frozen, thaws the layer otherwise
*/
void RS_Layer::toggle() {
//toggleFlag(RS2::FlagFrozen);
data.frozen = !data.frozen;
}
/**
* (De-)freezes this layer.
*
* @param freeze true: freeze, false: defreeze
*/
void RS_Layer::freeze(bool freeze) {
data.frozen = freeze;
}
/**
* Toggles the lock of this layer.
*/
void RS_Layer::toggleLock() {
//toggleFlag(RS2::FlagFrozen);
data.locked = !data.locked;
}
/**
* Toggles printing of this layer on / off.
*/
void RS_Layer::togglePrint() {
data.print = !data.print;
}
/**
* Toggles construction attribute of this layer on / off.
*/
void RS_Layer::toggleConstruction() {
data.construction = !data.construction;
}
/**
* Locks/Unlocks this layer.
*
* @param l true: lock, false: unlock
*/
void RS_Layer::lock(bool l) {
data.locked = l;
}
/**
* return the LOCK state of the Layer
*/
bool RS_Layer::isLocked() const{
return data.locked;
}
/**
* set visibility of layer in layer list
*
* @param l true: visible, false: invisible
*/
void RS_Layer::visibleInLayerList(bool l) {
data.visibleInLayerList = l;
}
/**
* return the visibility of the Layer in layer list
*/
bool RS_Layer::isVisibleInLayerList() const{
return data.visibleInLayerList;
}
/**
* set selection state of the layer in layer list
*
* @param val true: selected, false: deselected
*/
void RS_Layer::selectedInLayerList(bool val) {
data.selectedInLayerList = val;
}
/**
* return selection state of the layer in layer list
*/
bool RS_Layer::isSelectedInLayerList() const {
return data.selectedInLayerList;
}
/**
* set the PRINT state of the Layer
*
* @param print true: print layer, false: don't print layer
*/
bool RS_Layer::setPrint( const bool print) {
return data.print = print;
}
/**
* return the PRINT state of the Layer
*/
bool RS_Layer::isPrint() const{
return data.print;
}
/**
* whether the layer is a construction layer
* The construction layer property is stored
* in extended data in the DXF layer table
*/
bool RS_Layer::isConstruction() const{
return data.construction;
}
/**
* set the construction attribute for the layer
*
* @param construction true: infinite lines, false: normal layer
*/
bool RS_Layer::setConstruction( const bool construction){
data.construction = construction;
return construction;
}
/**
* Dumps the layers data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Layer& l) {
os << " name: " << l.getName().toLatin1().data()
<< " pen: " << l.getPen()
<< " frozen: " << (int)l.isFrozen()
<< " address: " << &l
<< std::endl;
return os;
}

206
lib/engine/rs_layer.h Normal file
View File

@@ -0,0 +1,206 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 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!
**
**********************************************************************/
#ifndef RS_LAYER_H
#define RS_LAYER_H
#ifdef __hpux
#include <sys/_size_t.h>
#endif
#include <iosfwd>
#include "rs_pen.h"
class QString;
/**
* Holds the data that defines a layer.
*/
struct RS_LayerData {
RS_LayerData() = default;
RS_LayerData(const QString& name,
const RS_Pen& pen,
bool frozen,
bool locked);
QString name; //!< Layer name
RS_Pen pen; //!< default pen for this layer
bool frozen {false}; //!< Frozen flag
bool locked {false}; //!< Locked flag
bool print {true}; //!< Print flag
bool converted {false}; //!< Converted flag (CAM)
bool construction {false}; //!< a construction layer has entities of infinite length
//!< and will never be printed out
bool visibleInLayerList {true}; //!< visible in layer list
bool selectedInLayerList {false}; //!< selected in layer list
};
/**
* Class for representing a layer
*
* @author Andrew Mustun
*/
class RS_Layer {
public:
explicit RS_Layer(const QString& name);
//RS_Layer(const char* name);
RS_Layer* clone() const;
/** sets a new name for this layer. */
void setName(const QString& name);
/** @return the name of this layer. */
QString getName() const;
/** sets the default pen for this layer. */
void setPen(const RS_Pen& pen);
/** @return default pen for this layer. */
RS_Pen getPen() const;
/**
* @retval true if this layer is frozen (invisible)
* @retval false if this layer isn't frozen (visible)
*/
bool isFrozen() const;
/**
* @retval true the layer has been converted already
* @retval false the layer still needs to be converted
*/
bool isConverted() const;
/**
* Sets the converted flag
*/
void setConverted(bool c);
/**
* Toggles the visibility of this layer.
* Freezes the layer if it's not frozen, thaws the layer otherwise
*/
void toggle();
/**
* (De-)freezes this layer.
*
* @param freeze true: freeze, false: defreeze
*/
void freeze(bool freeze);
/**
* Toggles the lock of this layer.
*/
void toggleLock();
/**
* Toggles printing of this layer on / off.
*/
void togglePrint();
/**
* Toggles construction attribute of this layer on / off.
*/
void toggleConstruction();
/**
* Locks/Unlocks this layer.
*
* @param l true: lock, false: unlock
*/
void lock(bool l);
/**
* return the LOCK state of the Layer
*/
bool isLocked() const;
/**
* set visibility of layer in layer list
*
* @param l true: visible, false: invisible
*/
void visibleInLayerList(bool l);
/**
* return the visibility of the Layer in layer list
*/
bool isVisibleInLayerList() const;
/**
* set selection state of the layer in layer list
*
* @param val true: selected, false: deselected
*/
void selectedInLayerList(bool val);
/**
* return selection state of the layer in layer list
*/
bool isSelectedInLayerList() const;
/**
* set the PRINT state of the Layer
*
* @param print true: print layer, false: don't print layer
*/
bool setPrint( const bool print);
/**
* return the PRINT state of the Layer
*/
bool isPrint() const;
/**
* whether the layer is a construction layer
* The construction layer property is stored
* in extended data in the DXF layer table
*/
bool isConstruction() const;
/**
* set the construction attribute for the layer
*
* @param construction true: infinite lines, false: normal layer
*/
bool setConstruction( const bool construction);
friend std::ostream& operator << (std::ostream& os, const RS_Layer& l);
private:
//! Layer data
RS_LayerData data;
};
#endif

496
lib/engine/rs_layerlist.cpp Normal file
View File

@@ -0,0 +1,496 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 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 "rs_debug.h"
#include "rs_layerlist.h"
#include "rs_layer.h"
#include "rs_layerlistlistener.h"
/**
* Default constructor.
*/
RS_LayerList::RS_LayerList() {
activeLayer = NULL;
setModified(false);
}
/**
* Removes all layers in the layerlist.
*/
void RS_LayerList::clear() {
layers.clear();
setModified(true);
}
QList<RS_Layer*>::iterator RS_LayerList::begin()
{
return layers.begin();
}
QList<RS_Layer*>::iterator RS_LayerList::end()
{
return layers.end();
}
QList<RS_Layer*>::const_iterator RS_LayerList::begin()const
{
return layers.begin();
}
QList<RS_Layer*>::const_iterator RS_LayerList::end()const
{
return layers.end();
}
/**
* Activates the given layer.
*
* @param notify Notify listeners.
*/
void RS_LayerList::activate(const QString& name, bool notify) {
RS_DEBUG->print("RS_LayerList::activate: %s, notify: %d begin",
name.toLatin1().data(), notify);
activate(find(name), notify);
/*
if (activeLayer==NULL) {
RS_DEBUG->print("activeLayer is NULL");
} else {
RS_DEBUG->print("activeLayer is %s", activeLayer->getName().latin1());
}
*/
RS_DEBUG->print("RS_LayerList::activate: %s end", name.toLatin1().data());
}
/**
* Activates the given layer.
*
* @param notify Notify listeners.
*/
void RS_LayerList::activate(RS_Layer* layer, bool notify) {
RS_DEBUG->print("RS_LayerList::activate notify: %d begin", notify);
/*if (layer) {
RS_DEBUG->print("RS_LayerList::activate: %s",
layer->getName().latin1());
} else {
RS_DEBUG->print("RS_LayerList::activate: NULL");
}*/
activeLayer = layer;
if (notify) {
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerActivated(activeLayer);
RS_DEBUG->print("RS_LayerList::activate listener notified");
}
}
RS_DEBUG->print("RS_LayerList::activate end");
}
/**
* @brief sort by layer names
*/
void RS_LayerList::sort()
{
std::stable_sort(layers.begin(), layers.end(), [](const RS_Layer* l0, const RS_Layer* l1 )->bool{
return l0->getName() < l1->getName();
});
}
/**
* Adds a layer to the layer list.
* If there is already a layer with the same name, no layer is
* added. In that case the layer passed to the method will be deleted!
* If no layer was active so far, the new layer becomes the active one.
*
* Listeners are notified.
*/
void RS_LayerList::add(RS_Layer* layer) {
RS_DEBUG->print("RS_LayerList::addLayer()");
if (layer==NULL) {
return;
}
// check if layer already exists:
RS_Layer* l = find(layer->getName());
if (l==NULL) {
layers.append(layer);
this->sort();
// notify listeners
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerAdded(layer);
}
setModified(true);
// if there was no active layer so far, activate this one.
if (activeLayer==NULL) {
activate(layer);
}
} else {
// if there was no active layer so far, activate this one.
if (activeLayer==NULL) {
activate(l);
}
l->freeze( layer->isFrozen());
l->lock( layer->isLocked());
l->setPrint( layer->isPrint());
l->setConverted( layer->isConverted());
l->setConstruction( layer->isConstruction());
l->visibleInLayerList( layer->isVisibleInLayerList());
l->setPen(layer->getPen());
delete layer;
layer = NULL;
}
}
/**
* Removes a layer from the list.
* Listeners are notified after the layer was removed from
* the list but before it gets deleted.
*/
void RS_LayerList::remove(RS_Layer* layer) {
RS_DEBUG->print("RS_LayerList::removeLayer()");
if (layer==NULL) {
return;
}
// here the layer is removed from the list but not deleted
layers.removeOne(layer);
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerRemoved(layer);
}
setModified(true);
// activate an other layer if necessary:
if (activeLayer==layer) {
activate(layers.first());
}
// now it's save to delete the layer
delete layer;
}
/**
* Changes a layer's attributes. The attributes of layer 'layer'
* are copied from layer 'source'.
* Listeners are notified.
*/
void RS_LayerList::edit(RS_Layer* layer, const RS_Layer& source) {
if (layer==NULL) {
return;
}
*layer = source;
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerEdited(layer);
}
setModified(true);
}
/**
* @return Pointer to the layer with the given name or
* \p NULL if no such layer was found.
*/
RS_Layer* RS_LayerList::find(const QString& name) {
//RS_DEBUG->print("RS_LayerList::find begin");
RS_Layer* ret = NULL;
for (int i=0; i<layers.size(); ++i) {
RS_Layer* l = layers.at(i);
if (l->getName()==name) {
ret = l;
break;
}
}
//RS_DEBUG->print("RS_LayerList::find end");
return ret;
}
/**
* @return Index of the given layer in the layer list or -1 if the layer
* was not found.
*/
int RS_LayerList::getIndex(const QString& name) {
//RS_DEBUG->print("RS_LayerList::find begin");
int ret = -1;
for (int i=0; i<layers.size(); i++) {
RS_Layer* l = layers.at(i);
if (l->getName()==name) {
ret = i;
break;
}
}
//RS_DEBUG->print("RS_LayerList::find end");
return ret;
}
/**
* @return Index of the given layer in the layer list or -1 if the layer
* was not found.
*/
int RS_LayerList::getIndex(RS_Layer* layer) {
//RS_DEBUG->print("RS_LayerList::find begin");
return layers.indexOf(layer);
}
/**
* Switches on / off the given layer.
* Listeners are notified.
*/
void RS_LayerList::toggle(const QString& name) {
toggle(find(name));
}
/**
* Switches on / off the given layer.
* Listeners are notified.
*/
void RS_LayerList::toggle(RS_Layer* layer) {
if (!layer) {
RS_DEBUG->print(RS_Debug::D_ERROR, "RS_LayerList::toggle: nullptr layer");
return;
}
// set flags
layer->toggle();
setModified(true);
// Notify listeners:
for (auto *i : layerListListeners) {
if (!i) {
RS_DEBUG->print(RS_Debug::D_WARNING, "RS_LayerList::toggle: nullptr layer listener");
continue;
}
RS_LayerListListener *l = (RS_LayerListListener *)i;
l->layerToggled(layer);
}
}
/**
* Locks or unlocks the given layer.
* Listeners are notified.
*/
void RS_LayerList::toggleLock(RS_Layer* layer) {
if (layer==NULL) {
return;
}
layer->toggleLock();
setModified(true);
// Notify listeners:
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerToggledLock(layer);
}
}
/**
* Switch printing for the given layer on / off.
* Listeners are notified.
*/
void RS_LayerList::togglePrint(RS_Layer* layer) {
if (layer==NULL) {
return;
}
layer->togglePrint();
setModified(true);
// Notify listeners:
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerToggledPrint(layer);
}
}
/**
* Switch construction attribute for the given layer on / off.
* Listeners are notified.
*/
void RS_LayerList::toggleConstruction(RS_Layer* layer) {
if (layer==NULL) {
return;
}
layer->toggleConstruction();
setModified(true);
// Notify listeners:
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerToggledConstruction(layer);
}
}
/**
* Freezes or defreezes all layers.
*
* @param freeze true: freeze, false: defreeze
*/
void RS_LayerList::freezeAll(bool freeze) {
for (unsigned l=0; l<count(); l++) {
if (at(l)->isVisibleInLayerList()) {
at(l)->freeze(freeze);
}
}
setModified(true);
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerToggled(NULL);
}
}
/**
* Locks or unlocks all layers.
*
* @param lock true: lock, false: unlock
*/
void RS_LayerList::lockAll(bool lock) {
for (unsigned l=0; l<count(); l++) {
if (at(l)->isVisibleInLayerList()) {
at(l)->lock(lock);
}
}
setModified(true);
for (int i=0; i<layerListListeners.size(); ++i) {
RS_LayerListListener* l = layerListListeners.at(i);
l->layerToggled(NULL);
}
}
/**
* adds a LayerListListener to the list of listeners. Listeners
* are notified when the layer list changes.
*
* Typical listeners are: layer list widgets, pen toolbar, graphic view
*/
void RS_LayerList::addListener(RS_LayerListListener* listener) {
layerListListeners.append(listener);
}
/**
* removes a LayerListListener from the list of listeners.
*/
void RS_LayerList::removeListener(RS_LayerListListener* listener) {
layerListListeners.removeOne(listener);
}
/**
* Dumps the layers to stdout.
*/
std::ostream& operator << (std::ostream& os, RS_LayerList& l) {
os << "Layerlist: \n";
for (unsigned i=0; i<l.count(); i++) {
os << *(l.at(i)) << "\n";
}
return os;
}
/**
* Sets the layer lists modified status to 'm'.
* Listeners are notified.
*/
void RS_LayerList::setModified(bool m) {
modified = m;
// Notify listeners
for (auto l: layerListListeners) {
l->layerListModified(m);
}
}

141
lib/engine/rs_layerlist.h Normal file
View File

@@ -0,0 +1,141 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 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!
**
**********************************************************************/
#ifndef RS_LAYERLIST_H
#define RS_LAYERLIST_H
#include <QList>
#include "rs_layer.h"
class RS_LayerListListener;
class QG_LayerWidget;
/**
* A list of layers.
*
* @author Andrew Mustun
*/
class RS_LayerList {
public:
RS_LayerList();
virtual ~RS_LayerList() = default;
void clear();
/**
* @return Number of layers in the list.
*/
unsigned int count() const {
return layers.count();
}
/**
* @return Layer at given position or NULL if i is out of range.
*/
RS_Layer* at(unsigned int i) {
return layers.at(i);
}
QList<RS_Layer*>::iterator begin();
QList<RS_Layer*>::iterator end();
QList<RS_Layer*>::const_iterator begin()const;
QList<RS_Layer*>::const_iterator end()const;
void activate(const QString& name, bool notify = false);
void activate(RS_Layer* layer, bool notify = false);
//! @return The active layer of NULL if no layer is activated.
RS_Layer* getActive() {
return activeLayer;
}
virtual void add(RS_Layer* layer);
virtual void remove(RS_Layer* layer);
virtual void edit(RS_Layer* layer, const RS_Layer& source);
RS_Layer* find(const QString& name);
int getIndex(const QString& name);
int getIndex(RS_Layer* layer);
void toggle(const QString& name);
void toggle(RS_Layer* layer);
void toggleLock(RS_Layer* layer);
void togglePrint(RS_Layer* layer);
void toggleConstruction(RS_Layer* layer);
void freezeAll(bool freeze);
void lockAll(bool lock);
//! sets the layerWidget pointer in RS_LayerListClass
void setLayerWitget(QG_LayerWidget * lw) {
layerWidget=lw;
}
//! @return the layerWidget pointer inside the RS_LayerListClass
QG_LayerWidget* getLayerWitget() {
return layerWidget;
}
//! @return First layer of the list.
//RS_Layer* firstLayer() {
// return layers.first();
//}
/** @return Next layer from the list after
* calling firstLayer() or nextLayer().
*/
//RS_Layer* nextLayer() {
// return layers.next();
//}
void addListener(RS_LayerListListener* listener);
void removeListener(RS_LayerListListener* listener);
/**
* Sets the layer lists modified status to 'm'.
*/
void setModified(bool m);
/**
* @retval true The layer list has been modified.
* @retval false The layer list has not been modified.
*/
virtual bool isModified() const {
return modified;
}
/**
* @brief sort by layer names
*/
void sort();
friend std::ostream& operator << (std::ostream& os, RS_LayerList& l);
private:
//! layers in the graphic
QList<RS_Layer*> layers;
//! List of registered LayerListListeners
QList<RS_LayerListListener*> layerListListeners;
QG_LayerWidget* layerWidget;
//! Currently active layer
RS_Layer* activeLayer;
/** Flag set if the layer list was modified and not yet saved. */
bool modified;
};
#endif

View File

@@ -0,0 +1,90 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015 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!
**
**********************************************************************/
#ifndef RS_LAYERLISTLISTENER_H
#define RS_LAYERLISTLISTENER_H
#include "rs_layer.h"
/**
* This class is an interface for classes that are interested in
* knowing about changes in the layer list.
*/
class RS_LayerListListener {
public:
RS_LayerListListener() {}
virtual ~RS_LayerListListener() {}
/**
* Called when the active layer changes.
*/
virtual void layerActivated(RS_Layer*) {}
/**
* Called when a new layer is added to the list.
*/
virtual void layerAdded(RS_Layer*) {}
/**
* Called when a layer is removed from the list.
*/
virtual void layerRemoved(RS_Layer*) {}
/**
* Called when a layer's attributes are modified.
*/
virtual void layerEdited(RS_Layer*) {}
/**
* Called when a layer's visibility is toggled.
*/
virtual void layerToggled(RS_Layer*) {}
/**
* Called when a layer's lock attribute is toggled.
*/
virtual void layerToggledLock(RS_Layer*) {}
/**
* Called when a layer's printing attribute is toggled.
*/
virtual void layerToggledPrint(RS_Layer*) {}
/**
* Called when a layer's construction attribute is toggled.
*/
virtual void layerToggledConstruction(RS_Layer*) {}
/**
* Called when layer list is modified.
*/
virtual void layerListModified(bool) {}
}
;
#endif

214
lib/engine/rs_leader.cpp Normal file
View File

@@ -0,0 +1,214 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_leader.h"
#include "rs_debug.h"
#include "rs_line.h"
#include "rs_solid.h"
/**
* Constructor.
*/
RS_Leader::RS_Leader(RS_EntityContainer* parent)
:RS_EntityContainer(parent)
,empty(true)
{
}
/**
* Constructor.
* @param d Leader data
*/
RS_Leader::RS_Leader(RS_EntityContainer* parent,
const RS_LeaderData& d)
:RS_EntityContainer(parent), data(d) {
empty = true;
}
RS_Entity* RS_Leader::clone() const{
RS_Leader* p = new RS_Leader(*this);
p->setOwner(isOwner());
p->initId();
p->detach();
return p;
}
/**
* Implementation of update. Updates the arrow.
*/
void RS_Leader::update() {
// find and delete arrow:
for(auto e: entities){
if (e->rtti()==RS2::EntitySolid) {
removeEntity(e);
break;
}
}
if (isUndone()) {
return;
}
RS_Entity* fe = firstEntity();
if (fe && fe->isAtomic()) {
RS_Vector p1 = ((RS_AtomicEntity*)fe)->getStartpoint();
RS_Vector p2 = ((RS_AtomicEntity*)fe)->getEndpoint();
// first entity must be the line which gets the arrow:
if (hasArrowHead()) {
RS_Solid* s = new RS_Solid(this, RS_SolidData());
s->shapeArrow(p1,
p2.angleTo(p1),
getGraphicVariableDouble("$DIMASZ", 2.5)* getGraphicVariableDouble("$DIMSCALE", 1.0));
s->setPen(RS_Pen(RS2::FlagInvalid));
s->setLayer(nullptr);
RS_EntityContainer::addEntity(s);
}
}
calculateBorders();
}
/**
* Adds a vertex from the endpoint of the last element or
* sets the startpoint to the point 'v'.
*
* The very first vertex added is the starting point.
*
* @param v vertex coordinate
*
* @return Pointer to the entity that was added or nullptr if this
* was the first vertex added.
*/
RS_Entity* RS_Leader::addVertex(const RS_Vector& v) {
RS_Entity* entity{nullptr};
static RS_Vector last = RS_Vector{false};
if (empty) {
last = v;
empty = false;
} else {
// add line to the leader:
entity = new RS_Line{this, {last, v}};
entity->setPen(RS_Pen(RS2::FlagInvalid));
entity->setLayer(nullptr);
RS_EntityContainer::addEntity(entity);
if (count()==1 && hasArrowHead()) {
update();
}
last = v;
}
return entity;
}
/**
* Reimplementation of the addEntity method for a normal container.
* This reimplementation deletes the given entity!
*
* To add entities use addVertex() instead.
*/
void RS_Leader::addEntity(RS_Entity* entity) {
RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Leader::addEntity:"
" should never be called");
if (!entity) return;
delete entity;
}
void RS_Leader::move(const RS_Vector& offset) {
RS_EntityContainer::move(offset);
update();
}
void RS_Leader::rotate(const RS_Vector& center, const double& angle) {
RS_EntityContainer::rotate(center, angle);
update();
}
void RS_Leader::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_EntityContainer::rotate(center, angleVector);
update();
}
void RS_Leader::scale(const RS_Vector& center, const RS_Vector& factor) {
RS_EntityContainer::scale(center, factor);
update();
}
void RS_Leader::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
RS_EntityContainer::mirror(axisPoint1, axisPoint2);
update();
}
void RS_Leader::stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) {
RS_EntityContainer::stretch(firstCorner, secondCorner, offset);
update();
}
/**
* Dumps the leader's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Leader& l) {
os << " Leader: " << l.getData() << " {\n";
os << (RS_EntityContainer&)l;
os << "\n}\n";
return os;
}
std::ostream& operator << (std::ostream& os,
const RS_LeaderData& /*ld*/) {
os << "(Leader)";
return os;
}

109
lib/engine/rs_leader.h Normal file
View File

@@ -0,0 +1,109 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_LEADER_H
#define RS_LEADER_H
#include "rs_entity.h"
#include "rs_entitycontainer.h"
/**
* Holds the data that defines a leader.
*/
class RS_LeaderData {
public:
RS_LeaderData() = default;
RS_LeaderData(bool arrowHeadFlag) {
arrowHead = arrowHeadFlag;
}
friend std::ostream& operator << (std::ostream& os,
const RS_LeaderData& /*ld*/);
/** true: leader has an arrow head. false: no arrow. */
bool arrowHead;
};
/**
* Class for a leader entity (kind of a polyline arrow).
*
* @author Andrew Mustun
*/
class RS_Leader : public RS_EntityContainer {
public:
RS_Leader(RS_EntityContainer* parent=NULL);
RS_Leader(RS_EntityContainer* parent,
const RS_LeaderData& d);
RS_Entity* clone() const override;
/** @return RS2::EntityDimLeader */
RS2::EntityType rtti() const override{
return RS2::EntityDimLeader;
}
void update() override;
/** @return Copy of data that defines the leader. */
RS_LeaderData getData() const {
return data;
}
/** @return true: if this leader has an arrow at the beginning. */
bool hasArrowHead() {
return data.arrowHead;
}
RS_Entity* addVertex(const RS_Vector& v);
void addEntity(RS_Entity* entity) override;
// double getLength() const {
// return -1.0;
// }
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Leader& l);
protected:
RS_LeaderData data;
bool empty;
};
#endif

789
lib/engine/rs_line.cpp Normal file
View File

@@ -0,0 +1,789 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2015-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 "rs_line.h"
#include "rs_debug.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
#include "rs_graphic.h"
#include "rs_linetypepattern.h"
#include "rs_information.h"
#include "lc_quadratic.h"
#include "rs_painterqt.h"
#include "rs_circle.h"
#include "lc_rect.h"
#ifdef EMU_C99
#include "emu_c99.h"
#endif
std::ostream& operator << (std::ostream& os, const RS_LineData& ld) {
os << "RS_LINE: ((" << ld.startpoint <<
")(" << ld.endpoint <<
"))";
return os;
}
/**
* Constructor.
*/
RS_Line::RS_Line(RS_EntityContainer* parent,
const RS_LineData& d)
:RS_AtomicEntity(parent), data(d) {
calculateBorders();
}
////construct a line from two endpoints
RS_Line::RS_Line(RS_EntityContainer* parent, const RS_Vector& pStart, const RS_Vector& pEnd)
:RS_AtomicEntity(parent), data({pStart,pEnd}) {
calculateBorders();
}
////construct a line from two endpoints, to be used for construction
RS_Line::RS_Line(const RS_Vector& pStart, const RS_Vector& pEnd)
:RS_AtomicEntity(nullptr), data({pStart,pEnd}) {
calculateBorders();
}
RS_Entity* RS_Line::clone() const {
RS_Line* l = new RS_Line(*this);
l->initId();
return l;
}
void RS_Line::calculateBorders() {
minV = RS_Vector::minimum(data.startpoint, data.endpoint);
maxV = RS_Vector::maximum(data.startpoint, data.endpoint);
}
RS_VectorSolutions RS_Line::getRefPoints() const
{
return RS_VectorSolutions({data.startpoint, data.endpoint});
}
RS_Vector RS_Line::getNearestEndpoint(const RS_Vector& coord,
double* dist)const {
double dist1((data.startpoint-coord).squared());
double dist2((data.endpoint-coord).squared());
if (dist2<dist1) {
if (dist) {
*dist = sqrt(dist2);
}
return data.endpoint;
} else {
if (dist) {
*dist = sqrt(dist1);
}
return data.startpoint;
}
}
/**
* This is similar to getNearestPointOnEntity, but only returns the value of
* the position of the projection. The value may be negative, or greater than
* the length of the line since there are no bounds checking. The absolute
* value of the return value represents a physical distance from the
* startpoint.
*
* @param coord The point which will be projected onto the line.
*/
double RS_Line::getProjectionValueAlongLine(const RS_Vector& coord) const
{
RS_Vector direction {data.endpoint - data.startpoint};
RS_Vector vpc {coord - data.startpoint};
double direction_magnitude {direction.magnitude()};
double v = 0.0;
if(direction_magnitude > RS_TOLERANCE2) {
//find projection on line
v = RS_Vector::dotP(vpc, direction) / direction_magnitude;
}
return v;
}
RS_Vector RS_Line::getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity,
double* dist,
RS_Entity** entity) const
{
if (entity) {
*entity = const_cast<RS_Line*>(this);
}
RS_Vector direction {data.endpoint - data.startpoint};
RS_Vector vpc {coord - data.startpoint};
double a {direction.squared()};
if( a < RS_TOLERANCE2) {
//line too short
vpc = getMiddlePoint();
}
else {
//find projection on line
const double t {RS_Vector::dotP( vpc, direction) / a};
if( !isConstruction()
&& onEntity
&& ( t <= -RS_TOLERANCE
|| t >= 1. + RS_TOLERANCE ) ) {
//projection point not within range, find the nearest endpoint
return getNearestEndpoint( coord, dist);
}
vpc = data.startpoint + direction * t;
}
if (dist) {
*dist = vpc.distanceTo( coord);
}
return vpc;
}
/*
RS_Vector RS_Line::getPointOnEntityAlongLine(const RS_Vector& coord,const double angle,
bool onEntity,
double* dist,
RS_Entity** entity) const
{
if (entity)
{
*entity = const_cast<RS_Line*>(this);
}
RS_Vector direction {data.endpoint - data.startpoint};
RS_Vector vpc {coord - data.startpoint};
double a {direction.squared()};
if( a < RS_TOLERANCE2)
{
//line too short
vpc = getMiddlePoint();
}
else
{
//find projection on line
const double t {RS_Vector::dotP( vpc, direction) / a};
if( !isConstruction() && onEntity && ( t <= -RS_TOLERANCE || t >= 1. + RS_TOLERANCE ) )
{
//projection point not within range, find the nearest endpoint
return getNearestEndpoint( coord, dist);
}
vpc = data.startpoint + direction * t;
}
if (dist)
{
*dist = vpc.distanceTo( coord);
}
return vpc;
}
*/
RS_Vector RS_Line::getMiddlePoint()const
{
return (getStartpoint() + getEndpoint())*0.5;
}
/** @return the nearest of equidistant middle points of the line. */
RS_Vector RS_Line::getNearestMiddle(const RS_Vector& coord,
double* dist,
int middlePoints
)const {
// RS_DEBUG->print("RS_Line::getNearestMiddle(): begin\n");
RS_Vector dvp(getEndpoint() - getStartpoint());
double l=dvp.magnitude();
if( l<= RS_TOLERANCE) {
//line too short
return const_cast<RS_Line*>(this)->getNearestCenter(coord, dist);
}
RS_Vector vp0(getNearestPointOnEntity(coord,true,dist));
int counts=middlePoints+1;
int i( static_cast<int>(vp0.distanceTo(getStartpoint())/l*counts+0.5));
if(!i) i++; // remove end points
if(i==counts) i--;
vp0=getStartpoint() + dvp*(double(i)/double(counts));
if (dist) {
*dist=vp0.distanceTo(coord);
}
// RS_DEBUG->print("RS_Line::getNearestMiddle(): end\n");
return vp0;
}
//RS_Vector RS_Line::getNearestCenter(const RS_Vector& coord,
// double* dist) {
// RS_Vector p = (data.startpoint + data.endpoint)*0.5;
// if (dist) {
// *dist = p.distanceTo(coord);
// }
// return p;
//}
RS_Vector RS_Line::getNearestDist(double distance,
const RS_Vector& coord,
double* dist) const{
RS_Vector dv = RS_Vector::polar(distance, getAngle1());
RS_Vector ret;
//if(coord.distanceTo(getStartpoint()) < coord.distanceTo(getEndpoint())) {
if( (coord-getStartpoint()).squared()< (coord-getEndpoint()).squared() ) {
ret = getStartpoint() + dv;
}else{
ret = getEndpoint() - dv;
}
if (dist)
*dist=coord.distanceTo(ret);
return ret;
}
RS_Vector RS_Line::getNearestDist(double distance,
bool startp) const{
double a1 = getAngle1();
RS_Vector dv = RS_Vector::polar(distance, a1);
RS_Vector ret;
if (startp) {
ret = data.startpoint + dv;
}
else {
ret = data.endpoint - dv;
}
return ret;
}
/*RS_Vector RS_Line::getNearestRef(const RS_Vector& coord,
double* dist) {
double d1, d2, d;
RS_Vector p;
RS_Vector p1 = getNearestEndpoint(coord, &d1);
RS_Vector p2 = getNearestMiddle(coord, &d2);
if (d1<d2) {
d = d1;
p = p1;
} else {
d = d2;
p = p2;
}
if (dist) {
*dist = d;
}
return p;
}*/
/** return the equation of the entity
for quadratic,
return a vector contains:
m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
for linear:
m0 x + m1 y + m2 =0
**/
LC_Quadratic RS_Line::getQuadratic() const
{
std::vector<double> ce(3,0.);
auto dvp=data.endpoint - data.startpoint;
RS_Vector normal(-dvp.y,dvp.x);
ce[0]=normal.x;
ce[1]=normal.y;
ce[2]= -normal.dotP(data.endpoint);
return LC_Quadratic(ce);
}
double RS_Line::areaLineIntegral() const
{
return 0.5*(data.endpoint.y - data.startpoint.y)*(data.startpoint.x + data.endpoint.x);
}
RS_Vector RS_Line::getTangentDirection(const RS_Vector& /*point*/)const{
return getEndpoint() - getStartpoint();
}
void RS_Line::moveStartpoint(const RS_Vector& pos) {
data.startpoint = pos;
calculateBorders();
}
void RS_Line::moveEndpoint(const RS_Vector& pos) {
data.endpoint = pos;
calculateBorders();
}
RS_Vector RS_Line::prepareTrim(const RS_Vector& trimCoord,
const RS_VectorSolutions& trimSol) {
//prepare trimming for multiple intersections
if ( ! trimSol.hasValid()) return(RS_Vector(false));
if ( trimSol.getNumber() == 1 ) return(trimSol.get(0));
auto vp0=trimSol.getClosest(trimCoord, nullptr, 0);
double dr2=trimCoord.squaredTo(vp0);
//the trim point found is closer to mouse location (trimCoord) than both end points, return this trim point
if(dr2 < trimCoord.squaredTo(getStartpoint()) && dr2 < trimCoord.squaredTo(getEndpoint())) return vp0;
//the closer endpoint to trimCoord
RS_Vector vp1=(trimCoord.squaredTo(getStartpoint()) <= trimCoord.squaredTo(getEndpoint()))?getStartpoint():getEndpoint();
//searching for intersection in the direction of the closer end point
auto dvp1=vp1 - trimCoord;
RS_VectorSolutions sol1;
for(size_t i=0; i<trimSol.size(); i++){
auto dvp2=trimSol.at(i) - trimCoord;
if( RS_Vector::dotP(dvp1, dvp2) > RS_TOLERANCE)
sol1.push_back(trimSol.at(i));
}
//if found intersection in direction, return the closest to trimCoord from it
if(sol1.size())
return sol1.getClosest(trimCoord, nullptr, 0);
//no intersection by direction, return previously found closest intersection
return vp0;
}
RS2::Ending RS_Line::getTrimPoint(const RS_Vector& trimCoord,
const RS_Vector& trimPoint) {
RS_Vector vp1=getStartpoint() - trimCoord;
RS_Vector vp2=trimPoint - trimCoord;
if ( RS_Vector::dotP(vp1,vp2) < 0 ) {
return RS2::EndingEnd;
} else {
return RS2::EndingStart;
}
}
void RS_Line::reverse() {
std::swap(data.startpoint, data.endpoint);
}
bool RS_Line::hasEndpointsWithinWindow(const RS_Vector& firstCorner, const RS_Vector& secondCorner) {
RS_Vector vLow( std::min(firstCorner.x, secondCorner.x), std::min(firstCorner.y, secondCorner.y));
RS_Vector vHigh( std::max(firstCorner.x, secondCorner.x), std::max(firstCorner.y, secondCorner.y));
return data.startpoint.isInWindowOrdered(vLow, vHigh)
|| data.endpoint.isInWindowOrdered(vLow, vHigh);
}
/**
* this function creates offset
*@coord, position indicates the direction of offset
*@distance, distance of offset
* return true, if success, otherwise, false
*
*Author: Dongxu Li
*/
bool RS_Line::offset(const RS_Vector& coord, const double& distance) {
RS_Vector direction{getEndpoint()-getStartpoint()};
double ds(direction.magnitude());
if(ds< RS_TOLERANCE) return false;
direction /= ds;
RS_Vector vp(coord-getStartpoint());
// RS_Vector vp1(getStartpoint() + direction*(RS_Vector::dotP(direction,vp))); //projection
direction.set(-direction.y,direction.x); //rotate pi/2
if(RS_Vector::dotP(direction,vp)<0.) {
direction *= -1.;
}
direction*=distance;
move(direction);
return true;
}
bool RS_Line::isTangent(const RS_CircleData& circleData) const{
double d;
getNearestPointOnEntity(circleData.center,false,&d);
if(fabs(d-circleData.radius)<20.*RS_TOLERANCE) return true;
return false;
}
RS_Vector RS_Line::getNormalVector() const
{
RS_Vector vp=data.endpoint - data.startpoint; //direction vector
double r=vp.magnitude();
if (r< RS_TOLERANCE) return RS_Vector{false};
return RS_Vector{-vp.y,vp.x}/r;
}
std::vector<RS_Entity* > RS_Line::offsetTwoSides(const double& distance) const
{
std::vector<RS_Entity*> ret(0);
RS_Vector const& vp=getNormalVector()*distance;
ret.push_back(new RS_Line{data.startpoint+vp,data.endpoint+vp});
ret.push_back(new RS_Line{data.startpoint-vp,data.endpoint-vp});
return ret;
}
/**
* revert the direction of line
*/
void RS_Line::revertDirection(){
std::swap(data.startpoint,data.endpoint);
}
void RS_Line::move(const RS_Vector& offset) {
// RS_DEBUG->print("RS_Line::move1: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
// RS_DEBUG->print("RS_Line::move1: offset: %f/%f", offset.x, offset.y);
data.startpoint.move(offset);
data.endpoint.move(offset);
moveBorders(offset);
// RS_DEBUG->print("RS_Line::move2: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
}
void RS_Line::rotate(const double& angle) {
// RS_DEBUG->print("RS_Line::rotate");
// RS_DEBUG->print("RS_Line::rotate1: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
RS_Vector rvp(angle);
data.startpoint.rotate(rvp);
data.endpoint.rotate(rvp);
// RS_DEBUG->print("RS_Line::rotate2: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
calculateBorders();
// RS_DEBUG->print("RS_Line::rotate: OK");
}
void RS_Line::rotate(const RS_Vector& center, const double& angle) {
// RS_DEBUG->print("RS_Line::rotate");
// RS_DEBUG->print("RS_Line::rotate1: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
RS_Vector rvp(angle);
data.startpoint.rotate(center, rvp);
data.endpoint.rotate(center, rvp);
// RS_DEBUG->print("RS_Line::rotate2: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
calculateBorders();
// RS_DEBUG->print("RS_Line::rotate: OK");
}
void RS_Line::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
data.startpoint.rotate(center, angleVector);
data.endpoint.rotate(center, angleVector);
calculateBorders();
}
/*scale the line around origin (0,0)
*
*/
void RS_Line::scale(const RS_Vector& factor) {
// RS_DEBUG->print("RS_Line::scale1: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
data.startpoint.scale(factor);
data.endpoint.scale(factor);
// RS_DEBUG->print("RS_Line::scale2: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
calculateBorders();
}
void RS_Line::scale(const RS_Vector& center, const RS_Vector& factor) {
// RS_DEBUG->print("RS_Line::scale1: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
data.startpoint.scale(center, factor);
data.endpoint.scale(center, factor);
// RS_DEBUG->print("RS_Line::scale2: sp: %f/%f, ep: %f/%f",
// data.startpoint.x, data.startpoint.y,
// data.endpoint.x, data.endpoint.y);
calculateBorders();
}
void RS_Line::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
data.startpoint.mirror(axisPoint1, axisPoint2);
data.endpoint.mirror(axisPoint1, axisPoint2);
calculateBorders();
}
/**
* Stretches the given range of the entity by the given offset.
*/
void RS_Line::stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) {
RS_Vector const vLow{std::min(firstCorner.x, secondCorner.x),
std::min(firstCorner.y, secondCorner.y)
};
RS_Vector const vHigh{std::max(firstCorner.x, secondCorner.x),
std::max(firstCorner.y, secondCorner.y)
};
if (getStartpoint().isInWindowOrdered(vLow, vHigh)) {
moveStartpoint(getStartpoint() + offset);
}
if (getEndpoint().isInWindowOrdered(vLow, vHigh)) {
moveEndpoint(getEndpoint() + offset);
}
}
void RS_Line::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
if( fabs(data.startpoint.x -ref.x)<1.0e-4 &&
fabs(data.startpoint.y -ref.y)<1.0e-4 ) {
moveStartpoint(data.startpoint+offset);
}
if( fabs(data.endpoint.x -ref.x)<1.0e-4 &&
fabs(data.endpoint.y -ref.y)<1.0e-4 ) {
moveEndpoint(data.endpoint+offset);
}
}
void RS_Line::draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) {
if (! (painter && view)) {
return;
}
auto viewportRect = view->getViewRect();
RS_VectorSolutions endPoints(0);
endPoints.push_back(getStartpoint());
endPoints.push_back(getEndpoint());
RS_EntityContainer ec(nullptr);
ec.addRectangle(viewportRect.minP(), viewportRect.maxP());
// if (viewportRect.inArea(getStartpoint(), RS_TOLERANCE))
// endPoints.push_back(getStartpoint());
// if (viewportRect.inArea(getEndpoint(), RS_TOLERANCE))
// endPoints.push_back(getEndpoint());
// if (endPoints.size() < 2){
// RS_VectorSolutions vpIts;
// for(auto p: ec) {
// auto const sol=RS_Information::getIntersection(this, p, true);
// for (auto const& vp: sol) {
// if (vpIts.getClosestDistance(vp) <= RS_TOLERANCE * 10.)
// continue;
// vpIts.push_back(vp);
// }
// }
// for (auto const& vp: vpIts) {
// if (endPoints.getClosestDistance(vp) <= RS_TOLERANCE * 10.)
// continue;
// endPoints.push_back(vp);
// }
// }
// if (endPoints.size()<2) return;
if ((endPoints[0] - getStartpoint()).squared() >
(endPoints[1] - getStartpoint()).squared() )
{
std::swap(endPoints[0],endPoints[1]);
}
RS_Vector pStart{view->toGui(endPoints.at(0))};
RS_Vector pEnd{view->toGui(endPoints.at(1))};
// std::cout<<"draw line: "<<pStart<<" to "<<pEnd<<std::endl;
RS_Vector direction = pEnd-pStart;
if (isConstruction(true) && direction.squared() > RS_TOLERANCE){
//extend line on a construction layer to fill the whole view
RS_VectorSolutions vpIts;
for(auto p: ec) {
auto const sol=RS_Information::getIntersection(this, p, true);
for (auto const& vp: sol) {
if (vpIts.getClosestDistance(vp) <= RS_TOLERANCE * 10.)
continue;
vpIts.push_back(vp);
}
}
//draw construction lines up to viewport border
switch (vpIts.size()) {
case 2:
// no need to sort intersections
break;
case 3:
case 4: {
// will use the inner two points
size_t i{0};
for (size_t j = 0; j < vpIts.size(); ++j)
if (viewportRect.inArea(vpIts.at(j), RS_TOLERANCE * 10.))
std::swap(vpIts[j], vpIts[i++]);
}
break;
default:
//should not happen
return;
}
pStart=view->toGui(vpIts.get(0));
pEnd=view->toGui(vpIts.get(1));
direction=pEnd-pStart;
}
bool drawAsSelected = isSelected() && !(view->isPrinting() || view->isPrintPreview());
double length=direction.magnitude();
patternOffset -= length;
if (( !drawAsSelected && (
getPen().getLineType()==RS2::SolidLine ||
view->getDrawingMode()==RS2::ModePreview)) ) {
//if length is too small, attempt to draw the line, could be a potential bug
painter->drawLine(pStart,pEnd);
return;
}
// double styleFactor = getStyleFactor(view);
// Pattern:
const RS_LineTypePattern* pat;
if (drawAsSelected) {
// styleFactor=1.;
pat = &RS_LineTypePattern::patternSelected;
} else {
pat = view->getPattern(getPen().getLineType());
}
if (!pat) {
// patternOffset -= length;
RS_DEBUG->print(RS_Debug::D_WARNING,
"RS_Line::draw: Invalid line pattern");
painter->drawLine(pStart,pEnd);
return;
}
// patternOffset = remainder(patternOffset - length-0.5*pat->totalLength,pat->totalLength)+0.5*pat->totalLength;
if(length<=RS_TOLERANCE){
painter->drawLine(pStart,pEnd);
return; //avoid division by zero
}
direction/=length; //cos(angle), sin(angle)
// Pen to draw pattern is always solid:
RS_Pen pen = painter->getPen();
pen.setLineType(RS2::SolidLine);
painter->setPen(pen);
if (pat->num <= 0) {
RS_DEBUG->print(RS_Debug::D_WARNING,"invalid line pattern for line, draw solid line instead");
painter->drawLine(view->toGui(getStartpoint()),
view->toGui(getEndpoint()));
return;
}
// pattern segment length:
double patternSegmentLength = pat->totalLength;
// create pattern:
std::vector<RS_Vector> dp(pat->num);
std::vector<double> ds(pat->num);
double dpmm=static_cast<RS_PainterQt*>(painter)->getDpmm();
for (size_t i=0; i < pat->num; ++i) {
// ds[j]=pat->pattern[i] * styleFactor;
//fixme, styleFactor support needed
ds[i]=dpmm*pat->pattern[i];
if (fabs(ds[i]) < 1. ) ds[i] = copysign(1., ds[i]);
dp[i] = direction*fabs(ds[i]);
}
double total= remainder(patternOffset-0.5*patternSegmentLength,patternSegmentLength) -0.5*patternSegmentLength;
// double total= patternOffset-patternSegmentLength;
RS_Vector curP{pStart+direction*total};
for (int j=0; total<length; j=(j+1)%pat->num) {
// line segment (otherwise space segment)
double const t2=total+fabs(ds[j]);
RS_Vector const& p3=curP+dp[j];
if (ds[j]>0.0 && t2 > 0.0) {
// drop the whole pattern segment line, for ds[i]<0:
// trim end points of pattern segment line to line
RS_Vector const& p1 =(total > -0.5)?curP:pStart;
RS_Vector const& p2 =(t2 < length+0.5)?p3:pEnd;
painter->drawLine(p1,p2);
}
total=t2;
curP=p3;
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Line& l) {
os << " Line: " << l.getData() << "\n";
return os;
}

234
lib/engine/rs_line.h Normal file
View File

@@ -0,0 +1,234 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_LINE_H
#define RS_LINE_H
#include "rs_atomicentity.h"
class LC_Quadratic;
/**
* Holds the data that defines a line.
*/
struct RS_LineData {
RS_LineData() :
startpoint( false),
endpoint( false)
{}
RS_LineData(const RS_Vector& point1,
const RS_Vector& point2) :
startpoint( point1),
endpoint( point2)
{}
RS_Vector startpoint;
RS_Vector endpoint;
};
std::ostream& operator << (std::ostream& os, const RS_LineData& ld);
/**
* Class for a line entity.
*
* @author Andrew Mustun
*/
class RS_Line : public RS_AtomicEntity {
public:
RS_Line() = default;
RS_Line(RS_EntityContainer* parent,
const RS_LineData& d);
RS_Line(RS_EntityContainer* parent, const RS_Vector& pStart, const RS_Vector& pEnd);
RS_Line(const RS_Vector& pStart, const RS_Vector& pEnd);
RS_Entity* clone() const override;
/** @return RS2::EntityLine */
RS2::EntityType rtti() const override{
return RS2::EntityLine;
}
/** @return true */
bool isEdge() const override{
return true;
}
/** @return Copy of data that defines the line. */
RS_LineData getData() const{
return data;
}
RS_VectorSolutions getRefPoints() const override;
/** @return Start point of the entity */
RS_Vector getStartpoint() const override{
return data.startpoint;
}
/** @return End point of the entity */
RS_Vector getEndpoint() const override{
return data.endpoint;
}
/** Sets the startpoint */
void setStartpoint(RS_Vector s) {
data.startpoint = s;
calculateBorders();
}
/** Sets the endpoint */
void setEndpoint(RS_Vector e) {
data.endpoint = e;
calculateBorders();
}
/**
* @return Direction 1. The angle at which the line starts at
* the startpoint.
*/
double getDirection1() const override{
return getAngle1();
}
/**
* @return Direction 2. The angle at which the line starts at
* the endpoint.
*/
double getDirection2() const override{
return getAngle2();
}
RS_Vector getTangentDirection(const RS_Vector& point)const override;
void moveStartpoint(const RS_Vector& pos) override;
void moveEndpoint(const RS_Vector& pos) override;
RS2::Ending getTrimPoint(const RS_Vector& trimCoord,
const RS_Vector& trimPoint) override;
RS_Vector prepareTrim(const RS_Vector& trimCoord,
const RS_VectorSolutions& trimSol) override;
void reverse() override;
/** Sets the y coordinate of the startpoint */
void setStartpointY(double val) {
data.startpoint.y = val;
calculateBorders();
}
/** Sets the y coordinate of the endpoint */
void setEndpointY(double val) {
data.endpoint.y = val;
calculateBorders();
}
bool hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2) override;
/**
* @return The length of the line.
*/
double getLength() const override{
return data.startpoint.distanceTo(data.endpoint);
}
/**
* @return The angle of the line (from start to endpoint).
*/
double getAngle1() const{
return data.startpoint.angleTo(data.endpoint);
}
/**
* @return The angle of the line (from end to startpoint).
*/
double getAngle2() const{
return data.endpoint.angleTo(data.startpoint);
}
bool isTangent(const RS_CircleData& circleData) const override;
/**
* @return a perpendicular vector
*/
RS_Vector getNormalVector() const;
double getProjectionValueAlongLine(const RS_Vector& coord) const;
RS_Vector getMiddlePoint() const override;
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true,
double* dist = nullptr,
RS_Entity** entity=nullptr) const override;
//RS_Vector getPointOnEntityAlongLine(const RS_Vector& coord,const double angle,bool onEntity,double* dist,RS_Entity** entity) const;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1) const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestDist(double distance,
bool startp) const override;
/**
* implementations must revert the direction of an atomic entity
*/
void revertDirection() override;
std::vector<RS_Entity* > offsetTwoSides(const double& distance) const override;
/**
* the modify offset action
*/
bool offset(const RS_Vector& coord, const double& distance) override;
void move(const RS_Vector& offset) override;
void rotate(const double& angle);
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& factor) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
/** whether the entity's bounding box intersects with visible portion of graphic view */
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Line& l);
void calculateBorders() override;
/**
* @brief getQuadratic() returns the equation of the entity
* for quadratic,
*
* return a vector contains:
* m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
*
* for linear:
* m0 x + m1 y + m2 =0
*/
LC_Quadratic getQuadratic() const override;
/**
* @brief areaLineIntegral line integral for contour area calculation by Green's Theorem
* Contour Area =\oint x dy
* @return line integral \oint x dy along the entity
* \oint x dy = 0.5*(x0+x1)*(y1-y0)
*/
double areaLineIntegral() const override;
protected:
RS_LineData data;
};
#endif

692
lib/engine/rs_mtext.cpp Normal file
View File

@@ -0,0 +1,692 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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_font.h"
#include "rs_mtext.h"
#include "rs_fontlist.h"
#include "rs_insert.h"
#include "rs_math.h"
#include "rs_debug.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
RS_MTextData::RS_MTextData(const RS_Vector& _insertionPoint,
double _height,
double _width,
VAlign _valign,
HAlign _halign,
MTextDrawingDirection _drawingDirection,
MTextLineSpacingStyle _lineSpacingStyle,
double _lineSpacingFactor,
const QString& _text,
const QString& _style,
double _angle,
RS2::UpdateMode _updateMode):
insertionPoint(_insertionPoint)
,height(_height)
,width(_width)
,valign(_valign)
,halign(_halign)
,drawingDirection(_drawingDirection)
,lineSpacingStyle(_lineSpacingStyle)
,lineSpacingFactor(_lineSpacingFactor)
,text(_text)
,style(_style)
,angle(_angle)
,updateMode(_updateMode)
{
}
std::ostream& operator << (std::ostream& os, const RS_MTextData& td) {
os << "("
<<td.insertionPoint<<','
<<td.height<<','
<<td.width<<','
<<td.valign<<','
<<td.halign<<','
<<td.drawingDirection<<','
<<td.lineSpacingStyle<<','
<<td.lineSpacingFactor<<','
<<td.text.toLatin1().data() <<','
<<td.style.toLatin1().data()<<','
<<td.angle<<','
<<td.updateMode<<','
<<")";
return os;
}
/**
* Constructor.
*/
RS_MText::RS_MText(RS_EntityContainer* parent,
const RS_MTextData& d)
: RS_EntityContainer(parent), data(d) {
usedTextHeight = 0.0;
usedTextWidth = 0.0;
setText(data.text);
}
RS_Entity* RS_MText::clone() const{
RS_MText* t = new RS_MText(*this);
t->setOwner(isOwner());
t->initId();
t->detach();
return t;
}
/**
* Sets a new text. The entities representing the
* text are updated.
*/
void RS_MText::setText(const QString& t) {
data.text = t;
// handle some special flags embedded in the text:
if (data.text.left(4)=="\\A0;") {
data.text = data.text.mid(4);
data.valign = RS_MTextData::VABottom;
} else if (data.text.left(4)=="\\A1;") {
data.text = data.text.mid(4);
data.valign = RS_MTextData::VAMiddle;
} else if (data.text.left(4)=="\\A2;") {
data.text = data.text.mid(4);
data.valign = RS_MTextData::VATop;
}
if (data.updateMode==RS2::Update) {
update();
//calculateBorders();
}
}
/**
* Gets the alignment as an int.
*
* @return 1: top left ... 9: bottom right
*/
int RS_MText::getAlignment() {
if (data.valign==RS_MTextData::VATop) {
if (data.halign==RS_MTextData::HALeft) {
return 1;
} else if (data.halign==RS_MTextData::HACenter) {
return 2;
} else if (data.halign==RS_MTextData::HARight) {
return 3;
}
} else if (data.valign==RS_MTextData::VAMiddle) {
if (data.halign==RS_MTextData::HALeft) {
return 4;
} else if (data.halign==RS_MTextData::HACenter) {
return 5;
} else if (data.halign==RS_MTextData::HARight) {
return 6;
}
} else if (data.valign==RS_MTextData::VABottom) {
if (data.halign==RS_MTextData::HALeft) {
return 7;
} else if (data.halign==RS_MTextData::HACenter) {
return 8;
} else if (data.halign==RS_MTextData::HARight) {
return 9;
}
}
return 1;
}
/**
* Sets the alignment from an int.
*
* @param a 1: top left ... 9: bottom right
*/
void RS_MText::setAlignment(int a) {
switch (a%3) {
default:
case 1:
data.halign = RS_MTextData::HALeft;
break;
case 2:
data.halign = RS_MTextData::HACenter;
break;
case 0:
data.halign = RS_MTextData::HARight;
break;
}
switch ((int)ceil(a/3.0)) {
default:
case 1:
data.valign = RS_MTextData::VATop;
break;
case 2:
data.valign = RS_MTextData::VAMiddle;
break;
case 3:
data.valign = RS_MTextData::VABottom;
break;
}
}
/**
* @return Number of lines in this text entity.
*/
int RS_MText::getNumberOfLines() {
int c=1;
for (int i=0; i<(int)data.text.length(); ++i) {
if (data.text.at(i).unicode()==0x0A) {
c++;
}
}
return c;
}
/**
* Updates the Inserts (letters) of this text. Called when the
* text or it's data, position, alignment, .. changes.
* This method also updates the usedTextWidth / usedTextHeight property.
*/
void RS_MText::update()
{
RS_DEBUG->print("RS_MText::update");
clear();
if (isUndone()) {
return;
}
usedTextWidth = 0.0;
usedTextHeight = 0.0;
RS_Font* font {RS_FONTLIST->requestFont( data.style)};
if (nullptr == font) {
return;
}
RS_Vector letterPos {RS_Vector( 0.0, -9.0)};
RS_Vector letterSpace {RS_Vector( font->getLetterSpacing(), 0.0)};
RS_Vector space {RS_Vector( font->getWordSpacing(), 0.0)};
int lineCounter {0};
// Every single text line gets stored in this entity container
// so we can move the whole line around easily:
RS_EntityContainer* oneLine {new RS_EntityContainer(this)};
// First every text line is created with
// alignment: top left
// angle: 0
// height: 9.0
// Rotation, scaling and centering is done later
// For every letter:
for (int i = 0; i < static_cast<int>(data.text.length()); ++i) {
bool handled {false};
switch (data.text.at(i).unicode()) {
case 0x0A:
// line feed:
updateAddLine( oneLine, lineCounter++);
oneLine = new RS_EntityContainer(this);
letterPos = RS_Vector( 0.0, -9.0);
break;
case 0x20:
// Space:
letterPos += space;
break;
case 0x5C: {
// code (e.g. \S, \P, ..)
++i;
if (static_cast<int>(data.text.length()) <= i) {
continue;
}
int ch {data.text.at(i).unicode()};
switch (ch) {
case 'P':
updateAddLine( oneLine, lineCounter++);
oneLine = new RS_EntityContainer(this);
letterPos = RS_Vector( 0.0, -9.0);
handled = true;
break;
case 'f':
case 'F': {
//font change
// \f{symbol} changes font to symbol
// \f{} sets font to standard
++i;
if ('{' != data.text.at(i).unicode()) {
--i;
continue;
}
int j {data.text.indexOf( '}', i)};
if (j > i) {
QString fontName;
if (i + 1 == j) {
fontName = "standard";
}
else {
fontName = data.text.mid( i + 1, j - i - 1);
}
RS_Font* fontNew {RS_FONTLIST->requestFont( fontName)};
if (nullptr != fontNew) {
font = fontNew;
}
if (nullptr == font) {
font = RS_FONTLIST->requestFont( "standard");
}
i = j;
}
continue;
} // inner case 'f','F'
case 'S': {
QString upperText;
QString lowerText;
// get upper string:
++i;
while (static_cast<int>(data.text.length()) > i
&& data.text.at(i).unicode()!='^'
&& data.text.at(i).unicode()!='\\') {
upperText += data.text.at(i);
++i;
}
++i;
if (static_cast<int>(data.text.length()) > i
&& '^' == data.text.at(i - 1).unicode()
&& ' ' == data.text.at(i).unicode() ) {
++i;
}
// get lower string:
while (static_cast<int>(data.text.length()) > i
&& ';' != data.text.at(i).unicode()) {
lowerText += data.text.at(i);
++i;
}
// add texts:
double upperWidth {0.0};
if (! upperText.isEmpty()) {
RS_MText* upper { new RS_MText( oneLine,
RS_MTextData( letterPos + RS_Vector( 0.0, 9.0),
4.0,
100.0,
RS_MTextData::VATop,
RS_MTextData::HALeft,
RS_MTextData::LeftToRight,
RS_MTextData::Exact,
1.0,
upperText,
data.style,
0.0,
RS2::Update)) };
upper->setLayer( nullptr);
upper->setPen( RS_Pen( RS2::FlagInvalid));
upper->calculateBorders();
oneLine->addEntity(upper);
upperWidth = upper->getSize().x;
}
double lowerWidth {0.0};
if (! lowerText.isEmpty()) {
RS_MText* lower { new RS_MText( oneLine,
RS_MTextData( letterPos + RS_Vector( 0.0, 4.0),
4.0,
100.0,
RS_MTextData::VATop,
RS_MTextData::HALeft,
RS_MTextData::LeftToRight,
RS_MTextData::Exact,
1.0,
lowerText,
data.style,
0.0,
RS2::Update)) };
lower->setLayer( nullptr);
lower->setPen( RS_Pen( RS2::FlagInvalid));
lower->calculateBorders();
oneLine->addEntity(lower);
lowerWidth = lower->getSize().x;
}
if (upperWidth > lowerWidth) {
letterPos += RS_Vector( upperWidth, 0.0);
}
else {
letterPos += RS_Vector( lowerWidth, 0.0);
}
letterPos += letterSpace;
handled = true;
break;
} // inner case 'S'
default:
--i;
break;
} // inner switch (ch)
if (handled) {
break;
}
} // outer case 0x5C
// if char is not handled
// fall-through
default: {
// One Letter:
QString letterText {QString(data.text.at(i))};
if (nullptr == font->findLetter( letterText)) {
RS_DEBUG->print("RS_MText::update: missing font for letter( %s ), replaced it with QChar(0xfffd)",
qPrintable( letterText));
letterText = QChar( 0xfffd);
}
RS_DEBUG->print("RS_MText::update: insert a letter at pos: %f/%f", letterPos.x, letterPos.y);
RS_InsertData d( letterText,
letterPos,
RS_Vector( 1.0, 1.0),
0.0,
1,
1,
RS_Vector( 0.0, 0.0),
font->getLetterList(),
RS2::NoUpdate);
RS_Insert* letter {new RS_Insert(this, d)};
RS_Vector letterWidth;
letter->setPen( RS_Pen( RS2::FlagInvalid));
letter->setLayer( nullptr);
letter->update();
letter->forcedCalculateBorders();
letterWidth = RS_Vector( letter->getMax().x - letterPos.x, 0.0);
if (0 > letterWidth.x) {
letterWidth.x = -letterSpace.x;
}
oneLine->addEntity( letter);
// next letter position:
letterPos += letterWidth;
letterPos += letterSpace;
break;
} // outer default
} // outer switch (data.text.at(i).unicode())
} // for (i) loop
double tt {updateAddLine( oneLine, lineCounter)};
if (RS_MTextData::VABottom == data.valign) {
RS_Vector ot {RS_Vector( 0.0, -tt).rotate( data.angle)};
RS_EntityContainer::move( ot);
}
usedTextHeight -= data.height * data.lineSpacingFactor * 5.0 / 3.0 - data.height;
forcedCalculateBorders();
RS_DEBUG->print("RS_MText::update: OK");
}
/**
* Used internally by update() to add a text line created with
* default values and alignment to this text container.
*
* @param textLine The text line.
* @param lineCounter Line number.
*
* @return distance over the text base-line
*/
double RS_MText::updateAddLine(RS_EntityContainer* textLine, int lineCounter) {
double ls =5.0/3.0;
RS_DEBUG->print("RS_MText::updateAddLine: width: %f", textLine->getSize().x);
//textLine->forcedCalculateBorders();
//RS_DEBUG->print("RS_MText::updateAddLine: width 2: %f", textLine->getSize().x);
// Move to correct line position:
textLine->move(RS_Vector(0.0, -9.0 * lineCounter
* data.lineSpacingFactor * ls));
if( ! RS_EntityContainer::autoUpdateBorders) {
//only update borders when needed
textLine->forcedCalculateBorders();
}
RS_Vector textSize = textLine->getSize();
RS_DEBUG->print("RS_MText::updateAddLine: width 2: %f", textSize.x);
// Horizontal Align:
switch (data.halign) {
case RS_MTextData::HACenter:
RS_DEBUG->print("RS_MText::updateAddLine: move by: %f", -textSize.x/2.0);
textLine->move(RS_Vector(-textSize.x/2.0, 0.0));
break;
case RS_MTextData::HARight:
textLine->move(RS_Vector(-textSize.x, 0.0));
break;
default:
break;
}
// Vertical Align:
double vSize = getNumberOfLines()*9.0*data.lineSpacingFactor*ls
- (9.0*data.lineSpacingFactor*ls - 9.0);
switch (data.valign) {
case RS_MTextData::VAMiddle:
textLine->move(RS_Vector(0.0, vSize/2.0));
break;
case RS_MTextData::VABottom:
textLine->move(RS_Vector(0.0, vSize));
break;
default:
break;
}
// Scale:
textLine->scale(RS_Vector(0.0,0.0),
RS_Vector(data.height/9.0, data.height/9.0));
textLine->forcedCalculateBorders();
// Update actual text size (before rotating, after scaling!):
if (textLine->getSize().x>usedTextWidth) {
usedTextWidth = textLine->getSize().x;
}
usedTextHeight += data.height*data.lineSpacingFactor*ls;
// Gets the distance over text base-line (before rotating, after scaling!):
double textTail = textLine->getMin().y;
// Rotate:
textLine->rotate(RS_Vector(0.0,0.0), data.angle);
// Move:
textLine->move(data.insertionPoint);
textLine->setPen(RS_Pen(RS2::FlagInvalid));
textLine->setLayer(NULL);
textLine->forcedCalculateBorders();
addEntity(textLine);
return textTail;
}
RS_Vector RS_MText::getNearestEndpoint(const RS_Vector& coord, double* dist)const {
if (dist) {
*dist = data.insertionPoint.distanceTo(coord);
}
return data.insertionPoint;
}
RS_VectorSolutions RS_MText::getRefPoints() const{
return RS_VectorSolutions({data.insertionPoint});
}
void RS_MText::move(const RS_Vector& offset) {
RS_EntityContainer::move(offset);
data.insertionPoint.move(offset);
// update();
}
void RS_MText::rotate(const RS_Vector& center, const double& angle) {
RS_Vector angleVector(angle);
RS_EntityContainer::rotate(center, angleVector);
data.insertionPoint.rotate(center, angleVector);
data.angle = RS_Math::correctAngle(data.angle+angle);
// update();
}
void RS_MText::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_EntityContainer::rotate(center, angleVector);
data.insertionPoint.rotate(center, angleVector);
data.angle = RS_Math::correctAngle(data.angle+angleVector.angle());
// update();
}
void RS_MText::scale(const RS_Vector& center, const RS_Vector& factor) {
data.insertionPoint.scale(center, factor);
data.width*=factor.x;
data.height*=factor.x;
update();
}
void RS_MText::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
data.insertionPoint.mirror(axisPoint1, axisPoint2);
//double ang = axisPoint1.angleTo(axisPoint2);
bool readable = RS_Math::isAngleReadable(data.angle);
RS_Vector vec = RS_Vector::polar(1.0, data.angle);
vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1);
data.angle = vec.angle();
bool corr;
data.angle = RS_Math::makeAngleReadable(data.angle, readable, &corr);
if (corr) {
if (data.halign==RS_MTextData::HALeft) {
data.halign=RS_MTextData::HARight;
} else if (data.halign==RS_MTextData::HARight) {
data.halign=RS_MTextData::HALeft;
}
} else {
if (data.valign==RS_MTextData::VATop) {
data.valign=RS_MTextData::VABottom;
} else if (data.valign==RS_MTextData::VABottom) {
data.valign=RS_MTextData::VATop;
}
}
update();
}
bool RS_MText::hasEndpointsWithinWindow(const RS_Vector& /*v1*/, const RS_Vector& /*v2*/) {
return false;
}
/**
* Implementations must stretch the given range of the entity
* by the given offset.
*/
void RS_MText::stretch(const RS_Vector& firstCorner, const RS_Vector& secondCorner, const RS_Vector& offset) {
if (getMin().isInWindow(firstCorner, secondCorner) &&
getMax().isInWindow(firstCorner, secondCorner)) {
move(offset);
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_MText& p) {
os << " Text: " << p.getData() << "\n";
return os;
}
void RS_MText::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/)
{
if (!(painter && view)) {
return;
}
if (!view->isPrintPreview() && !view->isPrinting())
{
if (view->isPanning() || view->toGuiDY(getHeight()) < 4)
{
painter->drawRect(view->toGui(getMin()), view->toGui(getMax()));
return;
}
}
foreach (auto e, entities)
{
view->drawEntity(painter, e);
}
}

276
lib/engine/rs_mtext.h Normal file
View File

@@ -0,0 +1,276 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_MTEXT_H
#define RS_MTEXT_H
#include "rs_entitycontainer.h"
/**
* Holds the data that defines a text entity.
*/
struct RS_MTextData {
/**
* Vertical alignments.
*/
enum VAlign {
VATop, /**< Top. */
VAMiddle, /**< Middle */
VABottom /**< Bottom */
};
/**
* Horizontal alignments.
*/
enum HAlign {
HALeft, /**< Left */
HACenter, /**< Centered */
HARight /**< Right */
};
/**
* MText drawing direction.
*/
enum MTextDrawingDirection {
LeftToRight, /**< Left to right */
TopToBottom, /**< Top to bottom */
ByStyle /**< Inherited from associated text style */
};
/**
* Line spacing style for MTexts.
*/
enum MTextLineSpacingStyle {
AtLeast, /**< Taller characters will override */
Exact /**< Taller characters will not override */
};
/**
* Default constructor. Leaves the data object uninitialized.
*/
RS_MTextData() = default;
/**
* Constructor with initialisation.
*
* @param insertionPoint Insertion point
* @param height Nominal (initial) text height
* @param width Reference rectangle width
* @param valign Vertical alignment
* @param halign Horizontal alignment
* @param drawingDirection Drawing direction
* @param lineSpacingStyle Line spacing style
* @param lineSpacingFactor Line spacing factor
* @param text Text string
* @param style Text style name
* @param angle Rotation angle
* @param updateMode RS2::Update will update the text entity instantly
* RS2::NoUpdate will not update the entity. You can update
* it later manually using the update() method. This is
* often the case since you might want to adjust attributes
* after creating a text entity.
*/
RS_MTextData(const RS_Vector& insertionPoint,
double height,
double width,
VAlign valign,
HAlign halign,
MTextDrawingDirection drawingDirection,
MTextLineSpacingStyle lineSpacingStyle,
double lineSpacingFactor,
const QString& text,
const QString& style,
double angle,
RS2::UpdateMode updateMode = RS2::Update);
/** Insertion point */
RS_Vector insertionPoint;
/** Nominal (initial) text height */
double height;
/** Reference rectangle width */
double width;
/** Vertical alignment */
VAlign valign;
/** Horizontal alignment */
HAlign halign;
/** Drawing direction */
MTextDrawingDirection drawingDirection;
/** Line spacing style */
MTextLineSpacingStyle lineSpacingStyle;
/** Line spacing factor */
double lineSpacingFactor;
/** Text string */
QString text;
/** Text style name */
QString style;
/** Rotation angle */
double angle;
/** Update mode */
RS2::UpdateMode updateMode;
};
std::ostream& operator << (std::ostream& os, const RS_MTextData& td);
/**
* Class for a text entity.
* Please note that text strings can contain special
* characters such as %%c for a diameter sign as well as unicode
* characters. Line feeds are stored as real line feeds in the string.
*
* @author Andrew Mustun
*/
class RS_MText : public RS_EntityContainer {
public:
RS_MText(RS_EntityContainer* parent,
const RS_MTextData& d);
virtual ~RS_MText() = default;
virtual RS_Entity* clone() const override;
/** @return RS2::EntityText */
virtual RS2::EntityType rtti() const override{
return RS2::EntityMText;
}
/** @return Copy of data that defines the text. */
RS_MTextData getData() const {
return data;
}
void update() override;
int getNumberOfLines();
RS_Vector getInsertionPoint() {
return data.insertionPoint;
}
double getHeight() {
return data.height;
}
void setHeight(double h) {
data.height = h;
}
double getWidth() {
return data.width;
}
void setAlignment(int a);
int getAlignment();
RS_MTextData::VAlign getVAlign() {
return data.valign;
}
void setVAlign(RS_MTextData::VAlign va) {
data.valign = va;
}
RS_MTextData::HAlign getHAlign() {
return data.halign;
}
void setHAlign(RS_MTextData::HAlign ha) {
data.halign = ha;
}
RS_MTextData::MTextDrawingDirection getDrawingDirection() {
return data.drawingDirection;
}
RS_MTextData::MTextLineSpacingStyle getLineSpacingStyle() {
return data.lineSpacingStyle;
}
void setLineSpacingFactor(double f) {
data.lineSpacingFactor = f;
}
double getLineSpacingFactor() {
return data.lineSpacingFactor;
}
void setText(const QString& t);
QString getText() {
return data.text;
}
void setStyle(const QString& s) {
data.style = s;
}
QString getStyle() {
return data.style;
}
void setAngle(double a) {
data.angle = a;
}
double getAngle() {
return data.angle;
}
double getUsedTextWidth() {
return usedTextWidth;
}
double getUsedTextHeight() {
return usedTextHeight;
}
// virtual double getLength() const {
// return -1.0;
// }
/**
* @return The insertion point as endpoint.
*/
virtual RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = NULL)const override;
virtual RS_VectorSolutions getRefPoints() const override;
virtual void move(const RS_Vector& offset) override;
virtual void rotate(const RS_Vector& center, const double& angle) override;
virtual void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
virtual void scale(const RS_Vector& center, const RS_Vector& factor) override;
virtual void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
virtual bool hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2) override;
virtual void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Text& p);
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
private:
double updateAddLine(RS_EntityContainer* textLine, int lineCounter);
protected:
RS_MTextData data;
/**
* Text width used by the current contents of this text entity.
* This property is updated by the update method.
* @see update
*/
double usedTextWidth;
/**
* Text height used by the current contents of this text entity.
* This property is updated by the update method.
* @see update
*/
double usedTextHeight;
};
#endif

View File

@@ -0,0 +1,93 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_overlaybox.h"
#include "rs_debug.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
#include "rs_graphic.h"
#include <QBrush>
/**
* Constructor.
*/
RS_OverlayBox::RS_OverlayBox(RS_EntityContainer* parent,
const RS_OverlayBoxData& d)
:RS_AtomicEntity(parent), data(d) {
}
RS_Entity* RS_OverlayBox::clone() const{
RS_OverlayBox* l = new RS_OverlayBox(*this);
l->initId();
return l;
}
void RS_OverlayBox::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) {
if (painter==NULL || view==NULL) {
return;
}
RS_Vector v1=view->toGui(getCorner1());
RS_Vector v2=view->toGui(getCorner2());
QRectF selectRect(
v1.x,
v1.y,
v2.x - v1.x,
v2.y - v1.y);
if (v1.x > v2.x) {
RS_Pen p(RS_Color(50,255,50),RS2::Width00,RS2::DashLine);
painter->setPen(p);
// painter->setPen(QColor(50, 255, 50));
painter->fillRect(selectRect, RS_Color(9, 255, 9, 90));
}
else {
painter->setPen(QColor(50, 50, 255));
painter->fillRect(selectRect, RS_Color(9, 9, 255, 90));
}
painter->drawRect(v1, v2);
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_OverlayBox& l) {
os << " Line: " << l.getData() << "\n";
return os;
}
std::ostream& operator << (std::ostream& os, const RS_OverlayBoxData& ld) {
os << "(" << ld.corner1 <<
"/" << ld.corner2 <<
")";
return os;
}

104
lib/engine/rs_overlaybox.h Normal file
View File

@@ -0,0 +1,104 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_OVERLAYBOX_H
#define RS_OVERLAYBOX_H
#include "rs_atomicentity.h"
/**
* Holds the data that defines a line.
*/
class RS_OverlayBoxData {
public:
/**
* Default constructor. Leaves the data object uninitialized.
*/
RS_OverlayBoxData() = default;
RS_OverlayBoxData(const RS_Vector& corner1, const RS_Vector& corner2)
: corner1(corner1), corner2(corner2) {}
friend class RS_OverlayBox;
friend std::ostream& operator << (std::ostream& os, const RS_OverlayBoxData& ld);
public:
RS_Vector corner1;
RS_Vector corner2;
};
/**
* Class for a line entity.
*
* @author R. van Twisk
*/
class RS_OverlayBox : public RS_AtomicEntity {
public:
RS_OverlayBox(RS_EntityContainer* parent, const RS_OverlayBoxData& d);
RS_Entity* clone() const override;
/** @return RS2::EntityLine */
RS2::EntityType rtti() const override{
return RS2::EntityOverlayBox;
}
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
/** @return Start point of the entity */
RS_Vector getCorner1() const {
return data.corner1;
}
/** @return End point of the entity */
RS_Vector getCorner2() const {
return data.corner2;
}
/** @return Copy of data that defines the line. */
RS_OverlayBoxData getData() const {
return data;
}
/** We should make a separate drawing mechanism for overlays and not use entities */
void move(const RS_Vector& /*offset*/)override{}
void rotate(const RS_Vector& /*center*/, const double& /*angle*/)override{}
void rotate(const RS_Vector& /*center*/, const RS_Vector& /*angleVector*/)override{}
void scale(const RS_Vector& /*center*/, const RS_Vector& /*factor*/)override{}
void mirror(const RS_Vector& /*axisPoint1*/, const RS_Vector& /*axisPoint2*/)override{}
void calculateBorders() override{}
RS_Vector getNearestEndpoint(const RS_Vector&, double*)const override{return {};}
RS_Vector getNearestPointOnEntity(const RS_Vector&, bool, double*, RS_Entity**)const override{return {};}
RS_Vector getNearestCenter(const RS_Vector&, double*)const override{return {};}
RS_Vector getNearestMiddle(const RS_Vector&, double*,int)const override{return {};}
RS_Vector getNearestDist(double, const RS_Vector&, double*)const override{return {};}
double getDistanceToPoint(const RS_Vector&, RS_Entity**, RS2::ResolveLevel, double)const override{return -1;}//is -1 right here
protected:
RS_OverlayBoxData data;
}
;
#endif

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_overlayline.h"
#include "rs_painter.h"
/**
* Constructor.
*/
RS_OverlayLine::RS_OverlayLine(RS_EntityContainer* parent,
const RS_LineData& d)
:RS_Line(parent, d) {
}
void RS_OverlayLine::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) {
if (painter==NULL || view==NULL) {
return;
}
painter->drawLine(getStartpoint(),
getEndpoint());
}

View File

@@ -0,0 +1,53 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_OVERLAYLINE_H
#define RS_OVERLAYLINE_H
#include "rs_line.h"
/**
* Class for a overlay line entity. It's used to draw lines on the overlay paint event
* The main difference is that the coordinates are actual screen coordinates and not real world coordinates
*
* @author R. van Twisk
*/
class RS_OverlayLine : public RS_Line {
public:
RS_OverlayLine(RS_EntityContainer* parent, const RS_LineData& d);
virtual void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
RS2::EntityType rtti() const override{
return RS2::EntityOverlayLine;
}
}
;
#endif

119
lib/engine/rs_pattern.cpp Normal file
View File

@@ -0,0 +1,119 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_pattern.h"
#include "rs_system.h"
#include "rs_fileio.h"
#include "rs_layer.h"
#include "rs_debug.h"
/**
* Constructor.
*
* @param fileName File name of a DXF file defining the pattern
*/
RS_Pattern::RS_Pattern(const QString& fileName)
: RS_EntityContainer(NULL)
,fileName(fileName)
,loaded(false)
{
RS_DEBUG->print("RS_Pattern::RS_Pattern() ");
}
/**
* Loads the given pattern file into this pattern.
* Entities other than lines are ignored.
*
* @param filename File name of the pattern file (without path and
* extension or full path.
*/
bool RS_Pattern::loadPattern() {
if (loaded) {
return true;
}
RS_DEBUG->print("RS_Pattern::loadPattern");
QString path;
// Search for the appropriate pattern if we have only the name of the pattern:
if (!fileName.toLower().contains(".dxf")) {
QStringList patterns = RS_SYSTEM->getPatternList();
QFileInfo file;
for (QStringList::Iterator it = patterns.begin();
it!=patterns.end();
it++) {
if (QFileInfo(*it).baseName().toLower()==fileName.toLower()) {
path = *it;
RS_DEBUG->print("Pattern found: %s", path.toLatin1().data());
break;
}
}
}
// We have the full path of the pattern:
else {
path = fileName;
}
// No pattern paths found:
if (path.isEmpty()) {
RS_DEBUG->print("No pattern \"%s\"available.", fileName.toLatin1().data());
return false;
}
RS_Graphic gr;
RS_FileIO::instance()->fileImport(gr, path);
for(auto e: gr){
if (e->rtti()==RS2::EntityLine ||
e->rtti()==RS2::EntityArc||
e->rtti()==RS2::EntityEllipse
) {
RS_Layer* l = e->getLayer();
RS_Entity* cl = e->clone();
cl->reparent(this);
if (l) {
cl->setLayer(l->getName());
}
addEntity(cl);
}
}
loaded = true;
RS_DEBUG->print("RS_Pattern::loadPattern: OK");
return true;
}
QString RS_Pattern::getFileName() const {
return fileName;
}

65
lib/engine/rs_pattern.h Normal file
View File

@@ -0,0 +1,65 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_PATTERN_H
#define RS_PATTERN_H
#include "rs_entitycontainer.h"
class RS_PatternList;
/**
* Patterns are used for hatches. They are stored in a RS_PatternList.
* Use RS_PatternList to access a pattern.
*
* @author Andrew Mustun
*/
class RS_Pattern : public RS_EntityContainer {
public:
RS_Pattern(const QString& fileName);
virtual ~RS_Pattern()=default;
RS2::EntityType rtti() const{
return RS2::EntityPattern;
}
virtual bool loadPattern();
/** @return the fileName of this pattern. */
QString getFileName() const;
protected:
//! Pattern file name
QString fileName;
//! Is this pattern currently loaded into memory?
bool loaded;
};
#endif

View File

@@ -0,0 +1,112 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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<QString>
#include "rs_patternlist.h"
#include "rs_system.h"
#include "rs_pattern.h"
#include "rs_debug.h"
RS_PatternList* RS_PatternList::instance() {
static RS_PatternList instance;
return &instance;
}
RS_PatternList::~RS_PatternList() = default;
/**
* Initializes the pattern list by creating empty RS_Pattern
* objects, one for each pattern that could be found.
*/
void RS_PatternList::init() {
RS_DEBUG->print("RS_PatternList::initPatterns");
QStringList list = RS_SYSTEM->getPatternList();
patterns.clear();
for (auto const& s: list) {
RS_DEBUG->print("pattern: %s:", s.toLatin1().data());
QFileInfo fi(s);
QString const name = fi.baseName().toLower();
patterns[name] = std::unique_ptr<RS_Pattern>{};
RS_DEBUG->print("base: %s", name.toLatin1().data());
}
}
/**
* @return Pointer to the pattern with the given name or
* \p NULL if no such pattern was found. The pattern will be loaded into
* memory if it's not already.
*/
RS_Pattern* RS_PatternList::requestPattern(const QString& name) {
RS_DEBUG->print("RS_PatternList::requestPattern %s", name.toLatin1().data());
QString name2 = name.toLower();
RS_DEBUG->print("name2: %s", name2.toLatin1().data());
if (patterns.count(name2)) {
if (!patterns[name2]) {
RS_Pattern* p = new RS_Pattern(name2);
p->loadPattern();
patterns[name2].reset(p);
}
RS_DEBUG->print("name2: %s, size= %d", name2.toLatin1().data(),
patterns[name2]->countDeep());
return patterns[name2].get();
}
return nullptr;
}
bool RS_PatternList::contains(const QString& name) const {
return patterns.count(name.toLower());
}
/**
* Dumps the patterns to stdout.
*/
std::ostream& operator << (std::ostream& os, RS_PatternList& l) {
os << "Patternlist: \n";
for (auto const& pa: l.patterns)
if (pa.second)
os<< *pa.second << '\n';
return os;
}

View File

@@ -0,0 +1,94 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_PATTERNLIST_H
#define RS_PATTERNLIST_H
#include<map>
#include<memory>
class RS_Pattern;
class QString;
#define RS_PATTERNLIST RS_PatternList::instance()
/**
* The global list of patterns. This is implemented as a singleton.
* Use RS_PatternList::instance() to get a pointer to the object.
*
* @author Andrew Mustun
*/
class RS_PatternList {
using PTN_MAP = std::map<QString, std::unique_ptr<RS_Pattern>>;
RS_PatternList() = default;
public:
/**
* @return Instance to the unique pattern list.
*/
static RS_PatternList* instance();
~RS_PatternList();
RS_PatternList(RS_PatternList const&) = delete;
RS_PatternList& operator = (RS_PatternList const&) = delete;
RS_PatternList(RS_PatternList &&) = delete;
RS_PatternList& operator = (RS_PatternList &&) = delete;
void init();
int countPatterns() const {
return patterns.size();
}
//! \{ range based loop support
PTN_MAP::iterator begin() {
return patterns.begin();
}
PTN_MAP::const_iterator begin() const{
return patterns.begin();
}
PTN_MAP::iterator end() {
return patterns.end();
}
PTN_MAP::const_iterator end() const{
return patterns.end();
}
//! \}
RS_Pattern* requestPattern(const QString& name);
bool contains(const QString& name) const;
friend std::ostream& operator << (std::ostream& os, RS_PatternList& l);
private:
//! patterns in the graphic
PTN_MAP patterns;
};
#endif

13
lib/engine/rs_pen.cpp Normal file
View File

@@ -0,0 +1,13 @@
#include <iostream>
#include "rs_pen.h"
std::ostream& operator << (std::ostream& os, const RS_Pen& p) {
//os << "style: " << p.style << std::endl;
os << " pen color: " << p.getColor()
<< " pen width: " << p.getWidth()
<< " pen screen width: " << p.getScreenWidth()
<< " pen line type: " << p.getLineType()
<< " flags: " << (p.getFlag(RS2::FlagInvalid) ? "INVALID" : "")
<< std::endl;
return os;
}

141
lib/engine/rs_pen.h Normal file
View File

@@ -0,0 +1,141 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_PEN_H
#define RS_PEN_H
#include "rs.h"
#include "rs_color.h"
#include "rs_flags.h"
/**
* A pen stores attributes for painting such as line width,
* linetype, color, ...
*
* @author Andrew Mustun
*/
class RS_Pen : public RS_Flags {
public:
/**
* Creates a default pen (black, solid, width 0).
*/
RS_Pen() : RS_Flags() {
setColor(RS_Color(0,0,0));
setWidth(RS2::Width00);
setLineType(RS2::SolidLine);
setScreenWidth(0);
}
/**
* Creates a pen with the given attributes.
*/
RS_Pen(const RS_Color& c,
RS2::LineWidth w,
RS2::LineType t) : RS_Flags() {
setColor(c);
setWidth(w);
setLineType(t);
setScreenWidth(0);
}
/**
* Creates a default pen with the given flags. This is
* usually used to create invalid pens.
*
* e.g.:
* <pre>
* RS_Pen p(RS2::FlagInvalid);
* </pre>
*/
RS_Pen(unsigned int f) : RS_Flags(f) {
setColor(RS_Color(0,0,0));
setWidth(RS2::Width00);
setLineType(RS2::SolidLine);
setScreenWidth(0);
}
//RS_Pen(const RS_Pen& pen) : RS_Flags(pen.getFlags()) {
// lineType = pen.lineType;
// width = pen.width;
// color = pen.color;
//}
virtual ~RS_Pen() {}
RS2::LineType getLineType() const {
return lineType;
}
void setLineType(RS2::LineType t) {
lineType = t;
}
RS2::LineWidth getWidth() const {
return width;
}
void setWidth(RS2::LineWidth w) {
width = w;
}
double getScreenWidth() const {
return screenWidth;
}
void setScreenWidth(double w) {
screenWidth = w;
}
const RS_Color& getColor() const {
return color;
}
void setColor(const RS_Color& c) {
color = c;
}
bool isValid() {
return !getFlag(RS2::FlagInvalid);
}
//RS_Pen& operator = (const RS_Pen& p) {
// lineType = p.lineType;
// width = p.width;
// color = p.color;
// setFlags(p.getFlags());
// return *this;
//}
bool operator == (const RS_Pen& p) const {
return (lineType==p.lineType && width==p.width && color==p.color);
}
bool operator != (const RS_Pen& p) const {
return !(*this==p);
}
friend std::ostream& operator << (std::ostream& os, const RS_Pen& p);
protected:
RS2::LineType lineType;
RS2::LineWidth width;
double screenWidth;
RS_Color color;
};
#endif

214
lib/engine/rs_point.cpp Normal file
View File

@@ -0,0 +1,214 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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_point.h"
#include "rs_circle.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
RS_Point::RS_Point(RS_EntityContainer* parent,
const RS_PointData& d)
:RS_AtomicEntity(parent), data(d) {
calculateBorders ();
}
RS_Entity* RS_Point::clone() const {
RS_Point* p = new RS_Point(*this);
p->initId();
return p;
}
RS2::EntityType RS_Point::rtti() const
{
return RS2::EntityPoint;
}
void RS_Point::calculateBorders () {
minV = maxV = data.pos;
}
RS_VectorSolutions RS_Point::getRefPoints() const
{
return RS_VectorSolutions{data.pos};
}
RS_Vector RS_Point::getStartpoint() const
{
return data.pos;
}
RS_Vector RS_Point::getEndpoint() const
{
return data.pos;
}
RS_PointData RS_Point::getData() const
{
return data;
}
RS_Vector RS_Point::getPos() const
{
return data.pos;
}
RS_Vector RS_Point::getCenter() const
{
return data.pos;
}
double RS_Point::getRadius() const
{
return 0.;
}
bool RS_Point::isTangent(const RS_CircleData& circleData) const
{
double const dist=data.pos.distanceTo(circleData.center);
return fabs(dist - fabs(circleData.radius))<50.*RS_TOLERANCE;
}
void RS_Point::setPos(const RS_Vector& pos)
{
data.pos = pos;
}
RS_Vector RS_Point::getNearestEndpoint(const RS_Vector& coord, double* dist)const {
if (dist) {
*dist = data.pos.distanceTo(coord);
}
return data.pos;
}
RS_Vector RS_Point::getNearestPointOnEntity(const RS_Vector& coord,
bool /*onEntity*/, double* dist, RS_Entity** entity) const{
if (dist) {
*dist = data.pos.distanceTo(coord);
}
if (entity) {
*entity = const_cast<RS_Point*>(this);
}
return data.pos;
}
RS_Vector RS_Point::getNearestCenter(const RS_Vector& coord, double* dist) const{
if (dist) {
*dist = data.pos.distanceTo(coord);
}
return data.pos;
}
RS_Vector RS_Point::getMiddlePoint()const{
return data.pos;
}
RS_Vector RS_Point::getNearestMiddle(const RS_Vector& coord,
double* dist,
const int /*middlePoints*/)const {
if (dist) {
*dist = data.pos.distanceTo(coord);
}
return data.pos;
}
RS_Vector RS_Point::getNearestDist(double /*distance*/,
const RS_Vector& /*coord*/,
double* dist) const{
if (dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
double RS_Point::getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity,
RS2::ResolveLevel /*level*/,
double /*solidDist*/)const {
if (entity) {
*entity = const_cast<RS_Point*>(this);
}
return data.pos.distanceTo(coord);
}
void RS_Point::moveStartpoint(const RS_Vector& pos) {
data.pos = pos;
calculateBorders();
}
void RS_Point::move(const RS_Vector& offset) {
data.pos.move(offset);
calculateBorders();
}
void RS_Point::rotate(const RS_Vector& center, const double& angle) {
data.pos.rotate(center, angle);
calculateBorders();
}
void RS_Point::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
data.pos.rotate(center, angleVector);
calculateBorders();
}
void RS_Point::scale(const RS_Vector& center, const RS_Vector& factor) {
data.pos.scale(center, factor);
calculateBorders();
}
void RS_Point::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
data.pos.mirror(axisPoint1, axisPoint2);
calculateBorders();
}
void RS_Point::draw(RS_Painter* painter,RS_GraphicView* view, double& /*patternOffset*/) {
if (painter==NULL || view==NULL) {
return;
}
painter->drawPoint(view->toGui(getPos()));
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Point& p) {
os << " Point: " << p.getData() << "\n";
return os;
}
std::ostream& operator << (std::ostream& os, const RS_PointData& pd)
{
os << "(" << pd.pos << ")";
return os;
}
// EOF

119
lib/engine/rs_point.h Normal file
View File

@@ -0,0 +1,119 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_POINT_H
#define RS_POINT_H
#include "rs_atomicentity.h"
/**
* Holds the data that defines a point.
*/
struct RS_PointData {
RS_PointData(const RS_Vector& pos): pos(pos) {}
friend std::ostream& operator << (std::ostream& os, const RS_PointData& pd);
RS_Vector pos;
};
/**
* Class for a point entity.
*
* @author Andrew Mustun
*/
class RS_Point : public RS_AtomicEntity {
public:
RS_Point(RS_EntityContainer* parent,
const RS_PointData& d);
RS_Entity* clone() const override;
/** @return RS_ENTITY_POINT */
RS2::EntityType rtti() const override;
/**
* @return Start point of the entity.
*/
RS_Vector getStartpoint() const override;
/**
* @return End point of the entity.
*/
RS_Vector getEndpoint() const override;
void moveStartpoint(const RS_Vector& pos) override;
/** @return Copy of data that defines the point. */
RS_PointData getData() const;
RS_VectorSolutions getRefPoints() const override;
/** @return Position of the point */
RS_Vector getPos() const;
/** Sets a new position for this point. */
void setPos(const RS_Vector& pos);
RS_Vector getCenter() const override;
double getRadius() const override;
bool isTangent(const RS_CircleData& circleData) const override;
RS_Vector getMiddlePoint(void)const override;
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr)const override;
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true, double* dist = nullptr, RS_Entity** entity = nullptr)const override;
RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = nullptr)const override;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1)const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = nullptr)const override;
double getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity=nullptr,
RS2::ResolveLevel level=RS2::ResolveNone,
double solidDist = RS_MAXDOUBLE)const override;
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Point& p);
/** Recalculates the borders of this entity. */
void calculateBorders () override;
protected:
RS_PointData data;
//RS_Vector point;
};
#endif

768
lib/engine/rs_polyline.cpp Normal file
View File

@@ -0,0 +1,768 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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<cassert>
#include "rs_polyline.h"
#include "rs_debug.h"
#include "rs_line.h"
#include "rs_arc.h"
#include "rs_graphicview.h"
#include "rs_math.h"
#include "rs_information.h"
RS_PolylineData::RS_PolylineData():
startpoint(false)
,endpoint(false)
{
}
RS_PolylineData::RS_PolylineData(const RS_Vector& _startpoint,
const RS_Vector& _endpoint,
bool _closed):
startpoint(_startpoint)
,endpoint(_endpoint)
{
if (_closed) {
setFlag(RS2::FlagClosed);
}
}
std::ostream& operator << (std::ostream& os,
const RS_PolylineData& pd) {
os << "(" << pd.startpoint <<
"/" << pd.endpoint <<
")";
return os;
}
/**
* Constructor.
*/
RS_Polyline::RS_Polyline(RS_EntityContainer* parent)
:RS_EntityContainer(parent, true)
,closingEntity(nullptr)
,nextBulge(0.)
{
}
/**
* Constructor.
* @param d Polyline data
*/
RS_Polyline::RS_Polyline(RS_EntityContainer* parent,
const RS_PolylineData& d)
:RS_EntityContainer(parent, true)
,data(d)
,closingEntity(nullptr)
,nextBulge(0.)
{
calculateBorders();
}
RS_Entity* RS_Polyline::clone() const {
RS_Polyline* p = new RS_Polyline(*this);
p->setOwner(isOwner());
p->initId();
p->detach();
return p;
}
/**
* Removes the last vertex of this polyline.
*/
void RS_Polyline::removeLastVertex() {
RS_Entity* l = last();
if (l) {
removeEntity(l);
l = last();
if (l) {
if (l->isAtomic()) {
data.endpoint = l->getEndpoint();
}
else {
RS_DEBUG->print(RS_Debug::D_WARNING,
"RS_Polyline::removeLastVertex: "
"polyline contains non-atomic entity");
}
}
}
}
/**
* Adds a vertex from the endpoint of the last segment or
* from the startpoint of the first segment to 'v' or
* sets the startpoint to the point 'v'.
*
* The very first vertex added with this method is the startpoint.
*
* @param v vertex coordinate to be added
* @param bulge The bulge of the arc or 0 for a line segment (see DXF documentation)
* @param prepend true: prepend at start instead of append at end
*
* @return Pointer to the entity that was added or nullptr if this
* was the first vertex added.
*/
RS_Entity* RS_Polyline::addVertex(const RS_Vector& v, double bulge, bool prepend) {
RS_Entity* entity=nullptr;
//static double nextBulge = 0.0;
// very first vertex:
if (!data.startpoint.valid) {
data.startpoint = data.endpoint = v;
nextBulge = bulge;
}
// consequent vertices:
else {
// add entity to the polyline:
entity = createVertex(v, nextBulge, prepend);
if (entity) {
if (!prepend) {
RS_EntityContainer::addEntity(entity);
data.endpoint = v;
}
else {
RS_EntityContainer::insertEntity(0, entity);
data.startpoint = v;
}
}
nextBulge = bulge;
endPolyline();
}
//data.endpoint = v;
return entity;
}
/**
* Appends a vertex list from the endpoint of the last segment
* sets the startpoint to the first point if not exist.
*
* The very first vertex added with this method is the startpoint if not exists.
*
* @param vl list of vertexs coordinate to be added
* @param Pair are RS_Vector of coord and the bulge of the arc or 0 for a line segment (see DXF documentation)
*
* @return None
*/
void RS_Polyline::appendVertexs(const std::vector< std::pair<RS_Vector, double> >& vl) {
RS_Entity* entity=nullptr;
//static double nextBulge = 0.0;
if (!vl.size()) return;
size_t idx = 0;
// very first vertex:
if (!data.startpoint.valid) {
data.startpoint = data.endpoint = vl.at(idx).first;
nextBulge = vl.at(idx++).second;
}
// consequent vertices:
for (; idx< vl.size();idx++){
entity = createVertex(vl.at(idx).first, nextBulge, false);
data.endpoint = entity->getEndpoint();
RS_EntityContainer::addEntity(entity);
nextBulge = vl.at(idx).second;
}
endPolyline();
}
/**
* Creates a vertex from the endpoint of the last element or
* sets the startpoint to the point 'v'.
*
* The very first vertex added is the starting point.
*
* @param v vertex coordinate
* @param bulge The bulge of the arc (see DXF documentation)
* @param prepend true: Prepend instead of append at end
*
* @return Pointer to the entity that was created or nullptr if this
* was the first vertex added.
*/
RS_Entity* RS_Polyline::createVertex(const RS_Vector& v, double bulge, bool prepend) {
RS_Entity* entity=nullptr;
RS_DEBUG->print("RS_Polyline::createVertex: %f/%f to %f/%f bulge: %f",
data.endpoint.x, data.endpoint.y, v.x, v.y, bulge);
// create line for the polyline:
if (fabs(bulge)<RS_TOLERANCE) {
if (prepend) {
entity = new RS_Line{this, v, data.startpoint};
} else {
entity = new RS_Line{this, data.endpoint, v};
}
entity->setSelected(isSelected());
entity->setPen(RS_Pen(RS2::FlagInvalid));
entity->setLayer(nullptr);
//RS_EntityContainer::addEntity(entity);
//data.endpoint = v;
}
// create arc for the polyline:
else {
bool reversed = (bulge<0.0);
double alpha = atan(bulge)*4.0;
RS_Vector middle;
double dist;
double angle;
if (!prepend) {
middle = (data.endpoint+v)/2.0;
dist = data.endpoint.distanceTo(v)/2.0;
angle = data.endpoint.angleTo(v);
}
else {
middle = (data.startpoint+v)/2.0;
dist = data.startpoint.distanceTo(v)/2.0;
angle = v.angleTo(data.startpoint);
}
// alpha can't be 0.0 at this point
double const radius = fabs(dist / sin(alpha/2.0));
double const wu = fabs(radius*radius - dist*dist);
double h = sqrt(wu);
if (bulge>0.0) {
angle+=M_PI_2;
} else {
angle-=M_PI_2;
}
if (fabs(alpha)>M_PI) {
h*=-1.0;
}
RS_Vector center = RS_Vector::polar(h, angle);
center+=middle;
double a1;
double a2;
if (!prepend) {
a1 = center.angleTo(data.endpoint);
a2 = center.angleTo(v);
}
else {
a1 = center.angleTo(v);
a2 = center.angleTo(data.startpoint);
}
RS_ArcData const d(center, radius,
a1, a2,
reversed);
entity = new RS_Arc(this, d);
entity->setSelected(isSelected());
entity->setPen(RS_Pen(RS2::FlagInvalid));
entity->setLayer(nullptr);
}
return entity;
}
/**
* Ends polyline and adds the last entity if the polyline is closed
*/
void RS_Polyline::endPolyline() {
RS_DEBUG->print("RS_Polyline::endPolyline");
if (isClosed()) {
RS_DEBUG->print("RS_Polyline::endPolyline: adding closing entity");
// remove old closing entity:
if (closingEntity) {
removeEntity(closingEntity);
}
// add closing entity to the polyline:
closingEntity = createVertex(data.startpoint, nextBulge);
if (closingEntity && closingEntity->getLength()>1.0E-4) {
RS_EntityContainer::addEntity(closingEntity);
//data.endpoint = data.startpoint;
}
}
calculateBorders();
}
//RLZ: rewrite this:
void RS_Polyline::setClosed(bool cl, double bulge) {
Q_UNUSED(bulge);
bool areClosed = isClosed();
setClosed(cl);
if (isClosed()) {
endPolyline();
} else if (areClosed){
removeLastVertex();
}
}
/** sets a new start point of the polyline */
void RS_Polyline::setStartpoint(RS_Vector const& v) {
data.startpoint = v;
if (!data.endpoint.valid) {
data.endpoint = v;
}
}
/** @return Start point of the entity */
RS_Vector RS_Polyline::getStartpoint() const {
return data.startpoint;
}
/** sets a new end point of the polyline */
void RS_Polyline::setEndpoint(RS_Vector const& v) {
data.endpoint = v;
}
void RS_Polyline::setLayer(const QString& name) {
RS_Entity::setLayer(name);
// set layer for sub-entities
for (auto *e : entities) {
e->setLayer(layer);
}
}
void RS_Polyline::setLayer(RS_Layer* l) {
layer = l;
// set layer for sub-entities
for (auto *e : entities) {
e->setLayer(layer);
}
}
/** @return End point of the entity */
RS_Vector RS_Polyline::getEndpoint() const {
return data.endpoint;
}
/**
* @return The bulge of the closing entity.
*/
double RS_Polyline::getClosingBulge() const{
if (isClosed()) {
RS_Entity const* e = last();
if (e && e->rtti()==RS2::EntityArc) {
return static_cast<RS_Arc const*>(e)->getBulge();
}
}
return 0.0;
}
bool RS_Polyline::isClosed() const {
return data.getFlag(RS2::FlagClosed);
}
void RS_Polyline::setClosed(bool cl) {
if (cl) {
data.setFlag(RS2::FlagClosed);
}
else {
data.delFlag(RS2::FlagClosed);
}
}
/**
* Sets the polylines start and endpoint to match the first and last vertex.
*/
void RS_Polyline::updateEndpoints() {
RS_Entity* e1 = firstEntity();
if (e1 && e1->isAtomic()) {
RS_Vector const& v = e1->getStartpoint();
setStartpoint(v);
}
RS_Entity const* e2 = last();
if (isClosed()) {
e2 = prevEntity();
}
if (e2 && e2->isAtomic()) {
RS_Vector const& v = e2->getEndpoint();
setEndpoint(v);
}
}
/**
* Reimplementation of the addEntity method for a normal container.
* This reimplementation deletes the given entity!
*
* To add entities use addVertex() or addSegment() instead.
*/
void RS_Polyline::addEntity(RS_Entity* /*entity*/) {
RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Polyline::addEntity:"
" should never be called\n"
"use addVertex() or addSegment() instead"
);
assert(false);
}
/**
* Adds a segment to the polyline.
*/
/*void RS_Polyline::addSegment(RS_Entity* entity) {
RS_EntityContainer::addEntity(entity);
// TODO: reorder and check polyline
}*/
RS_VectorSolutions RS_Polyline::getRefPoints() const{
RS_VectorSolutions ret{{data.startpoint}};
for(auto e: *this){
if (e->isAtomic()) {
ret.push_back(e->getEndpoint());
}
}
ret.push_back( data.endpoint);
return ret;
}
RS_Vector RS_Polyline::getNearestRef( const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
// override the RS_EntityContainer method
// use RS_Entity instead for vertex dragging
return RS_Entity::getNearestRef( coord, dist);
}
RS_Vector RS_Polyline::getNearestSelectedRef( const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
// override the RS_EntityContainer method
// use RS_Entity instead for vertex dragging
return RS_Entity::getNearestSelectedRef( coord, dist);
}
/*
void RS_Polyline::reorder() {
// current point:
RS_Vector cp;
bool done = false;
do {
} while(!done);
}
*/
/**
* this should handle modifyOffset
*@ coord, indicate direction of offset
*@ distance of offset
*
*@Author, Dongxu Li
*/
bool RS_Polyline::offset(const RS_Vector& coord, const double& distance){
double dist;
//find the nearest one
int length=count();
std::vector<RS_Vector> intersections(length);
if(length>1){//sort the polyline entity start/end point order
int i(0);
double d0,d1;
RS_Entity* en0(entityAt(0));
RS_Entity* en1(entityAt(1));
RS_Vector vStart(en0->getStartpoint());
RS_Vector vEnd(en0->getEndpoint());
en1->getNearestEndpoint(vStart,&d0);
en1->getNearestEndpoint(vEnd,&d1);
if(d0<d1) en0->revertDirection();
for(i=1;i<length;i++){
//linked to head-tail chain
en1=entityAt(i);
vStart=en1->getStartpoint();
vEnd=en1->getEndpoint();
en0->getNearestEndpoint(vStart,&d0);
en0->getNearestEndpoint(vEnd,&d1);
if(d0>d1) en1->revertDirection();
intersections[i-1]=(en0->getEndpoint()+en1->getStartpoint())*0.5;
en0=en1;
}
if (isClosed()) {
en1=entityAt(0);
intersections[length-1]=(en0->getEndpoint()+en1->getStartpoint())*0.5;
}
}
RS_Entity* en(getNearestEntity(coord, &dist, RS2::ResolveNone));
if(!en) return false;
int indexNearest=findEntity(en);
// RS_Vector vp(en->getNearestPointOnEntity(coord,false));
// RS_Vector direction(en->getTangentDirection(vp));
// RS_Vector vp1(-direction.y,direction.x);//normal direction
// double a2(vp1.squared());
// if(a2<RS_TOLERANCE2) return false;
// vp1 *= distance/sqrt(a2);
// move(vp1);
// return true;
RS_Polyline* pnew= static_cast<RS_Polyline*>(clone());
int i;
i=indexNearest;
int previousIndex(i);
pnew->entityAt(i)->offset(coord,distance);
RS_Vector vp;
//offset all
//fixme, this is too ugly
for(i=indexNearest-1;i>=0;i--){
RS_VectorSolutions sol0=RS_Information::getIntersection(pnew->entityAt(previousIndex),entityAt(i),true);
// RS_VectorSolutions sol1;
double dmax(RS_TOLERANCE15);
RS_Vector trimP(false);
for(const RS_Vector& vp: sol0){
double d0( (vp - pnew->entityAt(previousIndex)->getStartpoint()).squared());//potential bug, need to trim better
if(d0>dmax) {
dmax=d0;
trimP=vp;
}
}
if(trimP.valid){
static_cast<RS_AtomicEntity*>(pnew->entityAt(previousIndex))->trimStartpoint(trimP);
static_cast<RS_AtomicEntity*>(pnew->entityAt(i))->trimEndpoint(trimP);
vp=pnew->entityAt(previousIndex)->getMiddlePoint();
}else{
vp=pnew->entityAt(previousIndex)->getStartpoint();
vp.rotate(entityAt(previousIndex)->getStartpoint(),entityAt(i)->getDirection2()-entityAt(previousIndex)->getDirection1()+M_PI);
}
pnew->entityAt(i)->offset(vp,distance);
previousIndex=i;
}
previousIndex=indexNearest;
for(i=indexNearest+1;i<length;i++){
RS_VectorSolutions sol0=RS_Information::getIntersection(pnew->entityAt(previousIndex),entityAt(i),true);
// RS_VectorSolutions sol1;
double dmax(RS_TOLERANCE15);
RS_Vector trimP(false);
for(const RS_Vector& vp: sol0){
double d0( (vp - pnew->entityAt(previousIndex)->getEndpoint()).squared());//potential bug, need to trim better
if(d0>dmax) {
dmax=d0;
trimP=vp;
}
}
if(trimP.valid){
static_cast<RS_AtomicEntity*>(pnew->entityAt(previousIndex))->trimEndpoint(trimP);
static_cast<RS_AtomicEntity*>(pnew->entityAt(i))->trimStartpoint(trimP);
vp=pnew->entityAt(previousIndex)->getMiddlePoint();
}else{
vp=pnew->entityAt(previousIndex)->getEndpoint();
vp.rotate(entityAt(previousIndex)->getEndpoint(),entityAt(i)->getDirection1()-entityAt(previousIndex)->getDirection2()+M_PI);
}
pnew->entityAt(i)->offset(vp,distance);
previousIndex=i;
}
//trim
//connect and trim RS_Modification m(*container, graphicView);
for(i=0;i<length;i++){
RS_Entity* en0;
RS_Entity* en1;
if (i<length-1){
en0=pnew->entityAt(i);
en1=pnew->entityAt(i+1);
}else{
if (isClosed()) {
en0=pnew->entityAt(i);
en1=pnew->entityAt(0);
}else{
break;
}
}
RS_VectorSolutions sol0=RS_Information::getIntersection(en0,en1,true);
if(sol0.getNumber()==0){
sol0=RS_Information::getIntersection(en0,en1);
// RS_Vector vp0(pnew->entityAt(i)->getEndpoint());
// RS_Vector vp1(pnew->entityAt(i+1)->getStartpoint());
// double a0(intersections.at(i).angleTo(vp0));
// double a1(intersections.at(i).angleTo(vp1));
RS_VectorSolutions sol1;
//This lead result isn't connected.
//for(const RS_Vector& vp: sol0){
// if(!RS_Math::isAngleBetween(intersections.at(i).angleTo(vp),
// pnew->entityAt(i)->getDirection2(),
// pnew->entityAt(i+1)->getDirection1(),
// false)){
// sol1.push_back(vp);
// }
//}
sol1=sol0;
if(sol1.getNumber()==0) continue;
RS_Vector trimP(sol1.getClosest(intersections.at(i)));
static_cast<RS_AtomicEntity*>(en0)->trimEndpoint(trimP);
static_cast<RS_AtomicEntity*>(en1)->trimStartpoint(trimP);
}else{
RS_Vector trimP(sol0.getClosest(intersections.at(i)));
static_cast<RS_AtomicEntity*>(en0)->trimEndpoint(trimP);
static_cast<RS_AtomicEntity*>(en1)->trimStartpoint(trimP);
}
}
*this = *pnew;
return true;
}
void RS_Polyline::move(const RS_Vector& offset) {
RS_EntityContainer::move(offset);
data.startpoint.move(offset);
data.endpoint.move(offset);
calculateBorders();
}
void RS_Polyline::rotate(const RS_Vector& center, const double& angle) {
rotate(center, RS_Vector(angle));
}
void RS_Polyline::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_EntityContainer::rotate(center, angleVector);
data.startpoint.rotate(center, angleVector);
data.endpoint.rotate(center, angleVector);
calculateBorders();
}
void RS_Polyline::scale(const RS_Vector& center, const RS_Vector& factor) {
RS_EntityContainer::scale(center, factor);
data.startpoint.scale(center, factor);
data.endpoint.scale(center, factor);
calculateBorders();
}
void RS_Polyline::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
RS_EntityContainer::mirror(axisPoint1, axisPoint2);
data.startpoint.mirror(axisPoint1, axisPoint2);
data.endpoint.mirror(axisPoint1, axisPoint2);
calculateBorders();
}
void RS_Polyline::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
RS_EntityContainer::moveRef(ref, offset);
if (ref.distanceTo(data.startpoint)<1.0e-4) {
data.startpoint.move(offset);
}
if (ref.distanceTo(data.endpoint)<1.0e-4) {
data.endpoint.move(offset);
}
calculateBorders();
//update();
}
void RS_Polyline::revertDirection() {
RS_EntityContainer::revertDirection();
RS_Vector tmp = data.startpoint;
data.startpoint = data.endpoint;
data.endpoint = tmp;
}
void RS_Polyline::stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) {
if (data.startpoint.isInWindow(firstCorner, secondCorner)) {
data.startpoint.move(offset);
}
if (data.endpoint.isInWindow(firstCorner, secondCorner)) {
data.endpoint.move(offset);
}
RS_EntityContainer::stretch(firstCorner, secondCorner, offset);
calculateBorders();
}
/**
* Slightly optimized drawing for polylines.
*/
void RS_Polyline::draw(RS_Painter* painter,RS_GraphicView* view, double& /*patternOffset*/) {
if (!view) return;
// draw first entity and set correct pen:
RS_Entity* e = firstEntity(RS2::ResolveNone);
// We get the pen from the entitycontainer and apply it to the
// first line so that subsequent line are draw in the right color
//prevent segfault if polyline is empty
if (e) {
RS_Pen p=this->getPen(true);
e->setPen(p);
double patternOffset=0.;
view->drawEntity(painter, e, patternOffset);
e = nextEntity(RS2::ResolveNone);
while(e) {
view->drawEntityPlain(painter, e, patternOffset);
e = nextEntity(RS2::ResolveNone);
}
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Polyline& l) {
os << " Polyline: " << l.getData() << " {\n";
os << (RS_EntityContainer&)l;
os << "\n}\n";
return os;
}

154
lib/engine/rs_polyline.h Normal file
View File

@@ -0,0 +1,154 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_POLYLINE_H
#define RS_POLYLINE_H
#include "rs_entity.h"
#include "rs_entitycontainer.h"
/**
* Holds the data that defines a polyline.
*/
struct RS_PolylineData : public RS_Flags {
RS_PolylineData();
~RS_PolylineData()=default;
RS_PolylineData(const RS_Vector& startpoint,
const RS_Vector& endpoint,
bool closed);
RS_Vector startpoint;
RS_Vector endpoint;
};
std::ostream& operator << (std::ostream& os, const RS_PolylineData& pd);
/**
* Class for a poly line entity (lots of connected lines and arcs).
*
* @author Andrew Mustun
*/
class RS_Polyline : public RS_EntityContainer {
public:
RS_Polyline(RS_EntityContainer* parent=nullptr);
RS_Polyline(RS_EntityContainer* parent,
const RS_PolylineData& d);
RS_Entity* clone() const override;
/** @return RS2::EntityPolyline */
RS2::EntityType rtti() const override{
return RS2::EntityPolyline;
}
/** @return Copy of data that defines the polyline. */
RS_PolylineData getData() const {
return data;
}
/** sets a new start point of the polyline */
void setStartpoint(RS_Vector const& v);
/** @return Start point of the entity */
RS_Vector getStartpoint() const override;
/** sets a new end point of the polyline */
void setEndpoint(RS_Vector const& v);
// set layer for polyline and sub-entities
void setLayer(const QString& name);
void setLayer(RS_Layer* l);
/** @return End point of the entity */
RS_Vector getEndpoint() const override;
double getClosingBulge() const;
void updateEndpoints();
/** @return true if the polyline is closed. false otherwise */
bool isClosed() const;
void setClosed(bool cl);
void setClosed(bool cl, double bulge);//RLZ: rewrite this:
RS_VectorSolutions getRefPoints() const override;
RS_Vector getMiddlePoint(void)const override{
return RS_Vector(false);
}
RS_Vector getNearestRef( const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestSelectedRef( const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Entity* addVertex(const RS_Vector& v,
double bulge=0.0, bool prepend=false);
void appendVertexs(const std::vector< std::pair<RS_Vector, double> >& vl);
void setNextBulge(double bulge) {
nextBulge = bulge;
}
void addEntity(RS_Entity* entity) override;
//void addSegment(RS_Entity* entity) override;
void removeLastVertex();
void endPolyline();
//void reorder() override;
bool offset(const RS_Vector& coord, const double& distance) override;
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
void revertDirection() override;
void draw(RS_Painter* painter, RS_GraphicView* view,
double& patternOffset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Polyline& l);
protected:
RS_Entity* createVertex(const RS_Vector& v,
double bulge=0.0, bool prepend=false);
protected:
RS_PolylineData data;
RS_Entity* closingEntity;
double nextBulge;
};
#endif

290
lib/engine/rs_settings.cpp Normal file
View File

@@ -0,0 +1,290 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
// RVT_PORT changed QSettings s(QSettings::Ini) to QSettings s("./qcad.ini", QSettings::IniFormat);
#include <QSettings>
#include "rs_settings.h"
RS_Settings* RS_Settings::uniqueInstance = nullptr;
bool RS_Settings::save_is_allowed = true;
RS_Settings::RS_Settings():
initialized(false)
{
}
RS_Settings* RS_Settings::instance() {
if (!uniqueInstance) {
uniqueInstance = new RS_Settings();
}
return uniqueInstance;
}
/**
* Initialisation.
*
* @param companyKey String that identifies the company. Must start
* with a "/". E.g. "/RibbonSoft"
* @param appKey String that identifies the application. Must start
* with a "/". E.g. "/LibreCAD"
*/
void RS_Settings::init(const QString& companyKey,
const QString& appKey) {
group = "";
this->companyKey = companyKey;
this->appKey = appKey;
//insertSearchPath(QSettings::Windows, companyKey + appKey);
//insertSearchPath(QSettings::Unix, "/usr/share/");
initialized = true;
}
/**
* Destructor
*/
RS_Settings::~RS_Settings() {
}
void RS_Settings::beginGroup(const QString& group) {
this->group = group;
}
void RS_Settings::endGroup() {
this->group = "";
}
bool RS_Settings::writeEntry(const QString& key, int value) {
return writeEntry(key, QVariant(value));
}
bool RS_Settings::writeEntry(const QString& key,const QString& value) {
return writeEntry(key, QVariant(value));
}
bool RS_Settings::writeEntry(const QString& key, double value) {
return writeEntry(key, QVariant(value));
}
bool RS_Settings::writeEntry(const QString& key, const QVariant& value) {
// Skip writing operations if the key is found in the cache and
// its value is the same as the new one (it was already written).
QVariant ret = readEntryCache(key);
if (ret.isValid() && ret == value) {
return true;
}
QSettings s(companyKey, appKey);
// RVT_PORT not supported anymore s.insertSearchPath(QSettings::Windows, companyKey);
s.setValue(QString("%1%2").arg(group).arg(key), value);
cache[group+key]=value;
return true;
}
bool RS_Settings::writeEntry(const QString& key, const QMap<QString,double> value)
{
if(value.empty())
{
removeEntry(key);
return true;
}
QStringList textList = value.keys();
int size = value.size();
QMap<QString,QVariant> temp;
for(auto i=0;i<size;i++)
{
temp[textList.at(i)] = QVariant(value[textList.at(i)]);
}
return writeEntry(key, QVariant(temp));
}
QString RS_Settings::readEntry(const QString& key,
const QString& def,
bool* ok) {
// lookup:
QVariant ret = readEntryCache(group+key);
if (!ret.isValid()) {
QSettings s(companyKey, appKey);
// RVT_PORT not supported anymore s.insertSearchPath(QSettings::Windows, companyKey);
if (ok) {
*ok=s.contains(QString("%1%2").arg(group).arg(key));
}
ret = s.value(QString("%1%2").arg(group).arg(key), QVariant(def));
cache[group+key]=ret;
}
return ret.toString();
}
QByteArray RS_Settings::readByteArrayEntry(const QString& key,
const QString& def,
bool* ok) {
QVariant ret = readEntryCache(group+key);
if (!ret.isValid()) {
QSettings s(companyKey, appKey);
// RVT_PORT not supported anymore s.insertSearchPath(QSettings::Windows, companyKey);
if (ok) {
*ok=s.contains(QString("%1%2").arg(group).arg(key));
}
ret = s.value(QString("%1%2").arg(group).arg(key), QVariant(def));
cache[group+key]=ret;
}
return ret.toByteArray();
}
int RS_Settings::readNumEntry(const QString& key, int def)
{
QVariant value = readEntryCache(key);
if (!value.isValid())
{
QSettings s(companyKey, appKey);
QString str = QString("%1%2").arg(group).arg(key);
// qDebug() << str;
value = s.value(str, QVariant(def));
cache[group+key] = value;
}
return value.toInt();
}
double RS_Settings::readNumDEntry(const QString& key, double def)
{
QVariant value = readEntryCache(group+key);
if (!value.isValid())
{
QSettings s(companyKey, appKey);
QString str = QString("%1%2").arg(group).arg(key);
// qDebug() << str;
value = s.value(str, QVariant(def));
cache[group+key] = value;
}
return value.toDouble();
}
QVariant RS_Settings::readEntryCache(const QString& key) {
if(!cache.count(key)) return QVariant();
return cache[group+key];
}
QMap<QString,QVariant> RS_Settings::readQMapQStringQVariantEntry(const QString& key,QMap<QString,QVariant> def)
{
QVariant value = readEntryCache(group+key);
if (!value.isValid())
{
QSettings s(companyKey, appKey);
QString str = QString("%1%2").arg(group).arg(key);
// qDebug() << str;
value = s.value(str, QVariant::fromValue(def));
cache[group+key] = value;
}
return value.value<QMap<QString,QVariant>>();
}
QMap<QString,double> RS_Settings::readQMapQStringDoubleEntry(const QString& key,QMap<QString,double> def)
{
QVariant value = readEntryCache(group+key);
if (!value.isValid())
{
QSettings s(companyKey, appKey);
QString str = QString("%1%2").arg(group).arg(key);
// qDebug() << str;
value = s.value(str, QVariant::fromValue(def));
cache[group+key] = value;
}
QMap<QString,QVariant> temp = value.value<QMap<QString,QVariant>>();
QStringList textList = temp.keys();
int size = temp.size();
QMap<QString,double> temp2;
for(auto i=0;i<size;i++)
{
temp2[textList[i]] = temp[textList[i]].toDouble();
}
return temp2;
}
QStringList RS_Settings::readQListQStringEntry(const QString& key,QStringList def)
{
QVariant value = readEntryCache(group+key);
if (!value.isValid())
{
QSettings s(companyKey, appKey);
QString str = QString("%1%2").arg(group).arg(key);
// qDebug() << str;
value = s.value(str, QVariant::fromValue(def));
cache[group+key] = value;
}
QStringList reValue = value.value<QStringList>();
return reValue;
}
void RS_Settings::addToCache(const QString& key, const QVariant& value) {
cache[group+key]=value;
}
void RS_Settings::clear_all()
{
QSettings s(companyKey, appKey);
s.clear();
save_is_allowed = false;
}
void RS_Settings::clear_geometry()
{
QSettings s(companyKey, appKey);
s.remove("/Geometry");
save_is_allowed = false;
}
void RS_Settings::removeEntry(QString value)
{
if(cache.count(value)) cache.erase(value);
QSettings s(companyKey, appKey);
s.remove(group+value);
}

124
lib/engine/rs_settings.h Normal file
View File

@@ -0,0 +1,124 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_SETTINGS_H
#define RS_SETTINGS_H
#include <QString>
#include <map>
class QVariant;
// ---------------------------------------------------------------------------
// Default Settings
// ---------------------------------------------------------------------------
namespace Colors
{
const QString snap_indicator = "#FFC200";
const QString background = "Black";
const QString grid = "Gray";
const QString meta_grid = "#404040";
const QString select = "#A54747";
const QString highlight = "#739373";
const QString start_handle = "Cyan";
const QString handle = "Blue";
const QString end_handle = "Blue";
}
// ---------------------------------------------------------------------------
#define RS_SETTINGS RS_Settings::instance()
/**
* This class can store and reload settings from a
* configuration file or the windoze registry.
* Please note that the Qt default implementation doesn't
* work as one would expect. That's why this class overwrites
* most of the default behaviour.
*
*/
class RS_Settings {
public:
~RS_Settings();
/**
* @return Instance to the unique settings object.
*/
static RS_Settings* instance();
/**
* Initialize the system.
*
* @param companyKey Company Key
* @param appKey Application key
*/
void init(const QString& companyKey, const QString& appKey);
void beginGroup(const QString& group);
void endGroup();
bool writeEntry(const QString& key, int value);
bool writeEntry(const QString& key, double value);
bool writeEntry(const QString& key, const QVariant& value);
bool writeEntry(const QString& key, const QString& value);
bool writeEntry(const QString& key, const QMap<QString,double> value);
QString readEntry(const QString& key,
const QString& def = QString(),
bool* ok = 0);
QByteArray readByteArrayEntry(const QString& key,
const QString& def = QString(),
bool* ok = 0);
int readNumEntry(const QString& key, int def=0);
double readNumDEntry(const QString& key, double def=0.0000);
QMap<QString,QVariant> readQMapQStringQVariantEntry(const QString& key,QMap<QString,QVariant> def);
QMap<QString,double> readQMapQStringDoubleEntry(const QString& key,QMap<QString,double> def);
QStringList readQListQStringEntry(const QString& key,QStringList def);
void clear_all();
void clear_geometry();
void removeEntry(QString value);
static bool save_is_allowed;
private:
RS_Settings();
RS_Settings(RS_Settings const&) = delete;
RS_Settings& operator = (RS_Settings const&) = delete;
QVariant readEntryCache(const QString& key);
void addToCache(const QString& key, const QVariant& value);
protected:
static RS_Settings* uniqueInstance;
// 存方被读取过的键值
std::map<QString, QVariant> cache;
QString companyKey;
QString appKey;
QString group;
bool initialized;
};
#endif

482
lib/engine/rs_solid.cpp Normal file
View File

@@ -0,0 +1,482 @@
/****************************************************************************
**
** 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_solid.h"
#include "rs_line.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
#include "rs_information.h"
#include "rs_debug.h"
RS_SolidData::RS_SolidData():
corner{{RS_Vector(false), RS_Vector(false), RS_Vector(false), RS_Vector(false)}}
{
}
/**
* Constructor for a solid with 3 corners.
*/
RS_SolidData::RS_SolidData(const RS_Vector& corner1,
const RS_Vector& corner2,
const RS_Vector& corner3):
corner{{corner1, corner2, corner3, RS_Vector(false)}}
{
}
/**
* Constructor for a solid with 4 corners.
*/
RS_SolidData::RS_SolidData(const RS_Vector& corner1,
const RS_Vector& corner2,
const RS_Vector& corner3,
const RS_Vector& corner4):
corner{{corner1, corner2, corner3, corner4}}
{
}
std::ostream& operator << (std::ostream& os,
const RS_SolidData& pd)
{
os << "(";
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; i++) {
os << pd.corner[i];
}
os << ")";
return os;
}
/**
* Default constructor.
*/
RS_Solid::RS_Solid(RS_EntityContainer* parent,
const RS_SolidData& d) :
RS_AtomicEntity(parent),
data(d)
{
calculateBorders();
}
RS_Entity* RS_Solid::clone() const
{
RS_Solid* s = new RS_Solid(*this);
s->initId();
return s;
}
/**
* @return Corner number 'num'.
*/
RS_Vector RS_Solid::getCorner(int num) const
{
if (num >= RS_SolidData::FirstCorner && num < RS_SolidData::MaxCorners) {
return data.corner[num];
}
RS_DEBUG->print(RS_Debug::D_WARNING, "Illegal corner requested from Solid");
return RS_Vector(false);
}
/**
* Shapes this Solid into a standard arrow (used in dimensions).
*
* @param point The point the arrow points to.
* @param angle Direction of the arrow.
* @param arrowSize Size of arrow (length).
*/
void RS_Solid::shapeArrow(const RS_Vector& point,
double angle,
double arrowSize)
{
double arrowSide {arrowSize / cos(0.165)};
double cosv1 {cos( angle + 0.165) * arrowSide};
double sinv1 {sin( angle + 0.165) * arrowSide};
double cosv2 {cos( angle - 0.165) * arrowSide};
double sinv2 {sin( angle - 0.165) * arrowSide};
data.corner[0] = point;
data.corner[1] = RS_Vector(point.x - cosv1, point.y - sinv1);
data.corner[2] = RS_Vector(point.x - cosv2, point.y - sinv2);
data.corner[3] = RS_Vector(false);
calculateBorders();
}
void RS_Solid::calculateBorders()
{
resetBorders();
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
minV = RS_Vector::minimum( minV, data.corner[i]);
maxV = RS_Vector::maximum( maxV, data.corner[i]);
}
}
}
RS_Vector RS_Solid::getNearestEndpoint(const RS_Vector& coord, double* dist /*= nullptr*/)const
{
double minDist {RS_MAXDOUBLE};
double curDist {0.0};
RS_Vector ret;
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
curDist = data.corner[i].distanceTo(coord);
if (curDist < minDist) {
ret = data.corner[i];
minDist = curDist;
}
}
}
setDistPtr( dist, minDist);
return ret;
}
bool RS_Solid::isInCrossWindow(const RS_Vector& v1, const RS_Vector& v2) const
{
RS_Vector vBL;
RS_Vector vTR;
RS_VectorSolutions sol;
//sort input vectors to BottomLeft & TopRight
if (v1.x < v2.x) {
vBL.x = v1.x;
vTR.x = v2.x;
}
else {
vBL.x = v2.x;
vTR.x = v1.x;
}
if (v1.y < v2.y) {
vBL.y = v1.y;
vTR.y = v2.y;
}
else {
vBL.y = v2.y;
vTR.y = v1.y;
}
//Check if entity is out of window
if (getMin().x > vTR.x
|| getMax().x < vBL.x
|| getMin().y > vTR.y
|| getMax().y < vBL.y) {
return false;
}
std::vector<RS_Line> border;
border.emplace_back( data.corner[0], data.corner[1]);
border.emplace_back( data.corner[1], data.corner[2]);
if (isTriangle()) {
border.emplace_back( data.corner[2], data.corner[0]);
}
else {
border.emplace_back( data.corner[2], data.corner[3]);
border.emplace_back( data.corner[3], data.corner[0]);
}
//Find crossing edge
if (getMax().x > vBL.x
&& getMin().x < vBL.x) { //left
RS_Line edge {vBL, {vBL.x, vTR.y}};
for (auto const& line: border) {
sol = RS_Information::getIntersection(&edge, &line, true);
if (sol.hasValid()) {
return true;
}
}
}
if (getMax().x > vTR.x
&& getMin().x < vTR.x) { //right
RS_Line edge {{vTR.x, vBL.y}, vTR};
for (auto const& line: border) {
sol = RS_Information::getIntersection(&edge, &line, true);
if (sol.hasValid()) {
return true;
}
}
}
if (getMax().y > vBL.y
&& getMin().y < vBL.y) { //bottom
RS_Line edge {vBL, {vTR.x, vBL.y}};
for(auto const& line: border) {
sol = RS_Information::getIntersection(&edge, &line, true);
if (sol.hasValid()) {
return true;
}
}
}
if(getMax().y > vTR.y
&& getMin().y < vTR.y) { //top
RS_Line edge {{vBL.x, vTR.y}, vTR};
for(auto const& line: border) {
sol = RS_Information::getIntersection(&edge, &line, true);
if (sol.hasValid()) {
return true;
}
}
}
return false;
}
/**
*
* @return true if positive o zero, false if negative.
*/
bool RS_Solid::sign (const RS_Vector& v1, const RS_Vector& v2, const RS_Vector& v3) const
{
double res = (v1.x - v3.x) * (v2.y - v3.y) - (v2.x - v3.x) * (v1.y - v3.y);
return (res >= 0.0);
}
/**
* @todo Implement this.
*/
RS_Vector RS_Solid::getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity /*= true*/,
double* dist /*= nullptr*/,
RS_Entity** entity /*= nullptr*/) const
{
//first check if point is inside solid
bool s1 {sign(data.corner[0], data.corner[1], coord)};
bool s2 {sign(data.corner[1], data.corner[2], coord)};
bool s3 {sign(data.corner[2], data.corner[0], coord)};
if ((s1 == s2) && (s2 == s3)) {
setDistPtr( dist, 0.0);
return coord;
}
if (!isTriangle()) {
s1 = sign(data.corner[0], data.corner[2], coord);
s2 = sign(data.corner[2], data.corner[3], coord);
s3 = sign(data.corner[3], data.corner[0], coord);
if ((s1 == s2) && (s2 == s3)) {
setDistPtr( dist, 0.0);
return coord;
}
}
// not inside of solid
// Find nearest distance from each edge
if (nullptr != entity) {
*entity = const_cast<RS_Solid*>(this);
}
RS_Vector ret(false);
double currDist {RS_MAXDOUBLE};
double tmpDist {0.0};
int totalV {isTriangle() ? RS_SolidData::Triangle : RS_SolidData::MaxCorners};
for (int i = RS_SolidData::FirstCorner, next = i + 1; i <= totalV; ++i, ++next) {
//closing edge
if (next == totalV) {
next = RS_SolidData::FirstCorner;
}
RS_Vector direction {data.corner[next] - data.corner[i]};
RS_Vector vpc {coord-data.corner[i]};
double a {direction.squared()};
if( a < RS_TOLERANCE2) {
//line too short
vpc = data.corner[i];
}
else{
//find projection on line
vpc = data.corner[i] + direction * RS_Vector::dotP( vpc, direction) / a;
}
tmpDist = vpc.distanceTo( coord);
if (tmpDist < currDist) {
currDist = tmpDist;
ret = vpc;
}
}
//verify this part
if (onEntity && !ret.isInWindowOrdered( minV, maxV)) {
// projection point not within range, find the nearest endpoint
ret = getNearestEndpoint( coord, dist);
currDist = ret.distanceTo( coord);
}
setDistPtr( dist, currDist);
return ret;
}
RS_Vector RS_Solid::getNearestCenter(const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
Q_UNUSED( coord)
setDistPtr( dist, RS_MAXDOUBLE);
return RS_Vector(false);
}
RS_Vector RS_Solid::getNearestMiddle(const RS_Vector& coord,
double* dist /*= nullptr*/,
const int middlePoints /*= 1*/) const
{
Q_UNUSED( coord)
Q_UNUSED( middlePoints)
setDistPtr( dist, RS_MAXDOUBLE);
return RS_Vector(false);
}
RS_Vector RS_Solid::getNearestDist(double distance,
const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
Q_UNUSED( distance)
Q_UNUSED( coord)
setDistPtr( dist, RS_MAXDOUBLE);
return RS_Vector(false);
}
/**
* @return Distance from one of the boundary lines of this solid to given point.
*
*/
double RS_Solid::getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity /*= nullptr*/,
RS2::ResolveLevel level /*= RS2::ResolveNone*/,
double solidDist /*= RS_MAXDOUBLE*/) const
{
Q_UNUSED( level)
Q_UNUSED( solidDist)
if (nullptr != entity) {
*entity = const_cast<RS_Solid*>(this);
}
double ret {0.0};
getNearestPointOnEntity( coord, true, &ret, entity);
return ret;
}
void RS_Solid::move(const RS_Vector& offset)
{
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].move(offset);
}
}
calculateBorders();
}
void RS_Solid::rotate(const RS_Vector& center, const double& angle)
{
RS_Vector angleVector(angle);
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].rotate( center, angleVector);
}
}
calculateBorders();
}
void RS_Solid::rotate(const RS_Vector& center, const RS_Vector& angleVector)
{
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].rotate( center, angleVector);
}
}
calculateBorders();
}
void RS_Solid::scale(const RS_Vector& center, const RS_Vector& factor)
{
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].scale( center, factor);
}
}
calculateBorders();
}
void RS_Solid::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2)
{
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].mirror( axisPoint1, axisPoint2);
}
}
calculateBorders();
}
void RS_Solid::draw(RS_Painter* painter,
RS_GraphicView* view,
double& patternOffset)
{
Q_UNUSED( patternOffset)
if (nullptr == painter
|| nullptr == view) {
return;
}
painter->fillTriangle( view->toGui(getCorner(0)),
view->toGui(getCorner(1)),
view->toGui(getCorner(2)));
if (!isTriangle()) {
painter->fillTriangle( view->toGui(getCorner(1)),
view->toGui(getCorner(2)),
view->toGui(getCorner(3)));
}
}
void RS_Solid::setDistPtr(double *dist, const double value) const
{
if (nullptr != dist) {
*dist = value;
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Solid& p)
{
os << " Solid: " << p.getData() << "\n";
return os;
}

157
lib/engine/rs_solid.h Normal file
View File

@@ -0,0 +1,157 @@
/****************************************************************************
**
** 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!
**
**********************************************************************/
#ifndef RS_SOLID_H
#define RS_SOLID_H
#include <array>
#include "rs_atomicentity.h"
#include "rs_vector.h"
/**
* Holds the data that defines a solid.
*/
struct RS_SolidData
{
/**
* Default constructor. Leaves the data object uninitialized.
*/
RS_SolidData();
~RS_SolidData() = default;
/**
* Constructor for a solid with 3 corners.
*/
RS_SolidData(const RS_Vector& corner1,
const RS_Vector& corner2,
const RS_Vector& corner3);
/**
* Constructor for a solid with 4 corners.
*/
RS_SolidData(const RS_Vector& corner1,
const RS_Vector& corner2,
const RS_Vector& corner3,
const RS_Vector& corner4);
enum Corners {
FirstCorner = 0,
Triangle = 3,
MaxCorners
};
std::array<RS_Vector, MaxCorners> corner;
};
std::ostream& operator << (std::ostream& os, const RS_SolidData& pd);
/**
* Class for a solid entity (e.g. dimension arrows).
*
* @author Andrew Mustun
*/
class RS_Solid : public RS_AtomicEntity
{
public:
RS_Solid(RS_EntityContainer* parent,
const RS_SolidData& d);
RS_Entity* clone() const override;
/** @return RS_ENTITY_POINT */
RS2::EntityType rtti() const override
{
return RS2::EntitySolid;
}
/** @return Copy of data that defines the point. */
RS_SolidData const& getData() const
{
return data;
}
/** @return true if this is a triangle. */
bool isTriangle() const
{
return !data.corner[3].valid;
}
RS_Vector getCorner(int num) const;
void shapeArrow(const RS_Vector& point,
double angle,
double arrowSize);
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity = true,
double* dist = nullptr,
RS_Entity** entity = nullptr) const override;
RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = nullptr) const override;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1) const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = nullptr) const override;
double getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity = nullptr,
RS2::ResolveLevel level = RS2::ResolveNone,
double solidDist = RS_MAXDOUBLE) const override;
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Solid& p);
/** Recalculates the borders of this entity. */
void calculateBorders() override;
/** Check if is intersected by v1, v2 window.
* @return true if is crossed false otherwise.
**/
bool isInCrossWindow(const RS_Vector& v1, const RS_Vector& v2) const;
protected:
RS_SolidData data;
private:
//helper method for getNearestPointOnEntity
bool sign (const RS_Vector& v1, const RS_Vector& v2, const RS_Vector& v3) const;
void setDistPtr(double *dist, const double value) const;
};
#endif

636
lib/engine/rs_spline.cpp Normal file
View File

@@ -0,0 +1,636 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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<numeric>
#include "rs_spline.h"
#include "rs_line.h"
#include "rs_debug.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
#include "rs_graphic.h"
RS_SplineData::RS_SplineData(int _degree, bool _closed):
degree(_degree)
,closed(_closed)
{
}
std::ostream& operator << (std::ostream& os, const RS_SplineData& ld) {
os << "( degree: " << ld.degree <<
" closed: " << ld.closed;
if (ld.controlPoints.size()) {
os << "\n(control points:\n";
for (auto const& v: ld.controlPoints)
os<<v;
os<<")\n";
}
if (ld.knotslist.size()) {
os << "\n(knot vector:\n";
for (auto const& v: ld.knotslist)
os<<v;
os<<")\n";
}
os << ")";
return os;
}
/**
* Constructor.
*/
RS_Spline::RS_Spline(RS_EntityContainer* parent,
const RS_SplineData& d)
:RS_EntityContainer(parent), data(d) {
calculateBorders();
}
RS_Entity* RS_Spline::clone() const{
RS_Spline* l = new RS_Spline(*this);
l->setOwner(isOwner());
l->initId();
l->detach();
return l;
}
void RS_Spline::calculateBorders() {
/*minV = RS_Vector::minimum(data.startpoint, data.endpoint);
maxV = RS_Vector::maximum(data.startpoint, data.endpoint);
QList<RS_Vector>::iterator it;
for (it = data.controlPoints.begin();
it!=data.controlPoints.end(); ++it) {
minV = RS_Vector::minimum(*it, minV);
maxV = RS_Vector::maximum(*it, maxV);
}
*/
}
void RS_Spline::setDegree(size_t deg) {
if (deg>=1 && deg<=3) {
data.degree = deg;
}
}
/** @return Degree of this spline curve (1-3).*/
size_t RS_Spline::getDegree() const{
return data.degree;
}
size_t RS_Spline::getNumberOfControlPoints() const {
return data.controlPoints.size();
}
/**
* @retval true if the spline is closed.
* @retval false otherwise.
*/
bool RS_Spline::isClosed() const {
return data.closed;
}
/**
* Sets the closed flag of this spline.
*/
void RS_Spline::setClosed(bool c) {
data.closed = c;
update();
}
RS_VectorSolutions RS_Spline::getRefPoints() const
{
return RS_VectorSolutions(data.controlPoints);
}
RS_Vector RS_Spline::getNearestRef( const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
// override the RS_EntityContainer method
// use RS_Entity instead for spline point dragging
return RS_Entity::getNearestRef(coord, dist);
}
RS_Vector RS_Spline::getNearestSelectedRef( const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
// override the RS_EntityContainer method
// use RS_Entity instead for spline point dragging
return RS_Entity::getNearestSelectedRef(coord, dist);
}
/**
* Updates the internal polygon of this spline. Called when the
* spline or it's data, position, .. changes.
*/
void RS_Spline::update() {
RS_DEBUG->print("RS_Spline::update");
clear();
if (isUndone()) {
return;
}
if (data.degree<1 || data.degree>3) {
RS_DEBUG->print("RS_Spline::update: invalid degree: %d", data.degree);
return;
}
if (data.controlPoints.size() < data.degree+1) {
RS_DEBUG->print("RS_Spline::update: not enough control points");
return;
}
resetBorders();
std::vector<RS_Vector> tControlPoints = data.controlPoints;
if (data.closed) {
for (size_t i=0; i<data.degree; ++i) {
tControlPoints.push_back(data.controlPoints.at(i));
}
}
const size_t npts = tControlPoints.size();
// order:
const size_t k = data.degree+1;
// resolution:
const size_t p1 = getGraphicVariableInt("$SPLINESEGS", 8) * npts;
std::vector<double> h(npts+1, 1.);
std::vector<RS_Vector> p(p1, {0., 0.});
if (data.closed) {
rbsplinu(npts,k,p1,tControlPoints,h,p);
} else {
rbspline(npts,k,p1,tControlPoints,h,p);
}
RS_Vector prev{};
for (auto const& vp: p) {
if (prev.valid) {
RS_Line* line = new RS_Line{this, prev, vp};
line->setLayer(nullptr);
line->setPen(RS2::FlagInvalid);
addEntity(line);
}
prev = vp;
minV = RS_Vector::minimum(prev, minV);
maxV = RS_Vector::maximum(prev, maxV);
}
}
RS_Vector RS_Spline::getStartpoint() const {
if (data.closed) return RS_Vector(false);
return static_cast<RS_Line*>(const_cast<RS_Spline*>(this)->firstEntity())->getStartpoint();
}
RS_Vector RS_Spline::getEndpoint() const {
if (data.closed) return RS_Vector(false);
return static_cast<RS_Line*>(const_cast<RS_Spline*>(this)->lastEntity())->getEndpoint();
}
RS_Vector RS_Spline::getNearestEndpoint(const RS_Vector& coord,
double* dist)const {
double minDist = RS_MAXDOUBLE;
RS_Vector ret(false);
if(! data.closed) { // no endpoint for closed spline
RS_Vector vp1(getStartpoint());
RS_Vector vp2(getEndpoint());
double d1( (coord-vp1).squared());
double d2( (coord-vp2).squared());
if( d1<d2){
ret=vp1;
minDist=sqrt(d1);
}else{
ret=vp2;
minDist=sqrt(d2);
}
// for (int i=0; i<data.controlPoints.count(); i++) {
// d = (data.controlPoints.at(i)).distanceTo(coord);
// if (d<minDist) {
// minDist = d;
// ret = data.controlPoints.at(i);
// }
// }
}
if (dist) {
*dist = minDist;
}
return ret;
}
/*
// The default implementation of RS_EntityContainer is inaccurate but
// has to do for now..
RS_Vector RS_Spline::getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity, double* dist, RS_Entity** entity) {
}
*/
RS_Vector RS_Spline::getNearestCenter(const RS_Vector& /*coord*/,
double* dist) const{
if (dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
RS_Vector RS_Spline::getNearestMiddle(const RS_Vector& /*coord*/,
double* dist,
int /*middlePoints*/)const {
if (dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
RS_Vector RS_Spline::getNearestDist(double /*distance*/,
const RS_Vector& /*coord*/,
double* dist) const{
if (dist) {
*dist = RS_MAXDOUBLE;
}
return RS_Vector(false);
}
void RS_Spline::move(const RS_Vector& offset) {
RS_EntityContainer::move(offset);
for (RS_Vector& vp: data.controlPoints) {
vp.move(offset);
}
// update();
}
void RS_Spline::rotate(const RS_Vector& center, const double& angle) {
rotate(center,RS_Vector(angle));
}
void RS_Spline::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_EntityContainer::rotate(center, angleVector);
for (RS_Vector& vp: data.controlPoints) {
vp.rotate(center, angleVector);
}
// update();
}
void RS_Spline::scale(const RS_Vector& center, const RS_Vector& factor) {
for (RS_Vector& vp: data.controlPoints) {
vp.scale(center, factor);
}
update();
}
void RS_Spline::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
for (RS_Vector& vp: data.controlPoints) {
vp.mirror(axisPoint1, axisPoint2);
}
update();
}
void RS_Spline::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
for (RS_Vector& vp: data.controlPoints) {
if (ref.distanceTo(vp)<1.0e-4) {
vp.move(offset);
}
}
update();
}
void RS_Spline::revertDirection() {
std::reverse(data.controlPoints.begin(), data.controlPoints.end());
}
void RS_Spline::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) {
if (!(painter && view)) {
return;
}
RS_Entity* e=firstEntity(RS2::ResolveNone);
if (e) {
RS_Pen p=this->getPen(true);
e->setPen(p);
double patternOffset(0.0);
view->drawEntity(painter, e, patternOffset);
//RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
e = nextEntity(RS2::ResolveNone);
while(e) {
view->drawEntityPlain(painter, e, patternOffset);
e = nextEntity(RS2::ResolveNone);
//RS_DEBUG->print("offset: %f\nlength was: %f", offset, e->getLength());
}
}
}
/**
* Todo: draw the spline, user patterns.
*/
/*
void RS_Spline::draw(RS_Painter* painter, RS_GraphicView* view) {
if (!(painter && view)) {
return;
}
/ *
if (data.controlPoints.count()>0) {
RS_Vector prev(false);
QList<RS_Vector>::iterator it;
for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
if (prev.valid) {
painter->drawLine(view->toGui(prev),
view->toGui(*it));
}
prev = (*it);
}
}
* /
int i;
int npts = data.controlPoints.count();
// order:
int k = 4;
// resolution:
int p1 = 100;
double* b = new double[npts*3+1];
double* h = new double[npts+1];
double* p = new double[p1*3+1];
QList<RS_Vector>::iterator it;
i = 1;
for (it = data.controlPoints.begin(); it!=data.controlPoints.end(); ++it) {
b[i] = (*it).x;
b[i+1] = (*it).y;
b[i+2] = 0.0;
RS_DEBUG->print("RS_Spline::draw: b[%d]: %f/%f", i, b[i], b[i+1]);
i+=3;
}
// set all homogeneous weighting factors to 1.0
for (i=1; i <= npts; i++) {
h[i] = 1.0;
}
//
for (i = 1; i <= 3*p1; i++) {
p[i] = 0.0;
}
rbspline(npts,k,p1,b,h,p);
RS_Vector prev(false);
for (i = 1; i <= 3*p1; i=i+3) {
if (prev.valid) {
painter->drawLine(view->toGui(prev),
view->toGui(RS_Vector(p[i], p[i+1])));
}
prev = RS_Vector(p[i], p[i+1]);
}
}
*/
/**
* @return The reference points of the spline.
*/
const std::vector<RS_Vector>& RS_Spline::getControlPoints() const{
return data.controlPoints;
}
/**
* Appends the given point to the control points.
*/
void RS_Spline::addControlPoint(const RS_Vector& v) {
data.controlPoints.push_back(v);
}
/**
* Removes the control point that was last added.
*/
void RS_Spline::removeLastControlPoint() {
data.controlPoints.pop_back();
}
//TODO: private interface cleanup; de Boor's Algorithm
/**
* Generates B-Spline open knot vector with multiplicity
* equal to the order at the ends.
*/
std::vector<double> RS_Spline::knot(size_t num, size_t order) const{
if (data.knotslist.size() == num + order) {
//use custom knot vector
return data.knotslist;
}
std::vector<double> knotVector(num + order, 0.);
//use uniform knots
std::iota(knotVector.begin() + order, knotVector.begin() + num + 1, 1);
std::fill(knotVector.begin() + num + 1, knotVector.end(), knotVector[num]);
return knotVector;
}
/**
* Generates rational B-spline basis functions for an open knot vector.
*/
namespace{
std::vector<double> rbasis(int c, double t, int npts,
const std::vector<double>& x,
const std::vector<double>& h) {
int const nplusc = npts + c;
std::vector<double> temp(nplusc,0.);
// calculate the first order nonrational basis functions n[i]
for (int i = 0; i< nplusc-1; i++)
if ((t >= x[i]) && (t < x[i+1])) temp[i] = 1;
/* calculate the higher order nonrational basis functions */
for (int k = 2; k <= c; k++) {
for (int i = 0; i < nplusc-k; i++) {
// if the lower order basis function is zero skip the calculation
if (temp[i] != 0)
temp[i] = ((t-x[i])*temp[i])/(x[i+k-1]-x[i]);
// if the lower order basis function is zero skip the calculation
if (temp[i+1] != 0)
temp[i] += ((x[i+k]-t)*temp[i+1])/(x[i+k]-x[i+1]);
}
}
// pick up last point
if (t >= x[nplusc-1]) temp[npts-1] = 1;
// calculate sum for denominator of rational basis functions
double sum = 0.;
for (int i = 0; i < npts; i++) {
sum += temp[i]*h[i];
}
std::vector<double> r(npts, 0);
// form rational basis functions and put in r vector
if (sum != 0) {
for (int i = 0; i < npts; i++)
r[i] = (temp[i]*h[i])/sum;
}
return r;
}
}
/**
* Generates a rational B-spline curve using a uniform open knot vector.
*/
void RS_Spline::rbspline(size_t npts, size_t k, size_t p1,
const std::vector<RS_Vector>& b,
const std::vector<double>& h,
std::vector<RS_Vector>& p) const{
size_t const nplusc = npts + k;
// generate the open knot vector
auto const x = knot(npts, k);
// calculate the points on the rational B-spline curve
double t {x[0]};
double const step {(x[nplusc-1] - t) / (p1-1)};
for (auto& vp: p) {
if (x[nplusc-1] - t < 5e-6) t = x[nplusc-1];
// generate the basis function for this value of t
auto const nbasis = rbasis(k, t, npts, x, h);
// generate a point on the curve
for (size_t i = 0; i < npts; i++)
vp += b[i] * nbasis[i];
t += step;
}
}
std::vector<double> RS_Spline::knotu(size_t num, size_t order) const{
if (data.knotslist.size() == num + order) {
//use custom knot vector
return data.knotslist;
}
std::vector<double> knotVector(num + order, 0.);
std::iota(knotVector.begin(), knotVector.end(), 0);
return knotVector;
}
void RS_Spline::rbsplinu(size_t npts, size_t k, size_t p1,
const std::vector<RS_Vector>& b,
const std::vector<double>& h,
std::vector<RS_Vector>& p) const{
size_t const nplusc = npts + k;
/* generate the periodic knot vector */
std::vector<double> const x = knotu(npts, k);
/* calculate the points on the rational B-spline curve */
double t = k-1;
double const step = double(npts - k + 1)/(p1 - 1);
for (auto& vp: p) {
if (x[nplusc-1] - t < 5e-6) t = x[nplusc-1];
/* generate the basis function for this value of t */
auto const nbasis = rbasis(k, t, npts, x, h);
/* generate a point on the curve, for x, y, z */
for (size_t i = 0; i < npts; i++)
vp += b[i] * nbasis[i];
t += step;
}
}
/**
* Dumps the spline's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Spline& l) {
os << " Spline: " << l.getData() << "\n";
return os;
}

171
lib/engine/rs_spline.h Normal file
View File

@@ -0,0 +1,171 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_SPLINE_H
#define RS_SPLINE_H
#include <vector>
#include "rs_entitycontainer.h"
/**
* Holds the data that defines a line.
*/
struct RS_SplineData {
/**
* Default constructor. Leaves the data object uninitialized.
*/
RS_SplineData() = default;
RS_SplineData(int degree, bool closed);
/** Degree of the spline (1, 2, 3) */
size_t degree;
/** Closed flag. */
bool closed;
/** Control points of the spline. */
std::vector<RS_Vector> controlPoints;
std::vector<double> knotslist;
};
std::ostream& operator << (std::ostream& os, const RS_SplineData& ld);
/**
* Class for a spline entity.
*
* @author Andrew Mustun
*/
class RS_Spline : public RS_EntityContainer {
public:
RS_Spline(RS_EntityContainer* parent,
const RS_SplineData& d);
RS_Entity* clone() const override;
/** @return RS2::EntitySpline */
RS2::EntityType rtti() const override{
return RS2::EntitySpline;
}
/** @return false */
bool isEdge() const override{
return false;
}
/** @return Copy of data that defines the spline. */
const RS_SplineData& getData() const {
return data;
}
/** Sets the splines degree (1-3). */
void setDegree(size_t deg);
/** @return Degree of this spline curve (1-3).*/
size_t getDegree() const;
/** @return 0. */
int getNumberOfKnots() {
return 0;
}
/** @return Number of control points. */
size_t getNumberOfControlPoints() const;
/**
* @retval true if the spline is closed.
* @retval false otherwise.
*/
bool isClosed() const;
/**
* Sets the closed flag of this spline.
*/
void setClosed(bool c);
RS_VectorSolutions getRefPoints() const override;
RS_Vector getNearestRef( const RS_Vector& coord, double* dist = nullptr) const override;
RS_Vector getNearestSelectedRef( const RS_Vector& coord, double* dist = nullptr) const override;
/** @return Start point of the entity */
RS_Vector getStartpoint() const override;
/** @return End point of the entity */
RS_Vector getEndpoint() const override;
/** Sets the startpoint */
/** Sets the endpoint */
void update() override;
RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = nullptr)const override;
//RS_Vector getNearestPointOnEntity(const RS_Vector& coord,
// bool onEntity=true, double* dist = nullptr, RS_Entity** entity=nullptr);
RS_Vector getNearestCenter(const RS_Vector& coord,
double* dist = nullptr)const override;
RS_Vector getNearestMiddle(const RS_Vector& coord,
double* dist = nullptr,
int middlePoints = 1)const override;
RS_Vector getNearestDist(double distance,
const RS_Vector& coord,
double* dist = nullptr)const override;
void addControlPoint(const RS_Vector& v);
void removeLastControlPoint();
void move(const RS_Vector& offset) override;
void rotate(const RS_Vector& center, const double& angle) override;
void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
void scale(const RS_Vector& center, const RS_Vector& factor) override;
void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
void moveRef(const RS_Vector& ref, const RS_Vector& offset) override;
void revertDirection() override;
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
const std::vector<RS_Vector>& getControlPoints() const;
friend std::ostream& operator << (std::ostream& os, const RS_Spline& l);
void calculateBorders() override;
private:
std::vector<double> knot(size_t num, size_t order) const;
void rbspline(size_t npts, size_t k, size_t p1,
const std::vector<RS_Vector>& b,
const std::vector<double>& h,
std::vector<RS_Vector>& p) const;
std::vector<double> knotu(size_t num, size_t order) const;
void rbsplinu(size_t npts, size_t k, size_t p1,
const std::vector<RS_Vector>& b,
const std::vector<double>& h,
std::vector<RS_Vector>& p) const;
protected:
RS_SplineData data;
}
;
#endif

941
lib/engine/rs_system.cpp Normal file
View File

@@ -0,0 +1,941 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2021 A. Stebich (librecad@mail.lordofbikes.de)
** 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 <QMap>
#include <QApplication>
#include <QTextCodec>
#include <QTranslator>
#include <QFileInfo>
#include "rs_settings.h"
#include "rs_system.h"
#include "rs.h"
#include "rs_debug.h"
#include <QStandardPaths>
RS_System* RS_System::uniqueInstance = NULL;
/**
* Initializes the system.
*
* @param appName Application name (e.g. "librecad II")
* @param appVersion Application version (e.g. "1.2.3")
* @param appDirName Application directory name used for
* subdirectories in /usr, /etc ~/.
*/
void RS_System::init(const QString& appName,
const QString& appVersion,
const QString& appDirName,
const char *arg0) {
this->appName = appName;
this->appVersion = appVersion;
this->appDirName = appDirName;
if (QFile::decodeName( arg0).contains( "/.mount")) {
// in AppImage QCoreApplication::applicationDirPath() directs to /lib64 of mounted AppImage
// thus use argv[0] to extract the correct path to librecad executable
appDir = QFileInfo( QFile::decodeName( arg0)).absolutePath();
}
else {
// in regular application QCoreApplication::applicationDirPath() is preferred, see GitHub #1488
appDir = QCoreApplication::applicationDirPath();
}
// when appDir is not HOME or CURRENT dir, search appDir too in getDirectoryList()
externalAppDir = (!appDir.isEmpty()
&& "/" != appDir
&& getHomeDir() != appDir
&& getCurrentDir() != appDir);
RS_DEBUG->print("RS_System::init: System %s initialized.", appName.toLatin1().data());
RS_DEBUG->print("RS_System::init: App dir: %s", appDir.toLatin1().data());
initialized = true;
initAllLanguagesList();
initLanguageList();
}
/**
* Initializes the list of available translations.
*/
void RS_System::initLanguageList() {
RS_DEBUG->print("RS_System::initLanguageList");
QStringList lst = getFileList("qm", "qm");
RS_SETTINGS->beginGroup("/Paths");
lst += (RS_SETTINGS->readEntry("/Translations", "")).split(";", QString::SkipEmptyParts);
RS_SETTINGS->endGroup();
for (QStringList::Iterator it = lst.begin();
it != lst.end();
++it) {
RS_DEBUG->print("RS_System::initLanguageList: qm file: %s",
(*it).toLatin1().data());
int i0 = (*it).lastIndexOf(QString("librecad"),-1,Qt::CaseInsensitive);
int i1 = (*it).indexOf('_',i0);
int i2 = (*it).indexOf('.', i1);
if (i1 == -1 || i2 == -1) {
continue;
}
QString l = (*it).mid(i1+1, i2-i1-1);
if (!(languageList.contains(l)) ) {
RS_DEBUG->print("RS_System::initLanguageList: append language: %s",
l.toLatin1().data());
languageList.append(l);
}
}
RS_DEBUG->print("RS_System::initLanguageList: OK");
}
void RS_System::addLocale(RS_Locale *locale) {
allKnownLocales.push_back( QSharedPointer<RS_Locale>( locale));
}
#define LNG(canonical, direction, name) \
locale = new RS_Locale(); \
locale->setCanonical( canonical); \
locale->setDirection( direction); \
locale->setName( name); \
addLocale( locale);
void RS_System::initAllLanguagesList() {
// RVT uk_AU renamed to uk so that we don't have to change the pootle server
allKnownLocales.clear();
RS_Locale *locale;
LNG( "ab" , RS2::locLeftToRight, "Abkhazian")
LNG( "aa" , RS2::locLeftToRight, "Afar")
LNG( "af_ZA", RS2::locLeftToRight, "Afrikaans")
LNG( "sq_AL", RS2::locLeftToRight, "Albanian")
LNG( "am" , RS2::locLeftToRight, "Amharic")
LNG( "ar" , RS2::locRightToLeft, "Arabic")
LNG( "ar_DZ", RS2::locRightToLeft, "Arabic (Algeria)")
LNG( "ar_BH", RS2::locRightToLeft, "Arabic (Bahrain)")
LNG( "ar_EG", RS2::locRightToLeft, "Arabic (Egypt)")
LNG( "ar_IQ", RS2::locRightToLeft, "Arabic (Iraq)")
LNG( "ar_JO", RS2::locRightToLeft, "Arabic (Jordan)")
LNG( "ar_KW", RS2::locRightToLeft, "Arabic (Kuwait)")
LNG( "ar_LB", RS2::locRightToLeft, "Arabic (Lebanon)")
LNG( "ar_LY", RS2::locRightToLeft, "Arabic (Libya)")
LNG( "ar_MA", RS2::locRightToLeft, "Arabic (Morocco)")
LNG( "ar_OM", RS2::locRightToLeft, "Arabic (Oman)")
LNG( "ar_QA", RS2::locRightToLeft, "Arabic (Qatar)")
LNG( "ar_SA", RS2::locRightToLeft, "Arabic (Saudi Arabia)")
LNG( "ar_SD", RS2::locRightToLeft, "Arabic (Sudan)")
LNG( "ar_SY", RS2::locRightToLeft, "Arabic (Syria)")
LNG( "ar_TN", RS2::locRightToLeft, "Arabic (Tunisia)")
LNG( "ar_AE", RS2::locRightToLeft, "Arabic (Uae)")
LNG( "ar_YE", RS2::locRightToLeft, "Arabic (Yemen)")
LNG( "hy" , RS2::locLeftToRight, "Armenian")
LNG( "as" , RS2::locLeftToRight, "Assamese")
LNG( "ay" , RS2::locLeftToRight, "Aymara")
LNG( "az" , RS2::locLeftToRight, "Azeri")
LNG( "az" , RS2::locLeftToRight, "Azeri (Cyrillic)")
LNG( "az" , RS2::locLeftToRight, "Azeri (Latin)")
LNG( "ba" , RS2::locLeftToRight, "Bashkir")
LNG( "eu_ES", RS2::locLeftToRight, "Basque")
LNG( "be_BY", RS2::locLeftToRight, "Belarusian")
LNG( "bn" , RS2::locLeftToRight, "Bengali")
LNG( "dz" , RS2::locLeftToRight, "Bhutani")
LNG( "bh" , RS2::locLeftToRight, "Bihari")
LNG( "bi" , RS2::locLeftToRight, "Bislama")
LNG( "br" , RS2::locLeftToRight, "Breton")
LNG( "bg_BG", RS2::locLeftToRight, "Bulgarian")
LNG( "my" , RS2::locLeftToRight, "Burmese")
LNG( "km" , RS2::locLeftToRight, "Cambodian")
LNG( "ca_ES", RS2::locLeftToRight, "Catalan")
LNG( "zh_TW", RS2::locLeftToRight, "Chinese")
LNG( "zh_CN", RS2::locLeftToRight, "Chinese (Simplified)")
LNG( "zh_TW", RS2::locLeftToRight, "Chinese (Traditional)")
LNG( "zh_HK", RS2::locLeftToRight, "Chinese (Hongkong)")
LNG( "zh_MO", RS2::locLeftToRight, "Chinese (Macau)")
LNG( "zh_SG", RS2::locLeftToRight, "Chinese (Singapore)")
LNG( "zh_TW", RS2::locLeftToRight, "Chinese (Taiwan)")
LNG( "co" , RS2::locLeftToRight, "Corsican")
LNG( "hr_HR", RS2::locLeftToRight, "Croatian")
LNG( "cs_CZ", RS2::locLeftToRight, "Czech")
LNG( "da_DK", RS2::locLeftToRight, "Danish")
LNG( "nl_NL", RS2::locLeftToRight, "Dutch")
LNG( "nl_BE", RS2::locLeftToRight, "Dutch (Belgian)")
LNG( "en_GB", RS2::locLeftToRight, "English")
LNG( "en_GB", RS2::locLeftToRight, "English (U.K.)")
LNG( "en_US", RS2::locLeftToRight, "English (U.S.)")
LNG( "en_AU", RS2::locLeftToRight, "English (Australia)")
LNG( "en_BZ", RS2::locLeftToRight, "English (Belize)")
LNG( "en_BW", RS2::locLeftToRight, "English (Botswana)")
LNG( "en_CA", RS2::locLeftToRight, "English (Canada)")
LNG( "en_CB", RS2::locLeftToRight, "English (Caribbean)")
LNG( "en_DK", RS2::locLeftToRight, "English (Denmark)")
LNG( "en_IE", RS2::locLeftToRight, "English (Eire)")
LNG( "en_JM", RS2::locLeftToRight, "English (Jamaica)")
LNG( "en_NZ", RS2::locLeftToRight, "English (New Zealand)")
LNG( "en_PH", RS2::locLeftToRight, "English (Philippines)")
LNG( "en_ZA", RS2::locLeftToRight, "English (South Africa)")
LNG( "en_TT", RS2::locLeftToRight, "English (Trinidad)")
LNG( "en_ZW", RS2::locLeftToRight, "English (Zimbabwe)")
LNG( "eo" , RS2::locLeftToRight, "Esperanto")
LNG( "et_EE", RS2::locLeftToRight, "Estonian")
LNG( "fo_FO", RS2::locLeftToRight, "Faeroese")
LNG( "fa_IR", RS2::locLeftToRight, "Farsi")
LNG( "fj" , RS2::locLeftToRight, "Fiji")
LNG( "fi_FI", RS2::locLeftToRight, "Finnish")
LNG( "fr_FR", RS2::locLeftToRight, "French")
LNG( "fr_BE", RS2::locLeftToRight, "French (Belgian)")
LNG( "fr_CA", RS2::locLeftToRight, "French (Canadian)")
LNG( "fr_LU", RS2::locLeftToRight, "French (Luxembourg)")
LNG( "fr_MC", RS2::locLeftToRight, "French (Monaco)")
LNG( "fr_CH", RS2::locLeftToRight, "French (Swiss)")
LNG( "fy" , RS2::locLeftToRight, "Frisian")
LNG( "gl_ES", RS2::locLeftToRight, "Galician")
LNG( "ka_GE", RS2::locLeftToRight, "Georgian")
LNG( "de_DE", RS2::locLeftToRight, "German")
LNG( "de_AT", RS2::locLeftToRight, "German (Austrian)")
LNG( "de_BE", RS2::locLeftToRight, "German (Belgium)")
LNG( "de_LI", RS2::locLeftToRight, "German (Liechtenstein)")
LNG( "de_LU", RS2::locLeftToRight, "German (Luxembourg)")
LNG( "de_CH", RS2::locLeftToRight, "German (Swiss)")
LNG( "el_GR", RS2::locLeftToRight, "Greek")
LNG( "kl_GL", RS2::locLeftToRight, "Greenlandic")
LNG( "gn" , RS2::locLeftToRight, "Guarani")
LNG( "gu" , RS2::locLeftToRight, "Gujarati")
LNG( "ha" , RS2::locLeftToRight, "Hausa")
LNG( "he_IL", RS2::locRightToLeft, "Hebrew")
LNG( "hi_IN", RS2::locLeftToRight, "Hindi")
LNG( "hu_HU", RS2::locLeftToRight, "Hungarian")
LNG( "is_IS", RS2::locLeftToRight, "Icelandic")
LNG( "id_ID", RS2::locLeftToRight, "Indonesian")
LNG( "ia" , RS2::locLeftToRight, "Interlingua")
LNG( "ie" , RS2::locLeftToRight, "Interlingue")
LNG( "iu" , RS2::locLeftToRight, "Inuktitut")
LNG( "ik" , RS2::locLeftToRight, "Inupiak")
LNG( "ga_IE", RS2::locLeftToRight, "Irish")
LNG( "it_IT", RS2::locLeftToRight, "Italian")
LNG( "it_CH", RS2::locLeftToRight, "Italian (Swiss)")
LNG( "ja_JP", RS2::locLeftToRight, "Japanese")
LNG( "jw" , RS2::locLeftToRight, "Javanese")
LNG( "kn" , RS2::locLeftToRight, "Kannada")
LNG( "ks" , RS2::locLeftToRight, "Kashmiri")
LNG( "ks_IN", RS2::locLeftToRight, "Kashmiri (India)")
LNG( "kk" , RS2::locLeftToRight, "Kazakh")
LNG( "kw_GB", RS2::locLeftToRight, "Kernewek")
LNG( "rw" , RS2::locLeftToRight, "Kinyarwanda")
LNG( "ky" , RS2::locLeftToRight, "Kirghiz")
LNG( "rn" , RS2::locLeftToRight, "Kirundi")
LNG( "" , RS2::locLeftToRight, "Konkani")
LNG( "ko_KR", RS2::locLeftToRight, "Korean")
LNG( "ku_TR", RS2::locLeftToRight, "Kurdish")
LNG( "lo" , RS2::locLeftToRight, "Laothian")
LNG( "la" , RS2::locLeftToRight, "Latin")
LNG( "lv_LV", RS2::locLeftToRight, "Latvian")
LNG( "ln" , RS2::locLeftToRight, "Lingala")
LNG( "lt_LT", RS2::locLeftToRight, "Lithuanian")
LNG( "mk_MK", RS2::locLeftToRight, "Macedonian")
LNG( "mg" , RS2::locLeftToRight, "Malagasy")
LNG( "ms_MY", RS2::locLeftToRight, "Malay")
LNG( "ml" , RS2::locLeftToRight, "Malayalam")
LNG( "ms_BN", RS2::locLeftToRight, "Malay (Brunei Darussalam)")
LNG( "ms_MY", RS2::locLeftToRight, "Malay (Malaysia)")
LNG( "mt_MT", RS2::locLeftToRight, "Maltese")
LNG( "" , RS2::locLeftToRight, "Manipuri")
LNG( "mi" , RS2::locLeftToRight, "Maori")
LNG( "mr_IN", RS2::locLeftToRight, "Marathi")
LNG( "mo" , RS2::locLeftToRight, "Moldavian")
LNG( "mn" , RS2::locLeftToRight, "Mongolian")
LNG( "na" , RS2::locLeftToRight, "Nauru")
LNG( "ne_NP", RS2::locLeftToRight, "Nepali")
LNG( "ne_IN", RS2::locLeftToRight, "Nepali (India)")
LNG( "nb_NO", RS2::locLeftToRight, "Norwegian (Bokmal)")
LNG( "nn_NO", RS2::locLeftToRight, "Norwegian (Nynorsk)")
LNG( "oc" , RS2::locLeftToRight, "Occitan")
LNG( "or" , RS2::locLeftToRight, "Oriya")
LNG( "om" , RS2::locLeftToRight, "(Afan) Oromo")
LNG( "ps" , RS2::locLeftToRight, "Pashto, Pushto")
LNG( "pl_PL", RS2::locLeftToRight, "Polish")
LNG( "pt_PT", RS2::locLeftToRight, "Portuguese")
LNG( "pt_BR", RS2::locLeftToRight, "Portuguese (Brazilian)")
LNG( "pa" , RS2::locLeftToRight, "Punjabi")
LNG( "qu" , RS2::locLeftToRight, "Quechua")
LNG( "rm" , RS2::locLeftToRight, "Rhaeto-Romance")
LNG( "ro_RO", RS2::locLeftToRight, "Romanian")
LNG( "ru_RU", RS2::locLeftToRight, "Russian")
LNG( "ru_UA", RS2::locLeftToRight, "Russian (Ukraine)")
LNG( "sm" , RS2::locLeftToRight, "Samoan")
LNG( "sg" , RS2::locLeftToRight, "Sangho")
LNG( "sa" , RS2::locLeftToRight, "Sanskrit")
LNG( "gd" , RS2::locLeftToRight, "Scots Gaelic")
LNG( "se_NO", RS2::locLeftToRight, "Northern Sami")
LNG( "sr_SR", RS2::locLeftToRight, "Serbian")
LNG( "sr_SR", RS2::locLeftToRight, "Serbian (Cyrillic)")
LNG( "sr_SR@latin", RS2::locLeftToRight, "Serbian (Latin)")
LNG( "sr_YU", RS2::locLeftToRight, "Serbian (Cyrillic)")
LNG( "sr_YU@latin", RS2::locLeftToRight, "Serbian (Latin)")
LNG( "sh" , RS2::locLeftToRight, "Serbo-Croatian")
LNG( "st" , RS2::locLeftToRight, "Sesotho")
LNG( "tn" , RS2::locLeftToRight, "Setswana")
LNG( "sn" , RS2::locLeftToRight, "Shona")
LNG( "sd" , RS2::locLeftToRight, "Sindhi")
LNG( "si" , RS2::locLeftToRight, "Sinhalese")
LNG( "ss" , RS2::locLeftToRight, "Siswati")
LNG( "sk_SK", RS2::locLeftToRight, "Slovak")
LNG( "sl_SI", RS2::locLeftToRight, "Slovenian")
LNG( "so" , RS2::locLeftToRight, "Somali")
LNG( "es_ES", RS2::locLeftToRight, "Spanish")
LNG( "es_AR", RS2::locLeftToRight, "Spanish (Argentina)")
LNG( "es_BO", RS2::locLeftToRight, "Spanish (Bolivia)")
LNG( "es_CL", RS2::locLeftToRight, "Spanish (Chile)")
LNG( "es_CO", RS2::locLeftToRight, "Spanish (Colombia)")
LNG( "es_CR", RS2::locLeftToRight, "Spanish (Costa Rica)")
LNG( "es_DO", RS2::locLeftToRight, "Spanish (Dominican republic)")
LNG( "es_EC", RS2::locLeftToRight, "Spanish (Ecuador)")
LNG( "es_SV", RS2::locLeftToRight, "Spanish (El Salvador)")
LNG( "es_GT", RS2::locLeftToRight, "Spanish (Guatemala)")
LNG( "es_HN", RS2::locLeftToRight, "Spanish (Honduras)")
LNG( "es_MX", RS2::locLeftToRight, "Spanish (Mexican)")
LNG( "es_ES", RS2::locLeftToRight, "Spanish (Modern)")
LNG( "es_NI", RS2::locLeftToRight, "Spanish (Nicaragua)")
LNG( "es_PA", RS2::locLeftToRight, "Spanish (Panama)")
LNG( "es_PY", RS2::locLeftToRight, "Spanish (Paraguay)")
LNG( "es_PE", RS2::locLeftToRight, "Spanish (Peru)")
LNG( "es_PR", RS2::locLeftToRight, "Spanish (Puerto Rico)")
LNG( "es_UY", RS2::locLeftToRight, "Spanish (Uruguay)")
LNG( "es_US", RS2::locLeftToRight, "Spanish (U.S.)")
LNG( "es_VE", RS2::locLeftToRight, "Spanish (Venezuela)")
LNG( "su" , RS2::locLeftToRight, "Sundanese")
LNG( "sw_KE", RS2::locLeftToRight, "Swahili")
LNG( "sv_SE", RS2::locLeftToRight, "Swedish")
LNG( "sv_FI", RS2::locLeftToRight, "Swedish (Finland)")
LNG( "tl_PH", RS2::locLeftToRight, "Tagalog")
LNG( "tg" , RS2::locLeftToRight, "Tajik")
LNG( "ta" , RS2::locLeftToRight, "Tamil")
LNG( "tt" , RS2::locLeftToRight, "Tatar")
LNG( "te" , RS2::locLeftToRight, "Telugu")
LNG( "th_TH", RS2::locLeftToRight, "Thai")
LNG( "bo" , RS2::locLeftToRight, "Tibetan")
LNG( "ti" , RS2::locLeftToRight, "Tigrinya")
LNG( "to" , RS2::locLeftToRight, "Tonga")
LNG( "ts" , RS2::locLeftToRight, "Tsonga")
LNG( "tr_TR", RS2::locLeftToRight, "Turkish")
LNG( "tk" , RS2::locLeftToRight, "Turkmen")
LNG( "tw" , RS2::locLeftToRight, "Twi")
LNG( "ug" , RS2::locLeftToRight, "Uighur")
LNG( "uk" , RS2::locLeftToRight, "Ukrainian")
LNG( "ur" , RS2::locLeftToRight, "Urdu")
LNG( "ur_IN", RS2::locLeftToRight, "Urdu (India)")
LNG( "ur_PK", RS2::locLeftToRight, "Urdu (Pakistan)")
LNG( "uz" , RS2::locLeftToRight, "Uzbek")
LNG( "uz" , RS2::locLeftToRight, "Uzbek (Cyrillic)")
LNG( "uz" , RS2::locLeftToRight, "Uzbek (Latin)")
LNG( "ca_ES@valencia", RS2::locLeftToRight, "Valencian")
LNG( "vi_VN", RS2::locLeftToRight, "Vietnamese")
LNG( "vo" , RS2::locLeftToRight, "Volapuk")
LNG( "cy" , RS2::locLeftToRight, "Welsh")
LNG( "wo" , RS2::locLeftToRight, "Wolof")
LNG( "xh" , RS2::locLeftToRight, "Xhosa")
LNG( "yi" , RS2::locLeftToRight, "Yiddish")
LNG( "yo" , RS2::locLeftToRight, "Yoruba")
LNG( "za" , RS2::locLeftToRight, "Zhuang")
LNG( "zu" , RS2::locLeftToRight, "Zulu")
}
/**
* Loads a different translation for the application GUI.
*
*fixme, need to support command language
*/
void RS_System::loadTranslation(const QString& lang, const QString& /*langCmd*/) {
static QTranslator* tQt = NULL;
static QTranslator* tLibreCAD = NULL;
static QTranslator* tPlugIns = NULL;
//make translation filenames case insensitive, #276
QString langLower("");
QString langUpper("");
int i0 = lang.indexOf('_');
if (i0 >= 2 && lang.size() - i0 >= 2) {
//contains region code
langLower = lang.left( i0) + '_' + lang.mid( i0 + 1).toLower();
langUpper = lang.left( i0) + '_' + lang.mid( i0 + 1).toUpper();
}
else {
langLower = lang;
langUpper.clear();
}
// search in various directories for translations
QStringList lst = getDirectoryList( "qm");
RS_SETTINGS->beginGroup( "/Paths");
lst += (RS_SETTINGS->readEntry( "/Translations", "")).split( ";", QString::SkipEmptyParts);
RS_SETTINGS->endGroup();
if( tLibreCAD != NULL) {
qApp->removeTranslator( tLibreCAD);
delete tLibreCAD;
}
if( tPlugIns != NULL) {
qApp->removeTranslator( tPlugIns);
delete tPlugIns;
}
if( tQt != NULL) {
qApp->removeTranslator( tQt);
delete tQt;
}
QString langFileLower = "librecad_" + langLower + ".qm",
langFileUpper = "librecad_" + langUpper + ".qm",
langPlugInsLower = "plugins_" + langLower + ".qm",
langPlugInsUpper = "plugins_" + langUpper + ".qm",
langQtLower = "qt_" + langLower + ".qm",
langQtUpper = "qt_" + langUpper + ".qm";
QTranslator* t = new QTranslator(0);
for (QStringList::Iterator it = lst.begin();
it != lst.end();
++it) {
// load LibreCAD translations
if (NULL == tLibreCAD) {
if (t->load( langFileLower, *it) == true
|| ( ! langUpper.isEmpty()
&& t->load( langFileUpper, *it) == true)) {
tLibreCAD = t;
qApp->installTranslator( tLibreCAD);
t = new QTranslator(0);
}
}
// load PlugIns translations
if (NULL == tPlugIns) {
if (t->load( langPlugInsLower, *it) == true
|| ( ! langUpper.isEmpty()
&& t->load( langPlugInsUpper, *it) == true)) {
tPlugIns = t;
qApp->installTranslator( tPlugIns);
t = new QTranslator(0);
}
}
// load Qt standard dialog translations
if (NULL == tQt) {
if (t->load( langQtLower, *it) == true
|| ( ! langUpper.isEmpty()
&& t->load( langQtUpper, *it) == true)) {
tQt = t;
qApp->installTranslator( tQt);
t = new QTranslator(0);
}
}
if (NULL != tLibreCAD && NULL != tPlugIns && NULL != tQt) {
break;
}
}
if (NULL != t) {
delete t;
}
}
/**
* Checks if the system has been initialized and prints a warning
* otherwise to stderr.
*/
bool RS_System::checkInit() {
if (!initialized) {
RS_DEBUG->print(RS_Debug::D_WARNING,
"RS_System::checkInit: System not initialized.\n"
"Use RS_SYSTEM->init(appname, appdirname) to do so.");
}
return initialized;
}
/**
* Creates all given directories in the user's home.
*/
bool RS_System::createPaths(const QString& directory) {
QDir dir;
dir.cd( QDir::homePath());
dir.mkpath( directory);
return true;
}
/**
* Create if not exist and return the Application data directory.
* In OS_WIN32 "c:\documents&settings\<user>\local configuration\application data\LibreCAD"
* In OS_MAC "/Users/<user>/Library/Application Support/LibreCAD"
* In OS_LINUX "/home/<user>/.local/share/data/LibreCAD"
*
* @return Application data directory.
*/
QString RS_System::getAppDataDir() {
QString appData =
QStandardPaths::writableLocation( QStandardPaths::DataLocation);
QDir dir( appData);
if (!dir.exists()) {
if (!dir.mkpath( appData))
return QString();
}
return appData;
}
/**
* Searches for files in an application shared directory in the given
* subdirectory with the given extension.
*
* @return List of the absolute paths of the files found.
*/
QStringList RS_System::getFileList(const QString& subDirectory,
const QString& fileExtension) {
checkInit();
RS_DEBUG->print( "RS_System::getFileList: subdirectory %s ", subDirectory.toLatin1().data());
RS_DEBUG->print( "RS_System::getFileList: appDirName %s ", appDirName.toLatin1().data());
RS_DEBUG->print( "RS_System::getFileList: getCurrentDir %s ", getCurrentDir().toLatin1().data());
QStringList dirList = getDirectoryList( subDirectory);
QStringList fileList;
QString path;
QDir dir;
for (QStringList::Iterator it = dirList.begin();
it != dirList.end();
++it) {
//path = QString(*it) + "/" + subDirectory;
path = QString( *it);
dir = QDir( path);
if (dir.exists() && dir.isReadable()) {
QStringList files = dir.entryList( QStringList( "*." + fileExtension));
for (QStringList::Iterator it2 = files.begin();
it2 != files.end();
it2++) {
fileList += path + "/" + (*it2);
}
}
}
return fileList;
}
/**
* @return List of all directories in subdirectory 'subDirectory' in
* all possible LibreCAD directories.
*/
QStringList RS_System::getDirectoryList(const QString& _subDirectory) {
QStringList dirList;
QString subDirectory = QDir::fromNativeSeparators( _subDirectory);
#ifdef Q_OS_MAC
dirList.append( QStandardPaths::writableLocation( QStandardPaths::DocumentsLocation) + "/" + appDirName + "/" + subDirectory);
#endif // Q_OS_MAC
#ifdef Q_OS_WIN32
dirList.append( QStandardPaths::writableLocation( QStandardPaths::DocumentsLocation) + "/" + appDirName + "/" + subDirectory);
#endif // Q_OS_WIN32
// Unix home directory, it's old style but some people might have stuff there.
dirList.append( getHomeDir() + "/." + appDirName + "/" + subDirectory);
//local (application) directory has priority over other dirs:
if (!subDirectory.compare( "plugins")) {
// 17 Aug, 2011, Dongxu Li, do not look for plugins in the current folder,
// we should install plugins to system or ~/.librecad/plugins/
if (externalAppDir) {
dirList.append( appDir + "/" + subDirectory);
}
}
#ifdef Q_OS_UNIX
// for AppImage use relative paths from executable
// from package manager the executable is in /usr/bin
// in AppImage the executable is APPDIR/usr/bin
// so this should work for package manager and AppImage distribution
dirList.append( QDir::cleanPath( appDir + "/../share/doc/" + appDirName + "/" + subDirectory));
// Redhat style:
dirList.append( QDir::cleanPath( appDir + "/../share/" + appDirName + "/" + subDirectory));
// Debian style:
dirList.append( QDir::cleanPath( appDir + "/../lib/" + appDirName + "/" + subDirectory));
if (QStringLiteral( "plugins") == subDirectory) {
dirList.append( QDir::cleanPath( appDir + "/../lib/" + appDirName));
}
#endif
#ifdef Q_OS_MAC
// Apple uses the resource directory
if (!appDir.isEmpty() && appDir!="/") {
dirList.append( QDir::cleanPath( appDir + "/../Resources/" + subDirectory));
}
#endif
#ifndef Q_OS_MAC
// Add support directory if librecad is run-in-place,
// not for Apple because it uses resources this is more for unix systems
dirList.append( appDir + "/resources/" + subDirectory);
#endif
// Individual directories:
RS_SETTINGS->beginGroup( "/Paths");
if (subDirectory == "fonts") {
dirList += (RS_SETTINGS->readEntry( "/Fonts", "")).split( QRegExp("[;]"),
QString::SkipEmptyParts);
}
else if (subDirectory == "patterns") {
dirList += (RS_SETTINGS->readEntry( "/Patterns", "")).split( QRegExp("[;]"),
QString::SkipEmptyParts);
}
else if (subDirectory.startsWith( "scripts")) {
dirList += (RS_SETTINGS->readEntry( "/Scripts", "")).split( QRegExp("[;]"),
QString::SkipEmptyParts);
}
else if (subDirectory.startsWith( "library")) {
dirList += (RS_SETTINGS->readEntry( "/Library", "")).split( QRegExp("[;]"),
QString::SkipEmptyParts);
}
else if (subDirectory.startsWith( "qm")) {
dirList += (RS_SETTINGS->readEntry( "/Translations", "")).split( QRegExp("[;]"),
QString::SkipEmptyParts);
}
RS_SETTINGS->endGroup();
QStringList ret;
RS_DEBUG->print("RS_System::getDirectoryList: Paths:");
for (QStringList::Iterator it = dirList.begin();
it != dirList.end();
++it ) {
if (QFileInfo( *it).isDir()) {
ret += (*it);
RS_DEBUG->print( (*it).toLatin1() );
}
}
return ret;
}
/**
* Converts a language string to a symbol (e.g. Deutsch or German to 'de').
* Languages taken from RFC3066
*/
QString RS_System::languageToSymbol(const QString& lang) {
int i1 = lang.indexOf( ' ');
QString l = lang;
if (i1 >= 2){
l = lang.mid( 0, i1);
}
return l;
// RS_Locale *locale;
// foreach (locale, *RS_SYSTEM->allKnownLocales) {
// if (locale->getName().toLower() == l) {
// return locale->getCanonical();
// }
// }
// return "";
}
/**
* Converts a locale code into a readable string
* (e.g. 'de' to 'German Deutsch'
* (e.g. 'en_au' to 'English (Australia)'
*/
QString RS_System::symbolToLanguage(const QString& symb) {
RS_Locale loc( symb);
QString ret;
if (symb.contains( QRegExp( "^en"))) {
ret = RS_Locale::languageToString( loc.language());
if( symb.contains('_') ) {
ret += " (" + RS_Locale::countryToString( loc.country()) + ')';
}
}
else {
ret = RS_Locale::languageToString( loc.language()) + ' ' + loc.nativeLanguageName();
if( symb.contains( '_') ) {
ret += " (" + RS_Locale::countryToString( loc.country()) + ' ' + loc.nativeCountryName() + ')';
}
}
return ret;
}
/**
* Tries to convert the given encoding string to an encoding Qt knows.
*/
QString RS_System::getEncoding(const QString& str) {
QString l=str.toLower();
if (l=="latin1" || l=="ansi_1252" || l=="iso-8859-1" ||
l=="cp819" || l=="csiso" || l=="ibm819" || l=="iso_8859-1" ||
l=="iso8859-1" || l=="iso-ir-100" || l=="l1") {
return "Latin1";
} else if (l=="big5" || l=="ansi_950" || l=="cn-big5" || l=="csbig5" ||
l=="x-x-big5") {
return "Big5";
} else if (l=="big5-hkscs") {
return "Big5-HKSCS";
} else if (l=="eucjp" || l=="euc-jp" || l=="cseucpkdfmtjapanese" ||
l=="x-euc" || l=="x-euc-jp") {
return "eucJP";
} else if (l=="euckr") {
return "eucKR";
} else if (l=="gb2312" || l=="chinese" || l=="cn-gb" ||
l=="csgb2312" || l=="csgb231280" || l=="csiso58gb231280" ||
l=="gb_2312-80" || l=="gb231280" || l=="gb2312-80" || l=="gbk" ||
l=="iso-ir-58") {
return "GB2312";
} else if (l=="gbk") {
return "GBK";
} else if (l=="gb18030") {
return "GB18030";
} else if (l=="jis7") {
return "JIS7";
} else if (l=="shift-jis" || l=="ansi_932" || l=="shift_jis" || l=="csShiftJIS" ||
l=="cswindows31j" || l=="ms_kanji" || l=="x-ms-cp932" || l=="x-sjis") {
return "Shift-JIS";
} else if (l=="tscii") {
return "TSCII";
} else if (l=="utf88-bit") {
return "utf88-bit";
} else if (l=="utf16") {
return "utf16";
} else if (l=="utf8" || l=="utf-8") {
return "utf-8";
} else if (l=="koi8-r") {
return "KOI8-R";
} else if (l=="koi8-u") {
return "KOI8-U";
} else if (l=="iso8859-1") {
return "ISO8859-1";
} else if (l=="iso8859-2") {
return "ISO8859-2";
} else if (l=="iso8859-3") {
return "ISO8859-3";
} else if (l=="iso8859-4" || l=="ansi_1257") {
return "ISO8859-4";
} else if (l=="iso8859-5") {
return "ISO8859-5";
} else if (l=="iso8859-6" || l=="ansi_1256") {
return "ISO8859-6";
} else if (l=="iso8859-7" || l=="ansi_1253") {
return "ISO8859-7";
} else if (l=="iso8859-8") {
return "ISO8859-8";
} else if (l=="iso8859-8-i" || l=="ansi_1255") {
return "ISO8859-8-i";
} else if (l=="iso8859-9" || l=="ansi_1254") {
return "ISO8859-9";
} else if (l=="iso8859-10") {
return "ISO8859-10";
} else if (l=="iso8859-13") {
return "ISO8859-13";
} else if (l=="iso8859-14") {
return "ISO8859-14";
} else if (l=="iso8859-15") {
return "ISO8859-15";
} else if (l=="ibm 850") {
return "IBM 850";
} else if (l=="ibm 866") {
return "IBM 866";
} else if (l=="cp874") {
return "CP874";
} else if (l=="cp1250") {
return "CP1250";
} else if (l=="cp1251" || l=="ansi_1251") {
return "CP1251";
} else if (l=="cp1252") {
return "CP1252";
} else if (l=="cp1253") {
return "CP1253";
} else if (l=="cp1254") {
return "CP1254";
} else if (l=="cp1255") {
return "CP1255";
} else if (l=="cp1256") {
return "CP1256";
} else if (l=="cp1257") {
return "CP1257";
} else if (l=="cp1258") {
return "CP1258";
} else if (l=="apple roman") {
return "Apple Roman";
} else if (l=="tis-620") {
return "TIS-620";
}
return "latin1";
}
/** Returns ISO code for given locale. Needed for win32 to convert
from system encodings.
Locale names mostly copied from XFree86.
The code may be incomplete (chinese/japanese locales, etc.)
2004-05-13, J Staniek
*/
static QMap<QByteArray,QByteArray> loc_map;
QByteArray RS_System::localeToISO(const QByteArray& locale) {
if (loc_map.isEmpty()) {
loc_map["croatian"]="ISO8859-2";
loc_map["cs"]="ISO8859-2";
loc_map["cs_CS"]="ISO8859-2";
loc_map["cs_CZ"]="ISO8859-2";
loc_map["cz"]="ISO8859-2";
loc_map["cz_CZ"]="ISO8859-2";
loc_map["czech"]="ISO8859-2";
loc_map["hr"]="ISO8859-2";
loc_map["hr_HR"]="ISO8859-2";
loc_map["hu"]="ISO8859-2";
loc_map["hu_HU"]="ISO8859-2";
loc_map["hungarian"]="ISO8859-2";
loc_map["pl"]="ISO8859-2";
loc_map["pl_PL"]="ISO8859-2";
loc_map["polish"]="ISO8859-2";
loc_map["ro"]="ISO8859-2";
loc_map["ro_RO"]="ISO8859-2";
loc_map["rumanian"]="ISO8859-2";
loc_map["serbocroatian"]="ISO8859-2";
loc_map["sh"]="ISO8859-2";
loc_map["sh_SP"]="ISO8859-2";
loc_map["sh_YU"]="ISO8859-2";
loc_map["sk"]="ISO8859-2";
loc_map["sk_SK"]="ISO8859-2";
loc_map["sl"]="ISO8859-2";
loc_map["sl_CS"]="ISO8859-2";
loc_map["sl_SI"]="ISO8859-2";
loc_map["slovak"]="ISO8859-2";
loc_map["slovene"]="ISO8859-2";
loc_map["sr_SP"]="ISO8859-2";
loc_map["eo"]="ISO8859-3";
loc_map["ee"]="ISO8859-4";
loc_map["ee_EE"]="ISO8859-4";
loc_map["mk"]="ISO8859-5";
loc_map["mk_MK"]="ISO8859-5";
loc_map["sp"]="ISO8859-5";
loc_map["sp_YU"]="ISO8859-5";
loc_map["ar_AA"]="ISO8859-6";
loc_map["ar_SA"]="ISO8859-6";
loc_map["arabic"]="ISO8859-6";
loc_map["el"]="ISO8859-7";
loc_map["el_GR"]="ISO8859-7";
loc_map["greek"]="ISO8859-7";
loc_map["hebrew"]="ISO8859-8";
loc_map["he"]="ISO8859-8";
loc_map["he_IL"]="ISO8859-8";
loc_map["iw"]="ISO8859-8";
loc_map["iw_IL"]="ISO8859-8";
loc_map["tr"]="ISO8859-9";
loc_map["tr_TR"]="ISO8859-9";
loc_map["turkish"]="ISO8859-9";
loc_map["lt"]="ISO8859-13";
loc_map["lt_LT"]="ISO8859-13";
loc_map["lv"]="ISO8859-13";
loc_map["lv_LV"]="ISO8859-13";
loc_map["et"]="ISO8859-15";
loc_map["et_EE"]="ISO8859-15";
loc_map["br_FR"]="ISO8859-15";
loc_map["ca_ES"]="ISO8859-15";
loc_map["de"]="ISO8859-15";
loc_map["de_AT"]="ISO8859-15";
loc_map["de_BE"]="ISO8859-15";
loc_map["de_DE"]="ISO8859-15";
loc_map["de_LU"]="ISO8859-15";
loc_map["en_IE"]="ISO8859-15";
loc_map["es"]="ISO8859-15";
loc_map["es_EC"]="ISO8859-15";
loc_map["es_ES"]="ISO8859-15";
loc_map["eu_ES"]="ISO8859-15";
loc_map["fi"]="ISO8859-15";
loc_map["fi_FI"]="ISO8859-15";
loc_map["finnish"]="ISO8859-15";
loc_map["fr"]="ISO8859-15";
loc_map["fr_FR"]="ISO8859-15";
loc_map["fr_BE"]="ISO8859-15";
loc_map["fr_LU"]="ISO8859-15";
loc_map["french"]="ISO8859-15";
loc_map["ga_IE"]="ISO8859-15";
loc_map["gl_ES"]="ISO8859-15";
loc_map["it"]="ISO8859-15";
loc_map["it_IT"]="ISO8859-15";
loc_map["oc_FR"]="ISO8859-15";
loc_map["nl"]="ISO8859-15";
loc_map["nl_BE"]="ISO8859-15";
loc_map["nl_NL"]="ISO8859-15";
loc_map["pt"]="ISO8859-15";
loc_map["pt_PT"]="ISO8859-15";
loc_map["sv_FI"]="ISO8859-15";
loc_map["wa_BE"]="ISO8859-15";
loc_map["uk"]="KOI8-U";
loc_map["uk_UA"]="KOI8-U";
loc_map["ru_YA"]="KOI8-U";
loc_map["ukrainian"]="KOI8-U";
loc_map["be"]="KOI8-R";
loc_map["be_BY"]="KOI8-R";
loc_map["bg"]="KOI8-R";
loc_map["bg_BG"]="KOI8-R";
loc_map["bulgarian"]="KOI8-R";
loc_map["ba_RU"]="KOI8-R";
loc_map["ky"]="KOI8-R";
loc_map["ky_KG"]="KOI8-R";
loc_map["kk"]="KOI8-R";
loc_map["kk_KZ"]="KOI8-R";
}
QByteArray l = loc_map[locale];
if (l.isEmpty()) {
return "ISO8859-1";
}
return l;
}

203
lib/engine/rs_system.h Normal file
View File

@@ -0,0 +1,203 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2021 A. Stebich (librecad@mail.lordofbikes.de)
** 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!
**
**********************************************************************/
#ifndef RS_SYSTEM_H
#define RS_SYSTEM_H
#include <QDir>
#include <QList>
#include <QSharedPointer>
#include "rs_debug.h"
#include "rs_locale.h"
#define RS_SYSTEM RS_System::instance()
/**
* Class for some system methods such as file system operations.
* Implemented as singleton. Use init to Initialize the class
* before you use it.
*
* @author Andrew Mustun
*/
class RS_System {
protected:
RS_System() {
initialized = false;
}
public:
/**
* @return Instance to the unique system object.
*/
static RS_System* instance() {
if (uniqueInstance==NULL) {
uniqueInstance = new RS_System();
}
return uniqueInstance;
}
void init(const QString& appName,
const QString& appVersion,
const QString& appDirName,
const char *arg0);
void initLanguageList();
void initAllLanguagesList();
bool checkInit();
bool createPaths(const QString& p);
/**
* @return Users home directory.
*/
QString getHomeDir() {
return QDir::homePath();
}
/**
* @return Current directory.
*/
QString getCurrentDir() {
return QDir::currentPath();
}
/**
* @return Application Data directory.
*/
QString getAppDataDir();
/**
* @return A list of absolute paths to all font files found.
*/
QStringList getFontList() {
QStringList ret = getFileList("fonts", "cxf");
return ret;
}
/**
* @return A list of absolute paths to all NEW font files found.
*/
QStringList getNewFontList() {
QStringList ret = getFileList("fonts", "lff");
return ret;
}
/**
* @return A list of absolute paths to all hatch pattern files found.
*/
QStringList getPatternList() {
QStringList ret = getFileList("patterns", "dxf");
return ret;
}
/**
* @return A list of absolute paths to all script files found.
*/
QStringList getScriptList() {
QStringList ret = getFileList("scripts/qsa", "qs");
return ret;
}
/**
* @return A list of absolute paths to all machine configuration files found.
*/
QStringList getMachineList() {
QStringList ret = getFileList("machines", "cxm");
return ret;
}
/**
* @return Absolute path to the documentation.
*/
QString getDocPath() {
QStringList lst = getDirectoryList("doc");
if( !lst.isEmpty()) {
return lst.first();
}
else {
return QString();
}
}
/**
* @return The application name.
*/
QString getAppName() {
return appName;
}
/**
* @return The application version.
*/
QString getAppVersion() {
return appVersion;
}
QStringList getFileList(const QString& subDirectory,
const QString& fileExtension);
QStringList getDirectoryList(const QString& subDirectory);
QStringList getLanguageList() {
return languageList;
}
static QString languageToSymbol(const QString& lang);
static QString symbolToLanguage(const QString& symb);
static QString getEncoding(const QString& str);
void loadTranslation(const QString& lang, const QString& langCmd);
static bool test();
/** Returns ISO code for given locale. Needed for win32 to convert
* from system encodings.
*/
static QByteArray localeToISO(const QByteArray& locale);
private:
void addLocale(RS_Locale *locale);
protected:
static RS_System* uniqueInstance;
QString appName;
QString appVersion;
QString appDirName;
QString appDir;
QStringList languageList; //< List of available translations
bool initialized {false};
bool externalAppDir {false};
QList<QSharedPointer<RS_Locale> > allKnownLocales;
};
#endif

559
lib/engine/rs_text.cpp Normal file
View File

@@ -0,0 +1,559 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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_font.h"
#include "rs_text.h"
#include "rs_fontlist.h"
#include "rs_insert.h"
#include "rs_math.h"
#include "rs_debug.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
RS_TextData::RS_TextData(const RS_Vector& _insertionPoint,
const RS_Vector& _secondPoint,
double _height,
double _widthRel,
VAlign _valign,
HAlign _halign,
TextGeneration _textGeneration,
const QString& _text,
const QString& _style,
double _angle,
RS2::UpdateMode _updateMode):
insertionPoint(_insertionPoint)
,secondPoint(_secondPoint)
,height(_height)
,widthRel(_widthRel)
,valign(_valign)
,halign(_halign)
,textGeneration(_textGeneration)
,text(_text)
,style(_style)
,angle(_angle)
,updateMode(_updateMode)
{
}
std::ostream& operator << (std::ostream& os, const RS_TextData& td) {
os << "("
<<td.insertionPoint<<','
<<td.secondPoint<<','
<<td.height<<','
<<td.widthRel<<','
<<td.valign<<','
<<td.halign<<','
<<td.textGeneration<<','
<<td.text.toLatin1().data() <<','
<<td.style.toLatin1().data()<<','
<<td.angle<<','
<<td.updateMode<<','
<<")";
return os;
}
/**
* Constructor.
*/
RS_Text::RS_Text(RS_EntityContainer* parent,
const RS_TextData& d)
: RS_EntityContainer(parent), data(d) {
usedTextHeight = 0.0;
usedTextWidth = 0.0;
setText(data.text);
}
RS_Entity* RS_Text::clone() const{
RS_Text* t = new RS_Text(*this);
t->setOwner(isOwner());
t->initId();
t->detach();
return t;
}
/**
* Sets a new text. The entities representing the
* text are updated.
*/
void RS_Text::setText(const QString& t) {
data.text = t;
// handle some special flags embedded in the text:
if (data.text.left(4)=="\\A0;") {
data.text = data.text.mid(4);
data.valign = RS_TextData::VABottom;
} else if (data.text.left(4)=="\\A1;") {
data.text = data.text.mid(4);
data.valign = RS_TextData::VAMiddle;
} else if (data.text.left(4)=="\\A2;") {
data.text = data.text.mid(4);
data.valign = RS_TextData::VATop;
}
if (data.updateMode==RS2::Update) {
update();
//calculateBorders();
}
}
/**
* Gets the alignment as an int.
*
* @return 1: top left ... 9: bottom right
*/
//RLZ: bad function, this is MText style align
int RS_Text::getAlignment() {
if (data.valign==RS_TextData::VATop) {
if (data.halign==RS_TextData::HALeft) {
return 1;
} else if (data.halign==RS_TextData::HACenter) {
return 2;
} else if (data.halign==RS_TextData::HARight) {
return 3;
}
} else if (data.valign==RS_TextData::VAMiddle) {
if (data.halign==RS_TextData::HALeft) {
return 4;
} else if (data.halign==RS_TextData::HACenter) {
return 5;
} else if (data.halign==RS_TextData::HARight) {
return 6;
}
} else if (data.valign==RS_TextData::VABaseline) {
if (data.halign==RS_TextData::HALeft) {
return 7;
} else if (data.halign==RS_TextData::HACenter) {
return 8;
} else if (data.halign==RS_TextData::HARight) {
return 9;
}
} else if (data.valign==RS_TextData::VABottom) {
if (data.halign==RS_TextData::HALeft) {
return 10;
} else if (data.halign==RS_TextData::HACenter) {
return 11;
} else if (data.halign==RS_TextData::HARight) {
return 12;
}
}
if (data.halign==RS_TextData::HAFit) {
return 13;
} else if (data.halign==RS_TextData::HAAligned) {
return 14;
} else if (data.halign==RS_TextData::HAMiddle) {
return 15;
}
return 1;
}
/**
* Sets the alignment from an int.
*
* @param a 1: top left ... 9: bottom right
*/
//RLZ: bad function, this is MText style align
void RS_Text::setAlignment(int a) {
switch (a%3) {
default:
case 1:
data.halign = RS_TextData::HALeft;
break;
case 2:
data.halign = RS_TextData::HACenter;
break;
case 0:
data.halign = RS_TextData::HARight;
break;
}
switch ((int)ceil(a/3.0)) {
default:
case 1:
data.valign = RS_TextData::VATop;
break;
case 2:
data.valign = RS_TextData::VAMiddle;
break;
case 3:
data.valign = RS_TextData::VABaseline;
break;
case 4:
data.valign = RS_TextData::VABottom;
break;
}
if (a > 12) {
data.valign = RS_TextData::VABaseline;
if (a == 13) {
data.halign = RS_TextData::HAFit;
} else if (a == 14) {
data.halign = RS_TextData::HAAligned;
} else if (a == 15) {
data.halign = RS_TextData::HAMiddle;
}
}
}
/**
* @return Number of lines in this text entity.
*/
int RS_Text::getNumberOfLines() {
int c=1;
for (int i=0; i<(int)data.text.length(); ++i) {
if (data.text.at(i).unicode()==0x0A) {
c++;
}
}
return c;
}
/**
* Updates the Inserts (letters) of this text. Called when the
* text or it's data, position, alignment, .. changes.
* This method also updates the usedTextWidth / usedTextHeight property.
*/
void RS_Text::update() {
RS_DEBUG->print("RS_Text::update");
clear();
if (isUndone()) {
return;
}
usedTextWidth = 0.0;
usedTextHeight = 0.0;
RS_Font* font = RS_FONTLIST->requestFont(data.style);
if (font==NULL) {
return;
}
RS_Vector letterPos = RS_Vector(0.0, -9.0);
RS_Vector letterSpace = RS_Vector(font->getLetterSpacing(), 0.0);
RS_Vector space = RS_Vector(font->getWordSpacing(), 0.0);
// First every text line is created with
// alignment: top left
// angle: 0
// height: 9.0
// Rotation, scaling and centering is done later
// For every letter:
for (int i=0; i<(int)data.text.length(); ++i) {
// Space:
if (data.text.at(i).unicode() == 0x20) {
letterPos+=space;
} else {
// One Letter:
QString letterText = QString(data.text.at(i));
if (font->findLetter(letterText) == NULL) {
RS_DEBUG->print("RS_Text::update: missing font for letter( %s ), replaced it with QChar(0xfffd)",qPrintable(letterText));
letterText = QChar(0xfffd);
}
RS_DEBUG->print("RS_Text::update: insert a "
"letter at pos: %f/%f", letterPos.x, letterPos.y);
RS_InsertData d(letterText,
letterPos,
RS_Vector(1.0, 1.0),
0.0,
1,1, RS_Vector(0.0,0.0),
font->getLetterList(), RS2::NoUpdate);
RS_Insert* letter = new RS_Insert(this, d);
RS_Vector letterWidth;
letter->setPen(RS_Pen(RS2::FlagInvalid));
letter->setLayer(NULL);
letter->update();
letter->forcedCalculateBorders();
letterWidth = RS_Vector(letter->getMax().x-letterPos.x, 0.0);
if (letterWidth.x < 0)
letterWidth.x = -letterSpace.x;
// oneLine->addEntity(letter);
addEntity(letter);
// next letter position:
letterPos += letterWidth;
letterPos += letterSpace;
}
}
if( ! RS_EntityContainer::autoUpdateBorders) {
//only update borders when needed
forcedCalculateBorders();
}
RS_Vector textSize = getSize();
RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textSize.x);
// Vertical Align:
double vSize = 9.0;
//HAAligned, HAFit, HAMiddle require VABaseline
if (data.halign == RS_TextData::HAAligned
|| data.halign == RS_TextData::HAFit
|| data.halign == RS_TextData::HAMiddle) {
data.valign = RS_TextData::VABaseline;
}
RS_Vector offset(0.0, 0.0);
switch (data.valign) {
case RS_TextData::VAMiddle:
offset.move(RS_Vector(0.0, vSize/2.0));
break;
case RS_TextData::VABottom:
offset.move(RS_Vector(0.0, vSize+3));
break;
case RS_TextData::VABaseline:
offset.move(RS_Vector(0.0, vSize));
break;
default:
break;
}
// Horizontal Align:
switch (data.halign) {
case RS_TextData::HAMiddle:{
offset.move(RS_Vector(-textSize.x/2.0, -(vSize + textSize.y/2.0 + getMin().y) ));
break;}
case RS_TextData::HACenter:
RS_DEBUG->print("RS_Text::updateAddLine: move by: %f", -textSize.x/2.0);
offset.move(RS_Vector(-textSize.x/2.0, 0.0));
break;
case RS_TextData::HARight:
offset.move(RS_Vector(-textSize.x, 0.0));
break;
default:
break;
}
if (data.halign!=RS_TextData::HAAligned && data.halign!=RS_TextData::HAFit){
data.secondPoint = RS_Vector(offset.x, offset.y - vSize);
}
RS_EntityContainer::move(offset);
// Scale:
if (data.halign==RS_TextData::HAAligned){
double dist = data.insertionPoint.distanceTo(data.secondPoint)/textSize.x;
data.height = vSize*dist;
RS_EntityContainer::scale(RS_Vector(0.0,0.0),
RS_Vector(dist, dist));
} else if (data.halign==RS_TextData::HAFit){
double dist = data.insertionPoint.distanceTo(data.secondPoint)/textSize.x;
RS_EntityContainer::scale(RS_Vector(0.0,0.0),
RS_Vector(dist, data.height/9.0));
} else {
RS_EntityContainer::scale(RS_Vector(0.0,0.0),
RS_Vector(data.height*data.widthRel/9.0, data.height/9.0));
data.secondPoint.scale(RS_Vector(0.0,0.0),
RS_Vector(data.height*data.widthRel/9.0, data.height/9.0));
}
forcedCalculateBorders();
// Update actual text size (before rotating, after scaling!):
usedTextWidth = getSize().x;
usedTextHeight = data.height;
// Rotate:
if (data.halign==RS_TextData::HAAligned || data.halign==RS_TextData::HAFit){
double angle = data.insertionPoint.angleTo(data.secondPoint);
data.angle = angle;
} else {
data.secondPoint.rotate(RS_Vector(0.0,0.0), data.angle);
data.secondPoint.move(data.insertionPoint);
}
RS_EntityContainer::rotate(RS_Vector(0.0,0.0), data.angle);
// Move to insertion point:
RS_EntityContainer::move(data.insertionPoint);
forcedCalculateBorders();
RS_DEBUG->print("RS_Text::update: OK");
}
RS_Vector RS_Text::getNearestEndpoint(const RS_Vector& coord, double* dist)const {
if (dist) {
*dist = data.insertionPoint.distanceTo(coord);
}
return data.insertionPoint;
}
RS_VectorSolutions RS_Text::getRefPoints() const{
RS_VectorSolutions ret({data.insertionPoint, data.secondPoint});
return ret;
}
void RS_Text::move(const RS_Vector& offset) {
RS_EntityContainer::move(offset);
data.insertionPoint.move(offset);
data.secondPoint.move(offset);
// update();
}
void RS_Text::rotate(const RS_Vector& center, const double& angle) {
RS_Vector angleVector(angle);
RS_EntityContainer::rotate(center, angleVector);
data.insertionPoint.rotate(center, angleVector);
data.secondPoint.rotate(center, angleVector);
data.angle = RS_Math::correctAngle(data.angle+angle);
// update();
}
void RS_Text::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
RS_EntityContainer::rotate(center, angleVector);
data.insertionPoint.rotate(center, angleVector);
data.secondPoint.rotate(center, angleVector);
data.angle = RS_Math::correctAngle(data.angle+angleVector.angle());
// update();
}
void RS_Text::scale(const RS_Vector& center, const RS_Vector& factor) {
data.insertionPoint.scale(center, factor);
data.secondPoint.scale(center, factor);
data.height*=factor.x;
update();
}
void RS_Text::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
bool readable = RS_Math::isAngleReadable(data.angle);
RS_Vector vec = RS_Vector::polar(1.0, data.angle);
vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1);
data.angle = vec.angle();
bool corr;
data.angle = RS_Math::makeAngleReadable(data.angle, readable, &corr);
if (corr) {
data.insertionPoint.mirror(axisPoint1, axisPoint2);
data.secondPoint.mirror(axisPoint1, axisPoint2);
if (data.halign==RS_TextData::HALeft) {
data.halign=RS_TextData::HARight;
} else if (data.halign==RS_TextData::HARight) {
data.halign=RS_TextData::HALeft;
} else if (data.halign==RS_TextData::HAFit || data.halign==RS_TextData::HAAligned) {
RS_Vector tmp = data.insertionPoint;
data.insertionPoint = data.secondPoint;
data.secondPoint = tmp;
}
} else {
RS_Vector minP = RS_Vector(getMin().x, getMax().y);
minP = minP.mirror(axisPoint1, axisPoint2);
double mirrAngle = axisPoint1.angleTo(axisPoint2)*2.0;
data.insertionPoint.move(minP - getMin());
data.secondPoint.move(minP - getMin());
data.insertionPoint.rotate(minP, mirrAngle);
data.secondPoint.rotate(minP, mirrAngle);
}
update();
}
bool RS_Text::hasEndpointsWithinWindow(const RS_Vector& /*v1*/, const RS_Vector& /*v2*/) {
return false;
}
/**
* Implementations must stretch the given range of the entity
* by the given offset.
*/
void RS_Text::stretch(const RS_Vector& firstCorner, const RS_Vector& secondCorner, const RS_Vector& offset) {
if (getMin().isInWindow(firstCorner, secondCorner) &&
getMax().isInWindow(firstCorner, secondCorner)) {
move(offset);
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Text& p) {
os << " Text: " << p.getData() << "\n";
return os;
}
void RS_Text::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/)
{
if (!(painter && view)) {
return;
}
if (!view->isPrintPreview() && !view->isPrinting())
{
if (view->isPanning() || view->toGuiDY(getHeight()) < 4)
{
painter->drawRect(view->toGui(getMin()), view->toGui(getMax()));
return;
}
}
foreach (auto e, entities)
{
view->drawEntity(painter, e);
}
}

263
lib/engine/rs_text.h Normal file
View File

@@ -0,0 +1,263 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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!
**
**********************************************************************/
#ifndef RS_TEXT_H
#define RS_TEXT_H
#include "rs_entitycontainer.h"
/**
* Holds the data that defines a text entity.
*/
struct RS_TextData {
/**
* Vertical alignments.
*/
enum VAlign {
VABaseline, /**< Bottom */
VABottom, /**< Bottom */
VAMiddle, /**< Middle */
VATop /**< Top. */
};
/**
* Horizontal alignments.
*/
enum HAlign {
HALeft, /**< Left */
HACenter, /**< Centered */
HARight, /**< Right */
HAAligned, /**< Aligned */
HAMiddle, /**< Middle */
HAFit /**< Fit */
};
/**
* Text drawing direction.
*/
enum TextGeneration {
None, /**< Normal text */
Backward, /**< Mirrored in X */
UpsideDown /**< Mirrored in Y */
};
/**
* Default constructor. Leaves the data object uninitialized.
*/
RS_TextData() = default;
/**
* Constructor with initialisation.
*
* @param insertionPoint Insertion point
* @param secondPoint Second point for aligned-fit
* @param height Nominal (initial) text height
* @param widthRel Reference rectangle width
* @param valign Vertical alignment
* @param halign Horizontal alignment
* @param textGeneration Text Generation
* @param text Text string
* @param style Text style name
* @param angle Rotation angle
* @param updateMode RS2::Update will update the text entity instantly
* RS2::NoUpdate will not update the entity. You can update
* it later manually using the update() method. This is
* often the case since you might want to adjust attributes
* after creating a text entity.
*/
RS_TextData(const RS_Vector& insertionPoint,
const RS_Vector& secondPoint,
double height,
double widthRel,
VAlign valign,
HAlign halign,
TextGeneration textGeneration,
const QString& text,
const QString& style,
double angle,
RS2::UpdateMode updateMode = RS2::Update);
/** Insertion point */
RS_Vector insertionPoint;
/** Second point for fit or aligned*/
RS_Vector secondPoint;
/** Nominal (initial) text height */
double height;
/** Width/Height relation */
double widthRel;
/** Vertical alignment */
VAlign valign;
/** Horizontal alignment */
HAlign halign;
/** Text Generation */
TextGeneration textGeneration;
/** Text string */
QString text;
/** Text style name */
QString style;
/** Rotation angle */
double angle;
/** Update mode */
RS2::UpdateMode updateMode;
};
std::ostream& operator << (std::ostream& os, const RS_TextData& td);
/**
* Class for a text entity.
* Please note that text strings can contain special
* characters such as %%c for a diameter sign as well as unicode
* characters. Line feeds are stored as real line feeds in the string.
*
* @author Andrew Mustun
*/
class RS_Text : public RS_EntityContainer {
public:
RS_Text(RS_EntityContainer* parent,
const RS_TextData& d);
virtual ~RS_Text() = default;
virtual RS_Entity* clone() const override;
/** @return RS2::EntityText */
virtual RS2::EntityType rtti() const override{
return RS2::EntityText;
}
/** @return Copy of data that defines the text. */
RS_TextData getData() const {
return data;
}
void update() override;
int getNumberOfLines();
RS_Vector getInsertionPoint() {
return data.insertionPoint;
}
RS_Vector getSecondPoint() {
return data.secondPoint;
}
double getHeight() {
return data.height;
}
void setHeight(double h) {
data.height = h;
}
double getWidthRel() {
return data.widthRel;
}
void setWidthRel(double w) {
data.widthRel = w;
}
//RLZ: bad functions, this is MText style align
void setAlignment(int a);
int getAlignment();
RS_TextData::VAlign getVAlign() {
return data.valign;
}
void setVAlign(RS_TextData::VAlign va) {
data.valign = va;
}
RS_TextData::HAlign getHAlign() {
return data.halign;
}
void setHAlign(RS_TextData::HAlign ha) {
data.halign = ha;
}
RS_TextData::TextGeneration getTextGeneration() {
return data.textGeneration;
}
void setText(const QString& t);
QString getText() {
return data.text;
}
void setStyle(const QString& s) {
data.style = s;
}
QString getStyle() {
return data.style;
}
void setAngle(double a) {
data.angle = a;
}
double getAngle() {
return data.angle;
}
double getUsedTextWidth() {
return usedTextWidth;
}
double getUsedTextHeight() {
return usedTextHeight;
}
// virtual double getLength() const {
// return -1.0;
// }
/**
* @return The insertion point as endpoint.
*/
virtual RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = NULL)const override;
virtual RS_VectorSolutions getRefPoints() const override;
virtual void move(const RS_Vector& offset) override;
virtual void rotate(const RS_Vector& center, const double& angle) override;
virtual void rotate(const RS_Vector& center, const RS_Vector& angleVector) override;
virtual void scale(const RS_Vector& center, const RS_Vector& factor) override;
virtual void mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) override;
virtual bool hasEndpointsWithinWindow(const RS_Vector& v1, const RS_Vector& v2) override;
virtual void stretch(const RS_Vector& firstCorner,
const RS_Vector& secondCorner,
const RS_Vector& offset) override;
friend std::ostream& operator << (std::ostream& os, const RS_Text& p);
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
protected:
RS_TextData data;
/**
* Text width used by the current contents of this text entity.
* This property is updated by the update method.
* @see update
*/
double usedTextWidth;
/**
* Text height used by the current contents of this text entity.
* This property is updated by the update method.
* @see update
*/
double usedTextHeight;
};
#endif

415
lib/engine/rs_undo.cpp Normal file
View File

@@ -0,0 +1,415 @@
/****************************************************************************
**
** 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 "qc_applicationwindow.h"
#include "rs_undocycle.h"
#include "rs_undo.h"
#include "rs_debug.h"
/**
* @return Number of Cycles that can be undone.
*/
int RS_Undo::countUndoCycles() {
RS_DEBUG->print("RS_Undo::countUndoCycles");
return undoPointer+1;
}
/**
* @return Number of Cycles that can be redone.
*/
int RS_Undo::countRedoCycles() {
RS_DEBUG->print("RS_Undo::countRedoCycles");
return undoList.size()-1-undoPointer;
}
/**
* @return true, when current undo cycle has at least one undoable
*/
bool RS_Undo::hasUndoable()
{
if (nullptr != currentCycle
&& 0 < currentCycle->size()) {
return true;
}
return false;
}
/**
* Adds an Undo Cycle at the current position in the list.
* All Cycles after the new one are removed and the Undoabels
* on them deleted.
*/
void RS_Undo::addUndoCycle(std::shared_ptr<RS_UndoCycle> const& i) {
RS_DEBUG->print("RS_Undo::addUndoCycle");
// undoList.insert(++undoPointer, i);
undoList.insert(undoList.begin() + (++undoPointer), i);
RS_DEBUG->print("RS_Undo::addUndoCycle: ok");
}
/**
* Starts a new cycle for one undo step. Every undoable that is
* added after calling this method goes into this cycle.
*/
void RS_Undo::startUndoCycle()
{
if (1 < ++refCount) {
// only the first fresh top call starts a new cycle
return;
}
size_t removePointer {static_cast<size_t>(undoPointer + 1)};
// if there are undo cycles behind undoPointer
// remove obsolete entities and undoCycles
if (undoList.size() > removePointer) {
// collect remaining undoables
std::list<RS_Undoable*> keep;
for (auto it = undoList.begin(); it != undoList.begin() + removePointer; ++it) {
for (auto u: (*it)->getUndoables()){
keep.push_back( u);
}
}
keep.unique();
// collect obsolete undoables
std::list<RS_Undoable*> obsolete;
for (auto it = undoList.begin() + removePointer; it != undoList.end(); ++it) {
for (auto u: (*it)->getUndoables()){
obsolete.push_back( u);
}
}
// unique() only works correct on sorted list!
obsolete.sort();
obsolete.unique();
// delete obsolete undoables which are not in keep list
for (auto it = obsolete.begin(); it != obsolete.end(); ++it) {
if (keep.end() == std::find( keep.begin(), keep.end(), *it)) {
removeUndoable( *it);
}
}
// clean up obsolete undoCycles
while (undoList.size() > removePointer) {
undoList.pop_back();
}
}
// alloc new undoCycle
currentCycle = std::make_shared<RS_UndoCycle>();
}
/**
* Adds an undoable to the current undo cycle.
*/
void RS_Undo::addUndoable(RS_Undoable* u) {
RS_DEBUG->print("RS_Undo::%s(): begin", __func__);
if( nullptr == currentCycle) {
RS_DEBUG->print( RS_Debug::D_CRITICAL, "RS_Undo::%s(): invalid currentCycle, possibly missing startUndoCycle()", __func__);
return;
}
currentCycle->addUndoable(u);
RS_DEBUG->print("RS_Undo::%s(): end", __func__);
}
/**
* Ends the current undo cycle.
*/
void RS_Undo::endUndoCycle()
{
if (0 < refCount) {
// compensate nested calls of start-/endUndoCycle()
if( 0 < --refCount) {
// not the final nested call, nothing to do yet
return;
}
}
else {
RS_DEBUG->print( RS_Debug::D_WARNING, "Warning: RS_Undo::endUndoCycle() called without previous startUndoCycle() %d", refCount);
return;
}
if (hasUndoable()) {
// only keep the undoCycle, when it contains undoables
addUndoCycle(currentCycle);
}
setGUIButtons();
currentCycle = nullptr; // invalidate currentCycle for next startUndoCycle()
}
/**
* Undoes the last undo cycle.
*/
bool RS_Undo::undo() {
RS_DEBUG->print("RS_Undo::undo");
if (undoPointer < 0) return false;
std::shared_ptr<RS_UndoCycle> uc = undoList[undoPointer--];
setGUIButtons();
uc->changeUndoState();
return true;
}
/**
* Redoes the undo cycle which was at last undone.
*/
bool RS_Undo::redo() {
RS_DEBUG->print("RS_Undo::redo");
if (undoPointer+1 < int(undoList.size())) {
std::shared_ptr<RS_UndoCycle> uc = undoList[++undoPointer];
setGUIButtons();
uc->changeUndoState();
return true;
}
return false;
}
/**
* @return The undo item that is next if we're about to undo
* or nullptr.
*/
/**
std::shared_ptr<RS_UndoCycle> RS_Undo::getUndoCycle() {
std::shared_ptr<RS_UndoCycle> ret;
RS_DEBUG->print("RS_Undo::getUndoCycle");
if ((undoPointer>=0) && (undoPointer < int(undoList.size()))) {
ret = undoList.at(undoPointer);
}
RS_DEBUG->print("RS_Undo::getUndoCycle: OK");
return ret;
}
*/
/**
* @return The redo item that is next if we're about to redo
* or nullptr.
*/
/**
std::shared_ptr<RS_UndoCycle> RS_Undo::getRedoCycle() {
RS_DEBUG->print("RS_Undo::getRedoCycle");
if ((undoPointer+1>=0) && (undoPointer+1 < int(undoList.size()))) {
return undoList.at(undoPointer+1);
}
return std::shared_ptr<RS_UndoCycle>();
}
*/
/**
* enable/disable redo/undo buttons in main application window
* Author: Dongxu Li
**/
void RS_Undo::setGUIButtons() const
{
auto appWin = QC_ApplicationWindow::getAppWindow();
if (!appWin) return;
appWin->setRedoEnable(undoList.size() > 0 &&
undoPointer+1 < int(undoList.size()));
appWin->setUndoEnable(undoList.size() > 0 && undoPointer >= 0);
}
/**
* Dumps the undo list to stdout.
*/
std::ostream& operator << (std::ostream& os, RS_Undo& l) {
os << "Undo List: " << "\n";
os << " Pointer is at: " << l.undoPointer << "\n";
for (int i = 0; i < int(l.undoList.size()); ++i) {
if (i==l.undoPointer)
os << " -->";
else
os << " ";
os << *(l.undoList.at(i)) << "\n";
}
return os;
}
/**
* Testing Undoables, Undo Cycles and the Undo container.
*/
#ifdef RS_TEST
bool RS_Undo::test() {
int i, k;
RS_UndoStub undo;
//RS_UndoCycle* c1;
RS_Undoable* u1;
std::cout << "Testing RS_Undo\n";
std::cout << " Adding 500 cycles..";
// Add 500 Undo Cycles with i Undoables in every Cycle
for (i=1; i<=500; ++i) {
//c1 = new RS_UndoCycle();
undo.startUndoCycle();
for (k=1; k<=i; ++k) {
u1 = new RS_Undoable();
//c1->
undo.addUndoable(u1);
}
//undo.addUndoCycle(c1);
undo.endUndoCycle();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==500);
assert(undo.countRedoCycles()==0);
std::cout << " Undo 500 cycles..";
// Undo all 500 cycles
for (i=1; i<=500; ++i) {
undo.undo();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==0);
assert(undo.countRedoCycles()==500);
std::cout << " Redo 500 cycles..";
// Redo all 500 cycles
for (i=1; i<=500; ++i) {
undo.redo();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==500);
assert(undo.countRedoCycles()==0);
std::cout << " Undo 250 cycles..";
// Undo all 500 cycles
for (i=1; i<=250; ++i) {
undo.undo();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==250);
assert(undo.countRedoCycles()==250);
std::cout << " Adding 10 cycles..";
for (i=1; i<=10; ++i) {
//c1 = new RS_UndoCycle();
undo.startUndoCycle();
for (k=1; k<=10; ++k) {
u1 = new RS_Undoable();
//c1->addUndoable(u1);
undo.addUndoable(u1);
}
//undo.addUndoCycle(c1);
undo.endUndoCycle();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==260);
assert(undo.countRedoCycles()==0);
std::cout << " Undo 5 cycles..";
for (i=1; i<=5; ++i) {
undo.undo();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==255);
assert(undo.countRedoCycles()==5);
std::cout << " Redo 5 cycles..";
for (i=1; i<=5; ++i) {
undo.redo();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==260);
assert(undo.countRedoCycles()==0);
std::cout << " Undo 15 cycles..";
for (i=1; i<=15; ++i) {
undo.undo();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==245);
assert(undo.countRedoCycles()==15);
std::cout << " Adding 1 cycle..";
for (i=1; i<=1; ++i) {
//c1 = new RS_UndoCycle();
undo.startUndoCycle();
for (k=1; k<=10; ++k) {
u1 = new RS_Undoable();
//c1->addUndoable(u1);
undo.addUndoable(u1);
}
//undo.addUndoCycle(c1);
undo.endUndoCycle();
}
std::cout << "OK\n";
assert(undo.countUndoCycles()==246);
assert(undo.countRedoCycles()==0);
return true;
}
#endif

116
lib/engine/rs_undo.h Normal file
View File

@@ -0,0 +1,116 @@
/****************************************************************************
**
** 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!
**
**********************************************************************/
#ifndef RS_UNDO_H
#define RS_UNDO_H
#include <memory>
#include <vector>
class RS_UndoCycle;
class RS_Undoable;
/**
* Undo / redo functionality. The internal undo list consists of
* RS_UndoCycle entries.
*
* @see RS_UndoCycle
* @author Andrew Mustun
*/
class RS_Undo {
public:
virtual ~RS_Undo() = default;
virtual bool undo();
virtual bool redo();
// virtual std::shared_ptr<RS_UndoCycle> getUndoCycle();
// virtual std::shared_ptr<RS_UndoCycle> getRedoCycle();
virtual int countUndoCycles();
virtual int countRedoCycles();
virtual bool hasUndoable();
virtual void startUndoCycle();
virtual void addUndoable(RS_Undoable* u);
virtual void endUndoCycle();
/**
* Must be overwritten by the implementing class and delete
* the given Undoable (unrecoverable). This method is called
* for Undoables that are no longer in the undo buffer.
*/
virtual void removeUndoable(RS_Undoable* u) = 0;
/**
*\brief enable/disable redo/undo buttons in main application window
*\author: Dongxu Li
**/
void setGUIButtons() const;
friend std::ostream& operator << (std::ostream& os, RS_Undo& a);
static bool test();
private:
void addUndoCycle(std::shared_ptr<RS_UndoCycle> const& i);
//! List of undo list items. every item is something that can be undone.
std::vector<std::shared_ptr<RS_UndoCycle>> undoList;
/**
* Index that points to the current position in the undo list.
* The item it points on will be undone the next time undo is called.
* The item after will be redone (if there is an item) when redo
* is called.
*/
int undoPointer = -1;
/**
* Current undo cycle.
*/
std::shared_ptr<RS_UndoCycle> currentCycle {nullptr};
int refCount {0}; ///< reference counter for nested start/end calls
};
/**
* Stub for testing the RS_Undo class.
*/
#ifdef RS_TEST
class RS_UndoStub : public RS_Undo {
virtual void removeUndoable(RS_Undoable* u) {
delete u;
u = NULL;
}
};
#endif
#endif

View File

@@ -0,0 +1,57 @@
/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** 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 "rs_undoable.h"
#include "rs_undocycle.h"
/**
* The undoable thing gets activated if it was undone and
* deactivated otherwise.
*/
void RS_Undoable::changeUndoState() {
toggleFlag(RS2::FlagUndone);
undoStateChanged(isUndone());
}
/**
* Undoes or redoes an undoable.
*/
void RS_Undoable::setUndoState(bool undone) {
if (undone) {
setFlag(RS2::FlagUndone);
} else {
delFlag(RS2::FlagUndone);
}
undoStateChanged(isUndone());
}
/**
* Is this entity in the Undo memory and not active?
*/
bool RS_Undoable::isUndone() const {
return getFlag(RS2::FlagUndone);
}

Some files were not shown because too many files have changed in this diff Show More