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

BIN
lib/R_detection/RMS_Dll.dll Normal file

Binary file not shown.

View File

@@ -0,0 +1,12 @@
#ifndef RMS_DLL_GLOBAL_H
#define RMS_DLL_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(RMS_DLL_LIBRARY)
# define RMS_DLL_EXPORT Q_DECL_EXPORT
#else
# define RMS_DLL_EXPORT Q_DECL_IMPORT
#endif
#endif // RMS_DLL_GLOBAL_H

75
lib/R_detection/rms_dll.h Normal file
View File

@@ -0,0 +1,75 @@
#ifndef RMS_DLL_H
#define RMS_DLL_H
#include"qtcpserver.h"
#include"qtcpsocket.h"
#include "RMS_Dll_global.h"
#include <QImage>
class RMS_DLL_EXPORT RMS_Dll
{
public:
RMS_Dll();
int add(int a,int b);
QVector<double> x;//存储电阻率值
int isshow = 0;
QTcpSocket* _Socket;
//连接探头
bool RMS_Connect(QString IP, quint16 Port);
//取消连接
void RMS_Disconnect();
//RMS_Read_Empty()调用即空采
bool RMS_Read_Empty();
//正常采集传入x坐标y坐标
bool RMS_Read(int cx,int cy);
double RMS_Read_Single();
//所有点采集完成后,获取四个值
void RMS_ACQ(double *max,double *min,double *med,double *mode);//返回最大值,最小值,中位数,众数
//传入mode、Pm和Po获取Pt和Ps
void RMS_P(double mode,double *Pt,double *Ps,double Pm,double Po);
//传入晶锭尺寸size40、60、80分别为四英寸、六英寸和八英寸返回QImage
QImage RMS_Image(int size);
//之前tcp传输的点这回直接用就行
QString StrPlotSixInch = "(0,-60)(-30,-50)(-20,-50)(-10,-50)(0,-50)(10,-50)(20,-50)(30,-50)(-40,-40)(-30,-40)(-20,-40)(-10,-40)(0,-40)(10,-40)"
"(20,-40)(30,-40)(40,-40)(-50,-30)(-40,-30)(-30,-30)(-20,-30)(-10,-30)(0,-30)(10,-30)(20,-30)(30,-30)(40,-30)(50,-30)"
"(-50,-20)(-40,-20)(-30,-20)(-20,-20)(-10,-20)(0,-20)(10,-20)(20,-20)(30,-20)(40,-20)(50,-20)(-50,-10)(-40,-10)(-30,-10)"
"(-20,-10)(-10,-10)(0,-10)(10,-10)(20,-10)(30,-10)(40,-10)(50,-10)(-60,0)(-50,0)(-40,0)(-30,0)(-20,0)(-10,0)(0,0)(10,0)"
"(20,0)(30,0)(40,0)(50,0)(60,0)(-50,10)(-40,10)(-30,10)(-20,10)(-10,10)(0,10)(10,10)(20,10)(30,10)(40,10)(50,10)(-50,20)"
"(-40,20)(-30,20)(-20,20)(-10,20)(0,20)(10,20)(20,20)(30,20)(40,20)(50,20)(-50,30)(-40,30)(-30,30)(-20,30)(-10,30)(0,30)"
"(10,30)(20,30)(30,30)(40,30)(50,30)(-40,40)(-30,40)(-20,40)(-10,40)(0,40)(10,40)(20,40)(30,40)(40,40)(-30,50)(-20,50)"
"(-10,50)(0,50)(10,50)(20,50)(30,50)(0,60)";
QString StrPlotEightInch = "(-44,-72)(-30,-72)(-16,-72)(-2,-72)(12,-72)(26,-72)(40,-72)(-58,-58)"
"(-44,-58)(-30,-58)(-16,-58)(-2,-58)(12,-58)(26,-58)(40,-58)(54,-58)"
"(-72,-44)(-58,-44)(-44,-44)(-30,-44)(-16,-44)(-2,-44)(12,-44)(26,-44)"
"(40,-44)(54,-44)(68,-44)(-72,-30)(-58,-30)(-44,-30)(-30,-30)(-16,-30)"
"(-2,-30)(12,-30)(26,-30)(40,-30)(54,-30)(68,-30)(-72,-16)(-58,-16)"
"(-44,-16)(-30,-16)(-16,-16)(-2,-16)(12,-16)(26,-16)(40,-16)(54,-16)"
"(68,-16)(82,-16)(-72,-2)(-58,-2)(-44,-2)(-30,-2)(-16,-2)(-2,-2)"
"(12,-2)(26,-2)(40,-2)(54,-2)(68,-2)(82,-2)(-72,12)(-58,12)(-44,12)"
"(-30,12)(-16,12)(-2,12)(12,12)(26,12)(40,12)(54,12)(68,12)(82,12)"
"(-72,26)(-58,26)(-44,26)(-30,26)(-16,26)(-2,26)(12,26)(26,26)(40,26)"
"(54,26)(68,26)(-72,40)(-58,40)(-44,40)(-30,40)(-16,40)(-2,40)(12,40)"
"(26,40)(40,40)(54,40)(68,40)(-58,54)(-44,54)(-30,54)(-16,54)(-2,54)"
"(12,54)(26,54)(40,54)(54,54)(-44,68)(-30,68)(-16,68)(-2,68)(12,68)"
"(26,68)(40,68)(-16,82)(-2,82)(12,82)";
QString StrPlotFourInch = "(-2,-44)(-30,-30)(-16,-30)(-2,-30)(12,-30)(26,-30)(-30,-16)(-16,-16)"
"(-2,-16)(12,-16)(26,-16)(40,-16)(-44,-2)(-30,-2)(-16,-2)(-2,-2)(12,-2)"
"(26,-2)(40,-2)(-30,12)(-16,12)(-2,12)(12,12)(26,12)(40,12)(-30,26)(-16,26)"
"(-2,26)(12,26)(26,26)(-16,40)(-2,40)(12,40)";
};
#endif // RMS_DLL_H

BIN
lib/acs/ACSCL_x64.LIB Normal file

Binary file not shown.

BIN
lib/acs/ACSCL_x86.LIB Normal file

Binary file not shown.

View File

@@ -0,0 +1,326 @@
/****************************************************************************
**
** 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 <QKeyEvent>
#include "rs_actioninterface.h"
#include "rs_graphicview.h"
#include "rs_commands.h"
#include "rs_dialogfactory.h"
#include "rs_coordinateevent.h"
#include "rs_debug.h"
/**
* Constructor.
*
* Sets the entity container on which the action class inherited
* from this interface operates.
*
* @param name Action name. This can be used internally for
* debugging mainly.
* @param container Entity container this action operates on.
* @param graphicView Graphic view instance this action operates on.
* Please note that an action belongs to this
* view.
* @param cursor Default mouse cursor for this action. If the action
* is suspended and resumed again the cursor will always
* be reset to the one given here.
*/
RS_ActionInterface::RS_ActionInterface(const char* name,
RS_EntityContainer& container,
RS_GraphicView& graphicView) :
RS_Snapper(container, graphicView) {
RS_DEBUG->print("RS_ActionInterface::RS_ActionInterface: Setting up action: \"%s\"", name);
this->name = name;
status = 0;
finished = false;
//triggerOnResume = false;
// graphic provides a pointer to the graphic if the
// entity container is a graphic (i.e. can also hold
// layers).
graphic = container.getGraphic();
// document pointer will be used for undo / redo
document = container.getDocument();
//this->cursor = cursor;
//setSnapMode(graphicView.getDefaultSnapMode());
actionType=RS2::ActionNone;
RS_DEBUG->print("RS_ActionInterface::RS_ActionInterface: Setting up action: \"%s\": OK", name);
}
/**
* Must be implemented to return the ID of this action.
*
* @todo no default implementation
*/
RS2::ActionType RS_ActionInterface::rtti() const{
return actionType;
}
/**
* @return name of this action
*/
QString RS_ActionInterface::getName() {
return name;
}
void RS_ActionInterface::setName(const char* _name) {
this->name=_name;
}
/**
* Called to initiate an action. This funtcion is often
* overwritten by the implementing action.
*
* @param status The status on which to initiate this action.
* default is 0 to begin the action.
*/
void RS_ActionInterface::init(int status)
{
setStatus(status);
if (status>=0) {
RS_Snapper::init();
updateMouseButtonHints();
updateMouseCursor();
}else{
//delete snapper when finished, bug#3416878
deleteSnapper();
}
}
/**
* Called when the mouse moves and this is the current action.
* This function can be overwritten by the implementing action.
* The default implementation keeps track of the mouse position.
*/
void RS_ActionInterface::mouseMoveEvent(QMouseEvent*) {}
/**
* Called when the left mouse button is pressed and this is the
* current action.
* This function can be overwritten by the implementing action.
* The default implementation does nothing.
*/
void RS_ActionInterface::mousePressEvent(QMouseEvent*) {}
/**
* Called when the left mouse button is released and this is
* the current action.
* This function can be overwritten by the implementing action.
* The default implementation does nothing.
*/
void RS_ActionInterface::mouseReleaseEvent(QMouseEvent*) {}
/**
* Called when a key is pressed and this is the current action.
* This function can be overwritten by the implementing action.
* The default implementation does nothing.
*/
void RS_ActionInterface::keyPressEvent(QKeyEvent* e) {
e->ignore();
}
/**
* Called when a key is released and this is the current action.
* This function can be overwritten by the implementing action.
* The default implementation does nothing.
*/
void RS_ActionInterface::keyReleaseEvent(QKeyEvent* e) {
e->ignore();
}
/**
* Coordinate event. Triggered usually from a command line.
* This function can be overwritten by the implementing action.
* The default implementation does nothing.
*/
void RS_ActionInterface::coordinateEvent(RS_CoordinateEvent*) {}
/**
* Called when a command from the command line is launched.
* and this is the current action.
* This function can be overwritten by the implementing action.
* The default implementation does nothing.
*/
void RS_ActionInterface::commandEvent(RS_CommandEvent*) {
}
/**
* Must be implemented to return the currently available commands
* for the command line.
*/
QStringList RS_ActionInterface::getAvailableCommands() {
return QStringList{};
}
/**
* Sets the current status (progress) of this action.
* The default implementation sets the class variable 'status' to the
* given value and finishes the action if 'status' is negative.
*
* @param status Status number. It's up to the action implementor
* what the action uses the status for. However, a
* negative status number finishes the action. Usually
* the status of an action increases for every step
* of progress and decreases when the user goes one
* step back (i.e. presses the right mouse button).
*/
void RS_ActionInterface::setStatus(int status) {
this->status = status;
updateMouseButtonHints();
updateMouseCursor();
if(status<0) finish();
}
/**
* @return Current status of this action.
*/
int RS_ActionInterface::getStatus() {
return status;
}
/**
* Triggers this action. This should be called after all
* data needed for this action was collected / set.
* The default implementation does nothing.
*/
void RS_ActionInterface::trigger() {}
/**
* Should be overwritten to update the mouse button hints
* wherever they might needed.
*/
void RS_ActionInterface::updateMouseButtonHints() {}
/**
* Should be overwritten to set the mouse cursor for this action.
*/
void RS_ActionInterface::updateMouseCursor() {}
/**
* @return true, if the action is finished and can be deleted.
*/
bool RS_ActionInterface::isFinished() {
return finished;
}
/**
* Forces a termination of the action without any cleanup.
*/
void RS_ActionInterface::setFinished() {
status = -1;
}
/**
* Finishes this action.
*/
void RS_ActionInterface::finish(bool /*updateTB*/)
{
RS_DEBUG->print("RS_ActionInterface::finish");
//refuse to quit the default action
if(rtti() != RS2::ActionDefault) {
status = -1;
finished = true;
hideOptions();
RS_Snapper::finish();
}
RS_DEBUG->print("RS_ActionInterface::finish: OK");
}
/**
* Called by the event handler to give this action a chance to
* communicate with its predecessor.
*/
void RS_ActionInterface::setPredecessor(RS_ActionInterface* pre) {
predecessor = pre;
}
/**
* Suspends this action while another action takes place.
*/
void RS_ActionInterface::suspend() {
RS_Snapper::suspend();
}
/**
* Resumes an action after it was suspended.
*/
void RS_ActionInterface::resume() {
updateMouseCursor();
updateMouseButtonHints();
RS_Snapper::resume();
}
/**
* Hides the tool options. Default implementation does nothing.
*/
void RS_ActionInterface::hideOptions() {
RS_Snapper::hideOptions();
}
/**
* Shows the tool options. Default implementation does nothing.
*/
void RS_ActionInterface::showOptions() {
RS_Snapper::showOptions();
}
void RS_ActionInterface::setActionType(RS2::ActionType actionType){
this->actionType=actionType;
}
/**
* Calls checkCommand() from the RS_COMMANDS module.
*/
bool RS_ActionInterface::checkCommand(const QString& cmd, const QString& str,
RS2::ActionType action) {
return RS_COMMANDS->checkCommand(cmd, str, action);
}
/**
* Calls command() from the RS_COMMANDS module.
*/
QString RS_ActionInterface::command(const QString& cmd) {
return RS_COMMANDS->command(cmd);
}
/**
* Calls msgAvailableCommands() from the RS_COMMANDS module.
*/
QString RS_ActionInterface::msgAvailableCommands() {
return RS_COMMANDS->msgAvailableCommands();
}

View File

@@ -0,0 +1,161 @@
/****************************************************************************
**
** 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_ACTIONINTERFACE_H
#define RS_ACTIONINTERFACE_H
#include <QObject>
#include "rs_snapper.h"
class QKeyEvent;
class RS_CommandEvent;
class RS_CoordinateEvent;
class RS_Graphic;
class RS_Document;
class QAction;
/**
* This is the interface that must be implemented for all
* action classes. Action classes handle actions such
* as drawing lines, moving entities or zooming in.
*
* Inherited from QObject for Qt translation features.
*
* @author Andrew Mustun
*/
class RS_ActionInterface : public QObject, public RS_Snapper {
Q_OBJECT
public:
RS_ActionInterface(const char* name,
RS_EntityContainer& container,
RS_GraphicView& graphicView);
virtual ~RS_ActionInterface() = default;
virtual RS2::ActionType rtti() const;
void setName(const char* _name);
QString getName();
virtual void init(int status=0);
virtual void mouseMoveEvent(QMouseEvent*);
virtual void mousePressEvent(QMouseEvent*);
virtual void mouseReleaseEvent(QMouseEvent*);
virtual void keyPressEvent(QKeyEvent* e);
virtual void keyReleaseEvent(QKeyEvent* e);
virtual void coordinateEvent(RS_CoordinateEvent*);
virtual void commandEvent(RS_CommandEvent*);
virtual QStringList getAvailableCommands();
virtual void setStatus(int status);
virtual int getStatus();
virtual void trigger();
virtual void updateMouseButtonHints();
virtual void updateMouseCursor();
virtual bool isFinished();
virtual void setFinished();
virtual void finish(bool updateTB = true );
virtual void setPredecessor(RS_ActionInterface* pre);
virtual void suspend();
virtual void resume();
virtual void hideOptions();
virtual void showOptions();
virtual void setActionType(RS2::ActionType actionType);
bool checkCommand(const QString& cmd, const QString& str,
RS2::ActionType action=RS2::ActionNone);
QString command(const QString& cmd);
QString msgAvailableCommands();
private:
/**
* Current status of the action. After an action has
* been created the action status is set to 0. Actions
* that are terminated have a stats of -1. Other status
* numbers can be used to describe the stage this action
* is in. E.g. a window zoom consists of selecting the
* first corner (status 0), and selecting the second
* corner (status 1).
*/
int status;
protected:
/** Action name. Used internally for debugging */
QString name;
/**
* This flag is set when the action has terminated and
* can be deleted.
*/
bool finished;
/**
* Pointer to the graphic is this container is a graphic.
* NULL otherwise
*/
RS_Graphic* graphic;
/**
* Pointer to the document (graphic or block) or NULL.
*/
RS_Document* document;
/**
* Pointer to the default mouse cursor for this action or NULL.
*/
//RS2::CursorType cursor;
/**
* Predecessor of this action or NULL.
*/
RS_ActionInterface* predecessor;
/**
* String prepended to the help text for currently available commands.
*/
//static QString msgAvailableCommands;
/**
* Command used for showing help for every action.
*/
//static QString cmdHelp;
/**
* Command for answering yes to a question.
*/
//static QString cmdYes;
//static QString cmdYes2;
/**
* Command for answering no to a question.
*/
//static QString cmdNo;
//static QString cmdNo2;
RS2::ActionType actionType;
};
#endif

190
lib/actions/rs_preview.cpp Normal file
View File

@@ -0,0 +1,190 @@
/****************************************************************************
**
** 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_preview.h"
#include "rs_entitycontainer.h"
#include "rs_line.h"
#include "rs_graphicview.h"
#include "rs_information.h"
#include "rs_settings.h"
/**
* Constructor.
*/
RS_Preview::RS_Preview(RS_EntityContainer* parent)
: RS_EntityContainer(parent, true) {
RS_SETTINGS->beginGroup("/Appearance");
maxEntities = RS_SETTINGS->readNumEntry("/MaxPreview", 100);
RS_SETTINGS->endGroup();
}
/**
* Adds an entity to this preview and removes any attributes / layer
* connections before that.
*/
void RS_Preview::addEntity(RS_Entity* entity) {
if (!entity || entity->isUndone()) {
return;
}
// only border preview for complex entities:
//if ((entity->count() > maxEntities-count()) &&
bool addBorder = false;
if (entity->rtti()==RS2::EntityImage || entity->rtti()==RS2::EntityHatch ||
entity->rtti()==RS2::EntityInsert) {
addBorder = true;
} else {
if (entity->isContainer() && entity->rtti()!=RS2::EntitySpline) {
if (entity->countDeep() > maxEntities-countDeep()) {
addBorder = true;
}
}
}
if (addBorder) {
RS_Vector min = entity->getMin();
RS_Vector max = entity->getMax();
RS_Line* l1 =
new RS_Line(this, {min.x, min.y}, {max.x, min.y});
RS_Line* l2 =
new RS_Line(this, {max.x, min.y}, {max.x, max.y});
RS_Line* l3 =
new RS_Line(this, {max.x, max.y}, {min.x, max.y});
RS_Line* l4 =
new RS_Line(this, {min.x, max.y}, {min.x, min.y});
RS_EntityContainer::addEntity(l1);
RS_EntityContainer::addEntity(l2);
RS_EntityContainer::addEntity(l3);
RS_EntityContainer::addEntity(l4);
delete entity;
entity = nullptr;
} else {
entity->setLayer(nullptr);
entity->setSelected(false);
entity->reparent(this);
// Don't set this pen, let drawing routines decide entity->setPenToActive();
RS_EntityContainer::addEntity(entity);
}
}
/**
* Clones the given entity and adds the clone to the preview.
*/
void RS_Preview::addCloneOf(RS_Entity* entity) {
if (!entity) {
return;
}
RS_Entity* clone = entity->clone();
clone->reparent(this);
addEntity(clone);
}
/**
* Adds all entities from 'container' to the preview (unselected).
*/
void RS_Preview::addAllFrom(RS_EntityContainer& container) {
int c=0;
for(auto e: container){
if (c<maxEntities) {
RS_Entity* clone = e->clone();
clone->setSelected(false);
clone->reparent(this);
c+=clone->countDeep();
addEntity(clone);
// clone might be nullptr after this point
}
}
}
/**
* Adds all selected entities from 'container' to the preview (unselected).
*/
void RS_Preview::addSelectionFrom(RS_EntityContainer& container) {
int c=0;
for(auto e: container){
if (e->isSelected() && c<maxEntities) {
RS_Entity* clone = e->clone();
clone->setSelected(false);
clone->reparent(this);
c+=clone->countDeep();
addEntity(clone);
// clone might be nullptr after this point
}
}
}
/**
* Adds all entities in the given range and those which have endpoints
* in the given range to the preview.
*/
void RS_Preview::addStretchablesFrom(RS_EntityContainer& container,
const RS_Vector& v1, const RS_Vector& v2) {
int c=0;
for(auto e: container){
if (e->isVisible() &&
e->rtti()!=RS2::EntityHatch &&
((e->isInWindow(v1, v2)) ||
e->hasEndpointsWithinWindow(v1, v2)) &&
c<maxEntities) {
RS_Entity* clone = e->clone();
//clone->setSelected(false);
clone->reparent(this);
c+=clone->countDeep();
addEntity(clone);
// clone might be nullptr after this point
}
}
}
void RS_Preview::draw(RS_Painter* painter, RS_GraphicView* view,
double& patternOffset) {
if (!(painter && view)) {
return;
}
foreach (auto e, entities)
{
e->draw(painter, view, patternOffset);
}
}

60
lib/actions/rs_preview.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_PREVIEW_H
#define RS_PREVIEW_H
#include "rs_entitycontainer.h"
/**
* This class supports previewing. The RS_Snapper class uses
* an instance of RS_Preview to preview entities, ranges,
* lines, arcs, ... on the fly.
*
* @author Andrew Mustun
*/
class RS_Preview : public RS_EntityContainer {
public:
RS_Preview(RS_EntityContainer* parent=nullptr);
~RS_Preview() = default;
virtual RS2::EntityType rtti() const override{
return RS2::EntityPreview;
}
virtual void addEntity(RS_Entity* entity) override;
void addCloneOf(RS_Entity* entity);
virtual void addSelectionFrom(RS_EntityContainer& container);
virtual void addAllFrom(RS_EntityContainer& container);
virtual void addStretchablesFrom(RS_EntityContainer& container,
const RS_Vector& v1, const RS_Vector& v2);
void draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) override;
private:
int maxEntities;
};
#endif

View File

@@ -0,0 +1,130 @@
/****************************************************************************
**
** 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_previewactioninterface.h"
#include "rs_graphicview.h"
#include "rs_preview.h"
#include "rs_debug.h"
/**
* Constructor.
*
* Sets the entity container on which the action class inherited
* from this interface operates.
*/
RS_PreviewActionInterface::RS_PreviewActionInterface(const char* name,
RS_EntityContainer& container,
RS_GraphicView& graphicView) :
RS_ActionInterface(name, container, graphicView)
,preview(new RS_Preview(&container))
// ,offset(new RS_Vector{})
{
RS_DEBUG->print("RS_PreviewActionInterface::RS_PreviewActionInterface: Setting up action with preview: \"%s\"", name);
// preview is linked to the container for getting access to
// document settings / dictionary variables
preview->setLayer(NULL);
hasPreview = true;
RS_DEBUG->print("RS_PreviewActionInterface::RS_PreviewActionInterface: Setting up action with preview: \"%s\": OK", name);
}
/** Destructor */
RS_PreviewActionInterface::~RS_PreviewActionInterface() {
deletePreview();
}
void RS_PreviewActionInterface::init(int status) {
deletePreview();
RS_ActionInterface::init(status);
}
void RS_PreviewActionInterface::finish(bool updateTB) {
deletePreview();
RS_ActionInterface::finish(updateTB);
}
void RS_PreviewActionInterface::suspend() {
RS_ActionInterface::suspend();
deletePreview();
}
void RS_PreviewActionInterface::resume() {
RS_ActionInterface::resume();
drawPreview();
}
void RS_PreviewActionInterface::trigger() {
RS_ActionInterface::trigger();
deletePreview();
}
/**
* Deletes the preview from the screen.
*/
void RS_PreviewActionInterface::deletePreview() {
if (hasPreview){
//avoid deleting NULL or empty preview
preview->clear();
hasPreview=false;
}
if(!graphicView->isCleanUp()){
graphicView->getOverlayContainer(RS2::ActionPreviewEntity)->clear();
}
}
/**
* Draws / deletes the current preview.
*/
void RS_PreviewActionInterface::drawPreview() {
// RVT_PORT How does offset work?? painter->setOffset(offset);
RS_EntityContainer *container=graphicView->getOverlayContainer(RS2::ActionPreviewEntity);
container->clear();
container->setOwner(false); // Little hack for now so we don't delete the preview twice
container->addEntity(preview.get());
graphicView->redraw(RS2::RedrawOverlay);
hasPreview=true;
}

View File

@@ -0,0 +1,69 @@
/****************************************************************************
**
** 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_PREVIEWACTIONINTERFACE_H
#define RS_PREVIEWACTIONINTERFACE_H
#include <memory>
#include "rs_actioninterface.h"
/**
* This is the interface that must be implemented for all
* action classes which need a preview.
*
* @author Andrew Mustun
*/
class RS_PreviewActionInterface : public RS_ActionInterface {
public:
RS_PreviewActionInterface(const char* name,
RS_EntityContainer& container,
RS_GraphicView& graphicView);
~RS_PreviewActionInterface() override;
void init(int status=0) override;
void finish(bool updateTB=true) override;
void suspend() override;
void resume() override;
void trigger() override;
void drawPreview();
void deletePreview();
private:
protected:
/**
* Preview that holds the entities to be previewed.
*/
std::unique_ptr<RS_Preview> preview;
bool hasPreview;//whether preview is in use
// /**
// * Current offset of the preview.
// */
// std::unique_ptr<RS_Vector> offset;
};
#endif

1016
lib/actions/rs_snapper.cpp Normal file

File diff suppressed because it is too large Load Diff

237
lib/actions/rs_snapper.h Normal file
View File

@@ -0,0 +1,237 @@
/****************************************************************************
**
** 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!
**
**********************************************************************/
#ifndef RS_SNAPPER_H
#define RS_SNAPPER_H
#include<memory>
#include "rs.h"
class RS_Entity;
class RS_GraphicView;
class RS_Vector;
class RS_Preview;
class QMouseEvent;
class RS_EntityContainer;
/**
* This class holds information on how to snap the mouse.
*
* @author Kevin Cox
*/
struct RS_SnapMode {
/* SnapModes for RS_SnapMode to Int conversion and vice versa
*
* The conversion is only used for save/restore of active snap modes in application settings.
* Don't change existing mode order, because this will mess up settings on upgrades
*
* When adding new values, take care for correct implementation in \p toInt() and \p fromInt()
*/
enum SnapModes {
SnapIntersection = 1 << 0,
SnapOnEntity = 1 << 1,
SnapCenter = 1 << 2,
SnapDistance = 1 << 3,
SnapMiddle = 1 << 4,
SnapEndpoint = 1 << 5,
SnapGrid = 1 << 6,
SnapFree = 1 << 7,
RestrictHorizontal = 1 << 8,
RestrictVertical = 1 << 9,
RestrictOrthogonal = RestrictHorizontal | RestrictVertical,
SnapAngle = 1 << 10
};
bool snapIntersection {false}; //< Whether to snap to intersections or not.
bool snapOnEntity {false}; //< Whether to snap to entities or not.
bool snapCenter {false}; //< Whether to snap to centers or not.
bool snapDistance {false}; //< Whether to snap to distance from endpoints or not.
bool snapMiddle {false}; //< Whether to snap to midpoints or not.
bool snapEndpoint {false}; //< Whether to snap to endpoints or not.
bool snapGrid {false}; //< Whether to snap to grid or not.
bool snapFree {false}; //< Whether to snap freely
bool snapAngle {false}; //< Whether to snap along line under certain angle
RS2::SnapRestriction restriction {RS2::RestrictNothing}; /// The restriction on the free snap.
double distance {5.0}; //< The distance to snap before defaulting to free snapping.
/**
* Disable all snapping.
*
* This effectively puts the object into free snap mode.
*
* @returns A reference to itself.
*/
RS_SnapMode const & clear(void);
bool operator == (RS_SnapMode const& rhs) const;
static uint toInt(const RS_SnapMode& s); //< convert to int, to save settings
static RS_SnapMode fromInt(unsigned int); //< convert from int, to restore settings
};
typedef std::initializer_list<RS2::EntityType> EntityTypeList;
/**
* This class is used for snapping functions in a graphic view.
* Actions are usually derived from this base class if they need
* to catch entities or snap to coordinates. Use the methods to
* retrieve a graphic coordinate from a mouse coordinate.
*
* Possible snapping functions are described in RS_SnapMode.
*
* @author Andrew Mustun
*/
class RS_Snapper {
public:
RS_Snapper() = delete;
RS_Snapper(RS_EntityContainer& container, RS_GraphicView& graphicView);
virtual ~RS_Snapper();
void init();
//!
//! \brief finish stop using snapper
//!
void finish();
/**
* @return Pointer to the entity which was the key entity for the
* last successful snapping action. If the snap mode is "end point"
* the key entity is the entity whose end point was caught.
* If the snap mode didn't require an entity (e.g. free, grid) this
* method will return NULL.
*/
RS_Entity* getKeyEntity() const {
return keyEntity;
}
/** Sets a new snap mode. */
void setSnapMode(const RS_SnapMode& snapMode);
RS_SnapMode const* getSnapMode() const;
RS_SnapMode* getSnapMode();
/** Sets a new snap restriction. */
void setSnapRestriction(RS2::SnapRestriction /*snapRes*/) {
//this->snapRes = snapRes;
}
/**
* Sets the snap range in pixels for catchEntity().
*
* @see catchEntity()
*/
void setSnapRange(int r) {
snapRange = r;
}
/**manually set snapPoint*/
RS_Vector snapPoint(const RS_Vector& coord, bool setSpot = false);
RS_Vector snapPoint(QMouseEvent* e);
RS_Vector snapFree(QMouseEvent* e);
RS_Vector snapFree(const RS_Vector& coord);
RS_Vector snapGrid(const RS_Vector& coord);
RS_Vector snapEndpoint(const RS_Vector& coord);
RS_Vector snapOnEntity(const RS_Vector& coord);
RS_Vector snapCenter(const RS_Vector& coord);
RS_Vector snapMiddle(const RS_Vector& coord);
RS_Vector snapDist(const RS_Vector& coord);
RS_Vector snapIntersection(const RS_Vector& coord);
//RS_Vector snapDirect(RS_Vector coord, bool abs);
RS_Vector snapToAngle(const RS_Vector &coord, const RS_Vector &ref_coord, const double ang_res);
RS_Vector restrictOrthogonal(const RS_Vector& coord);
RS_Vector restrictHorizontal(const RS_Vector& coord);
RS_Vector restrictVertical(const RS_Vector& coord);
//RS_Entity* catchLeafEntity(const RS_Vector& pos);
//RS_Entity* catchLeafEntity(QMouseEvent* e);
RS_Entity* catchEntity(const RS_Vector& pos,
RS2::ResolveLevel level=RS2::ResolveNone);
RS_Entity* catchEntity(QMouseEvent* e,
RS2::ResolveLevel level=RS2::ResolveNone);
// catch Entity closest to pos and of the given entity type of enType, only search for a particular entity type
RS_Entity* catchEntity(const RS_Vector& pos, RS2::EntityType enType,
RS2::ResolveLevel level=RS2::ResolveNone);
RS_Entity* catchEntity(QMouseEvent* e, RS2::EntityType enType,
RS2::ResolveLevel level=RS2::ResolveNone);
RS_Entity* catchEntity(QMouseEvent* e, const EntityTypeList& enTypeList,
RS2::ResolveLevel level=RS2::ResolveNone);
/**
* Suspends this snapper while another action takes place.
*/
virtual void suspend();
/**
* Resumes this snapper after it has been suspended.
*/
virtual void resume() {
drawSnapper();
}
virtual void hideOptions();
virtual void showOptions();
void drawSnapper();
protected:
void deleteSnapper();
double getSnapRange() const;
RS_EntityContainer* container;
RS_GraphicView* graphicView;
RS_Entity* keyEntity;
RS_SnapMode snapMode;
//RS2::SnapRestriction snapRes;
/**
* Snap distance for snapping to points with a
* given distance from endpoints.
*/
double m_SnapDistance;
/**
* Snap to equidistant middle points
* default to 1, i.e., equidistant to start/end points
*/
int middlePoints;
/**
* Snap range for catching entities.
*/
int snapRange;
bool finished{false};
private:
struct ImpData;
std::unique_ptr<ImpData> pImpData;
struct Indicator;
Indicator* snap_indicator{nullptr};
};
#endif
//EOF

Binary file not shown.

View File

@@ -0,0 +1,24 @@
#ifndef COORDINATE_H
#define COORDINATE_H
#include <QtCore/qglobal.h>
#if defined(COORDINATE_LIBRARY)
# define COORDINATESHARED_EXPORT Q_DECL_EXPORT
#else
# define COORDINATESHARED_EXPORT Q_DECL_IMPORT
#endif
class COORDINATESHARED_EXPORT Coordinate
{
public:
Coordinate();
int add(int a,int b);
void getCoordinate(QVector<QVector<int>> **vec,int *row,int *col);
};
#endif // COORDINATE_H

1119
lib/creation/rs_creation.cpp Normal file

File diff suppressed because it is too large Load Diff

164
lib/creation/rs_creation.h Normal file
View File

@@ -0,0 +1,164 @@
/****************************************************************************
**
** 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_CREATION_H
#define RS_CREATION_H
#include "rs_vector.h"
class RS_Document;
class RS_EntityContainer;
class RS_GraphicView;
class RS_Graphic;
class RS_Entity;
class RS_Arc;
class RS_Circle;
class RS_Ellipse;
class RS_Line;
class LC_SplinePoints;
struct RS_BlockData;
struct RS_ImageData;
class RS_Image;
struct RS_InsertData;
class RS_Insert;
class RS_Block;
class QString;
/**
* Data needed to insert library items.
*/
struct RS_LibraryInsertData {
QString file;
RS_Vector insertionPoint;
double factor;
double angle;
};
/**
* Class for the creation of new entities.
* This class is bound to an entity container in which the
* entities are stored.
*
* @author Andrew Mustun
*/
class RS_Creation {
public:
RS_Creation(RS_EntityContainer* container,
RS_GraphicView* graphicView=nullptr,
bool handleUndo=true);
~RS_Creation()=default;
RS_Entity* createParallelThrough(const RS_Vector& coord,
int number,
RS_Entity* e);
RS_Entity* createParallel(const RS_Vector& coord,
double distance,
int number,
RS_Entity* e);
RS_Line* createParallelLine(const RS_Vector& coord,
double distance, int number,
RS_Line* e);
RS_Arc* createParallelArc(const RS_Vector& coord,
double distance, int number,
RS_Arc* e);
RS_Circle* createParallelCircle(const RS_Vector& coord,
double distance, int number,
RS_Circle* e);
LC_SplinePoints* createParallelSplinePoints(const RS_Vector& coord,
double distance, int number,
LC_SplinePoints* e);
RS_Line* createBisector(const RS_Vector& coord1,
const RS_Vector& coord2,
double length,
int num,
RS_Line* l1,
RS_Line* l2);
RS_Line* createTangent1(const RS_Vector& coord,
const RS_Vector& point,
RS_Entity* circle);
/**
* create a tangent line which is orthogonal to the given RS_Line(normal)
*/
RS_Line* createLineOrthTan(const RS_Vector& coord,
RS_Line* normal,
RS_Entity* circle);
RS_Line* createTangent2(const RS_Vector& coord,
RS_Entity* circle1,
RS_Entity* circle2);
/**
* create the path of centers of common tangent circles of the two given circles
*@ return nullptr, if failed
*@ at success return either an ellipse or hyperbola
*/
std::vector<RS_Entity*> createCircleTangent2( RS_Entity* circle1,RS_Entity* circle2);
RS_Line* createLineRelAngle(const RS_Vector& coord,
RS_Entity* entity,
double angle,
double length);
RS_Line* createPolygon(const RS_Vector& center,
const RS_Vector& corner,
int number);
RS_Line* createPolygon2(const RS_Vector& corner1,
const RS_Vector& corner2,
int number);
RS_Line* createPolygon3(const RS_Vector& center,
const RS_Vector& tangent,
int number);
RS_Insert* createInsert(const RS_InsertData* pdata);
RS_Image* createImage(const RS_ImageData* pdata);
RS_Block* createBlock(const RS_BlockData* data,
const RS_Vector& referencePoint,
const bool remove);
RS_Insert* createLibraryInsert(RS_LibraryInsertData& data);
protected:
RS_EntityContainer* container;
RS_Graphic* graphic;
RS_Document* document;
RS_GraphicView* graphicView;
bool handleUndo;
private:
void setEntity(RS_Entity* en) const;
};
#endif

180
lib/debug/rs_debug.cpp Normal file
View File

@@ -0,0 +1,180 @@
/****************************************************************************
**
** 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_debug.h"
#include <iostream>
#include <cstdio>
#include <cstdarg>
#include <QString>
#include <QDateTime>
#include <QDebug>
RS_Debug* RS_Debug::uniqueInstance = nullptr;
void debugHeader(char const* file, char const* func, int line)
{
std::cout<<file<<" : "<<func<<" : line "<<line<<std::endl;
}
/**
* Gets the one and only RS_Debug instance
* (creates a new one on first call only)
*
* @return Pointer to the single instance of this
* singleton class
*/
RS_Debug* RS_Debug::instance() {
if(!uniqueInstance) {
QDateTime now = QDateTime::currentDateTime();
QString nowStr;
nowStr = now.toString("yyyyMMdd_hhmmss");
//QString fName = QString("%1/debug_%2.log")
// .arg(RS_SYSTEM->getHomeDir())
// .arg(nowStr);
// 定义打印日志名称
QString fName = QString("debug_%1.log").arg(nowStr);
uniqueInstance = new RS_Debug;
// 获取日志文件句柄
//uniqueInstance->stream = fopen(fName.latin1(), "wt");
uniqueInstance->stream = stderr;
}
return uniqueInstance;
}
/**
* Deletes the one and only RS_Debug instance.
*/
void
RS_Debug::deleteInstance() {
if (uniqueInstance) {
fclose(uniqueInstance->stream);
delete uniqueInstance;
}
}
/**
* Constructor setting the default debug level.
*/
RS_Debug::RS_Debug()
{
debugLevel = D_DEBUGGING;
}
/**
* Sets the debugging level.
*/
void RS_Debug::setLevel(RS_DebugLevel level) {
if(debugLevel==level) return;
debugLevel = level;
print( D_NOTHING, "RS_DEBUG::setLevel(%d)", level);
print( D_CRITICAL, "RS_DEBUG: Critical");
print( D_ERROR, "RS_DEBUG: Errors");
print( D_WARNING, "RS_DEBUG: Warnings");
print( D_NOTICE, "RS_DEBUG: Notice");
print( D_INFORMATIONAL, "RS_DEBUG: Informational");
print( D_DEBUGGING, "RS_DEBUG: Debugging");
}
/**
* Gets the current debugging level.
*/
RS_Debug::RS_DebugLevel RS_Debug::getLevel() {
return debugLevel;
}
/**
* Prints the given message to stdout.
* 实现把文本字符串写入文件,并且文本字符可以带多个格式参数
*/
void RS_Debug::print(const char* format ...) {
if(debugLevel==D_DEBUGGING) {
// 定义可变参数指针
va_list ap;
// 初始化可变参数指针,指向第一个可变参数
va_start(ap, format);
// 使用参数列表发送格式化输出到流 stream 中
// 参数1这是指向 FILE 对象的指针,该 FILE 对象标识了流
// 参数2这是 C 字符串,包含了要被写入到流 stream 中的文本。它可以包含嵌入的 format 标签format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。
// 参数3可变参数指针
vfprintf(stream, format, ap);
fprintf(stream, "\n");
va_end(ap);
fflush(stream);
}
}
/**
* Prints the given message to stdout if the current debug level
* is lower then the given level
*
* @param level Debug level.
*/
void RS_Debug::print(RS_DebugLevel level, const char* format ...) {
if(debugLevel>=level) {
va_list ap;
va_start(ap, format);
vfprintf(stream, format, ap);
fprintf(stream, "\n");
va_end(ap);
fflush(stream);
}
}
/**
* Prints a time stamp in the format yyyyMMdd_hhmmss.
*/
void RS_Debug::timestamp() {
QDateTime now = QDateTime::currentDateTime();
QString nowStr;
nowStr = now.toString("yyyyMMdd_hh:mm:ss:zzz ");
fprintf(stream, "%s", nowStr.toLatin1().data());
fprintf(stream, "\n");
fflush(stream);
}
/**
* Prints the unicode for every character in the given string.
*/
void RS_Debug::printUnicode(const QString& text) {
for(auto const& v: text){
print("[%X] %c", v.unicode(), v.toLatin1());
}
}
// EOF

109
lib/debug/rs_debug.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_DEBUG_H
#define RS_DEBUG_H
#include <iosfwd>
#ifdef __hpux
#include <sys/_size_t.h>
#endif
#include<QObject>
class QString;
/** print out a debug header*/
#define DEBUG_HEADER debugHeader(__FILE__, __func__, __LINE__);
void debugHeader(char const* file, char const* func, int line);
#define RS_DEBUG RS_Debug::instance()
#define RS_DEBUG_VERBOSE DEBUG_HEADER \
RS_Debug::instance()
/**
* Debugging facilities.
*
* @author Andrew Mustun
*/
class RS_Debug:public QObject
{
Q_OBJECT
public:
/**
* Enum for debug levels. Only messages of the current
* or a higher level are printed.
* <ul>
* <li>D_NOTHING: nothing
* <li>D_CRITICAL: critical messages
* <li>D_ERROR: errors
* <li>D_WARNING: warnings
* <li>D_NOTICE: notes
* <li>D_INFORMATIONAL: infos
* <li>D_DEBUGGING: very verbose
* </ul>
*/
enum RS_DebugLevel { D_NOTHING,
D_CRITICAL,
D_ERROR,
D_WARNING,
D_NOTICE,
D_INFORMATIONAL,
D_DEBUGGING };
private:
RS_Debug();
RS_Debug(const RS_Debug&)=delete;
RS_Debug& operator = (const RS_Debug&)=delete;
RS_Debug(RS_Debug&&)=delete;
RS_Debug& operator = (RS_Debug&&)=delete;
public:
static RS_Debug* instance();
static void deleteInstance();
void setLevel(RS_DebugLevel level);
RS_DebugLevel getLevel();
void print(RS_DebugLevel level, const char* format ...);
void print(const char* format ...);
void printUnicode(const QString& text);
void timestamp();
void setStream(FILE* s) {
stream = s;
}
private:
static RS_Debug* uniqueInstance;
RS_DebugLevel debugLevel;
FILE* stream;
};
#endif
// EOF

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

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