Browse files

Appollonius' problem

Squashed commit of the following:

commit 55329fc
Author: Dongxu Li <dongxuli2011@gmail.com>
Date:   Fri Apr 6 22:40:52 2012 -0400

    Appollonius: solution verifying logic

commit d48397c
Author: Dongxu Li <dongxuli2011@gmail.com>
Date:   Fri Apr 6 19:31:50 2012 -0400

    Appollonius: verify solutions found

commit f2ff016
Author: Dongxu Li <dongxuli2011@gmail.com>
Date:   Fri Apr 6 18:31:36 2012 -0400

    Appollonius: debug selection conditions

commit 1031e35
Author: Dongxu Li <dongxuli2011@gmail.com>
Date:   Fri Apr 6 18:24:42 2012 -0400

    Appollonius: fixed math in quadratic

commit 5645847
Author: Dongxu Li <dongxuli2011@gmail.com>
Date:   Fri Apr 6 18:01:25 2012 -0400

    Appollonius: fixed a crash

commit 2200236
Author: Dongxu Li <dongxuli2011@gmail.com>
Date:   Fri Apr 6 17:26:42 2012 -0400

    Appollonius: initial implementation

commit 25ec3ad
Author: Dongxu Li <dongxuli2011@gmail.com>
Date:   Fri Apr 6 07:16:03 2012 -0400

    Apollonius: added icon
  • Loading branch information...
1 parent 4a3f76b commit 646b4b9b11e06fa3a4bd93a4bdae847ca3957b24 @dxli dxli committed Apr 7, 2012
View
177 desktop/graphics_program_icons/circletan3.svg
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ width="150"
+ height="150"
+ sodipodi:docname="circletan3.png">
+ <metadata
+ id="metadata8">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs6" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1008"
+ inkscape:window-height="596"
+ id="namedview4"
+ showgrid="false"
+ inkscape:object-nodes="true"
+ inkscape:zoom="2.2250293"
+ inkscape:cx="51.834393"
+ inkscape:cy="111.51742"
+ inkscape:window-x="15"
+ inkscape:window-y="31"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <path
+ style="fill:#000000;stroke:#000000;stroke-width:0.44943228;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d=""
+ id="path3868"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke:#000000;stroke-width:0.44943228;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d=""
+ id="path3870"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke:#000000;stroke-width:0.44943228;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d=""
+ id="path3874"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;stroke:#000000;stroke-width:0.44943228;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d=""
+ id="path3876"
+ inkscape:connector-curvature="0" />
+ <g
+ inkscape:groupmode="layer"
+ id="layer1"
+ inkscape:label="a" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3887-2-7"
+ sodipodi:cx="88.987587"
+ sodipodi:cy="125.73066"
+ sodipodi:rx="68.250603"
+ sodipodi:ry="45.8923"
+ d="m 157.23819,125.73066 a 68.250603,45.8923 0 1 1 -136.501206,0 68.250603,45.8923 0 1 1 136.501206,0 z"
+ transform="matrix(0.34690136,-0.00863447,0,0.53707504,72.719368,39.20379)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3887-2"
+ sodipodi:cx="88.987587"
+ sodipodi:cy="125.73066"
+ sodipodi:rx="68.250603"
+ sodipodi:ry="45.8923"
+ d="m 157.23819,125.73066 a 68.250603,45.8923 0 1 1 -136.501206,0 68.250603,45.8923 0 1 1 136.501206,0 z"
+ transform="matrix(0.30635729,-0.00766658,0,0.47687083,75.948072,46.940556)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3887-2-7-9"
+ sodipodi:cx="88.987587"
+ sodipodi:cy="125.73066"
+ sodipodi:rx="68.250603"
+ sodipodi:ry="45.8923"
+ d="m 157.23819,125.73066 a 68.250603,45.8923 0 1 1 -136.501206,0 68.250603,45.8923 0 1 1 136.501206,0 z"
+ transform="matrix(0.36756327,-0.00982665,0,0.61122953,5.9978191,24.895847)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3887-2-75"
+ sodipodi:cx="88.987587"
+ sodipodi:cy="125.73066"
+ sodipodi:rx="68.250603"
+ sodipodi:ry="45.8923"
+ d="m 157.23819,125.73066 a 68.250603,45.8923 0 1 1 -136.501206,0 68.250603,45.8923 0 1 1 136.501206,0 z"
+ transform="matrix(0.32952899,-0.00879399,0,0.54699709,9.5202551,32.809461)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3887-2-7-8"
+ sodipodi:cx="88.987587"
+ sodipodi:cy="125.73066"
+ sodipodi:rx="68.250603"
+ sodipodi:ry="45.8923"
+ d="m 157.23819,125.73066 a 68.250603,45.8923 0 1 1 -136.501206,0 68.250603,45.8923 0 1 1 136.501206,0 z"
+ transform="matrix(0.3630862,-0.00929451,0,0.57813049,43.676025,-4.2281683)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3887-2-6"
+ sodipodi:cx="88.987587"
+ sodipodi:cy="125.73066"
+ sodipodi:rx="68.250603"
+ sodipodi:ry="45.8923"
+ d="m 157.23819,125.73066 a 68.250603,45.8923 0 1 1 -136.501206,0 68.250603,45.8923 0 1 1 136.501206,0 z"
+ transform="matrix(0.32065054,-0.00819401,0,0.50967769,47.055365,4.5713912)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3910-53-4"
+ sodipodi:cx="29.887247"
+ sodipodi:cy="105.05677"
+ sodipodi:rx="7.8650646"
+ sodipodi:ry="7.6403489"
+ d="m 37.752312,105.05677 a 7.8650646,7.6403489 0 1 1 -15.73013,0 7.8650646,7.6403489 0 1 1 15.73013,0 z"
+ transform="matrix(1.17592,0,0,1.230277,41.585167,-60.986598)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#7cfc00;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3910-3-9-6"
+ sodipodi:cx="29.887247"
+ sodipodi:cy="105.05677"
+ sodipodi:rx="7.8650646"
+ sodipodi:ry="7.6403489"
+ d="m 37.752312,105.05677 a 7.8650646,7.6403489 0 1 1 -15.73013,0 7.8650646,7.6403489 0 1 1 15.73013,0 z"
+ transform="matrix(1.0154558,0,0,0.97608483,46.475545,-34.424059)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3887-2-7-9-5"
+ sodipodi:cx="88.987587"
+ sodipodi:cy="125.73066"
+ sodipodi:rx="68.250603"
+ sodipodi:ry="45.8923"
+ d="m 157.23819,125.73066 a 68.250603,45.8923 0 1 1 -136.501206,0 68.250603,45.8923 0 1 1 136.501206,0 z"
+ transform="matrix(0.36756327,-0.00982665,0,0.61122953,82.143826,-36.536184)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3887-2-75-1"
+ sodipodi:cx="88.987587"
+ sodipodi:cy="125.73066"
+ sodipodi:rx="68.250603"
+ sodipodi:ry="45.8923"
+ d="m 157.23819,125.73066 a 68.250603,45.8923 0 1 1 -136.501206,0 68.250603,45.8923 0 1 1 136.501206,0 z"
+ transform="matrix(0.32952899,-0.00879399,0,0.54699709,85.666262,-28.62257)" />
+</svg>
View
BIN librecad/res/extui/circletan3.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
1 librecad/res/extui/extui.qrc
@@ -167,5 +167,6 @@
<file>ellipsecenter3points.png</file>
<file>circleinscribe.png</file>
<file>circletan2.png</file>
+ <file>circletan3.png</file>
</qresource>
</RCC>
View
396 librecad/src/actions/rs_actiondrawcircletan3.cpp
@@ -0,0 +1,396 @@
+/****************************************************************************
+**
+ * Draw circle by foci and a point on circle
+
+Copyright (C) 2012 Dongxu Li (dongxuli2011@gmail.com)
+Copyright (C) 2011 R. van Twisk (librecad@rvt.dds.nl)
+
+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_actiondrawcircletan3.h"
+
+#include <QAction>
+#include "rs_dialogfactory.h"
+#include "rs_graphicview.h"
+#include "rs_commandevent.h"
+
+/**
+ * Constructor.
+ *
+ */
+RS_ActionDrawCircleTan3::RS_ActionDrawCircleTan3(
+ RS_EntityContainer& container,
+ RS_GraphicView& graphicView)
+ :RS_PreviewActionInterface("Draw circle inscribed",
+ container, graphicView),
+ cData(RS_Vector(0.,0.),1.),
+ enTypeList()
+{
+// supported types
+// enTypeList<<RS2::EntityLine<<RS2::EntityArc<<RS2::EntityCircle;
+ enTypeList<<RS2::EntityArc<<RS2::EntityCircle;
+}
+
+
+
+RS_ActionDrawCircleTan3::~RS_ActionDrawCircleTan3() {
+}
+
+
+
+QAction* RS_ActionDrawCircleTan3::createGUIAction(RS2::ActionType /*type*/, QObject* /*parent*/) {
+ QAction* action;
+
+ action = new QAction(tr("Circle Tangential &3"), NULL);
+ action->setIcon(QIcon(":/extui/circletan3.png"));
+ return action;
+}
+
+void RS_ActionDrawCircleTan3::init(int status) {
+ RS_PreviewActionInterface::init(status);
+ if(status>=0) {
+ RS_Snapper::suspend();
+ }
+
+ if (status==SetCircle1) {
+ circles.clear();
+ }
+}
+
+
+void RS_ActionDrawCircleTan3::finish(bool updateTB){
+ if(circles.size()>0){
+ for(int i=0;i<circles.size();i++) {
+ if(circles.at(i) != NULL) circles.at(i)->setHighlighted(false);
+ }
+ graphicView->redraw(RS2::RedrawDrawing);
+ circles.clear();
+ }
+ RS_PreviewActionInterface::finish(updateTB);
+}
+
+//void RS_ActionDrawCircleTan3::finish(bool updateTB){
+//// for(int i=0;i<circles.size();i++) circles[i]->setHighlighted(false);
+//// graphicView->redraw(RS2::RedrawDrawing);
+//// circles.clear();
+// RS_PreviewActionInterface::finish(updateTB);
+//}
+
+
+void RS_ActionDrawCircleTan3::trigger() {
+// std::cout<<__FILE__<<" : "<<__FUNCTION__<<" : line "<<__LINE__<<std::endl;
+// std::cout<<"begin"<<std::endl;
+
+ RS_PreviewActionInterface::trigger();
+
+
+ RS_Circle* circle=new RS_Circle(container, cData);
+
+ container->addEntity(circle);
+
+ // upd. undo list:
+ if (document!=NULL) {
+ document->startUndoCycle();
+ document->addUndoable(circle);
+ document->endUndoCycle();
+ }
+
+ for(int i=0;i<circles.size();i++) circles[i]->setHighlighted(false);
+ graphicView->redraw(RS2::RedrawDrawing);
+// drawSnapper();
+
+ circles.clear();
+ setStatus(SetCircle1);
+
+ RS_DEBUG->print("RS_ActionDrawCircleTan3::trigger():"
+ " entity added: %d", circle->getId());
+}
+
+
+
+void RS_ActionDrawCircleTan3::mouseMoveEvent(QMouseEvent* e) {
+ RS_DEBUG->print("RS_ActionDrawCircleTan3::mouseMoveEvent begin");
+
+ switch(getStatus() ){
+ case SetCenter: {
+// RS_Entity* en = catchEntity(e, enTypeList, RS2::ResolveAll);
+ coord= graphicView->toGraph(e->x(), e->y());
+// circles[getStatus()]=static_cast<RS_Line*>(en);
+ if(preparePreview()) {
+ deletePreview();
+ RS_Circle* e=new RS_Circle(preview, cData);
+ preview->addEntity(e);
+ drawPreview();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ RS_DEBUG->print("RS_ActionDrawCircleTan3::mouseMoveEvent end");
+}
+
+
+bool RS_ActionDrawCircleTan3::getData(){
+ if(getStatus() != SetCircle3) return false;
+ //find the nearest circle
+ RS_Circle c(NULL,cData);
+ candidates=c.createTan3(circles);
+ valid = ( candidates.size() >0);
+ return valid;
+}
+
+bool RS_ActionDrawCircleTan3::preparePreview(){
+ if(getStatus() != SetCenter || valid==false) {
+ valid=false;
+ return false;
+ }
+ //find the nearest circle
+ int index=candidates.size();
+ double dist=RS_MAXDOUBLE*RS_MAXDOUBLE;
+ for(int i=0;i<candidates.size();i++){
+ double d;
+ candidates.at(i).getNearestPointOnEntity(coord,false,&d);
+ if(d<dist){
+ dist=d;
+ index=i;
+ }
+ }
+ if( index<candidates.size()){
+ cData= candidates.at(index).getData();
+ valid=true;
+ }else{
+ valid=false;
+ }
+ return valid;
+}
+
+RS_Entity* RS_ActionDrawCircleTan3::catchCircle(QMouseEvent* e) {
+ RS_Entity* ret=NULL;
+ RS_Entity* en = catchEntity(e,enTypeList, RS2::ResolveAll);
+ if(en == NULL) return ret;
+ if(en->isVisible()==false) return ret;
+ for(int i=0;i<getStatus();i++) {
+ if(en->getId() == circles[i]->getId()) return ret; //do not pull in the same line again
+ }
+ if(en->getParent() != NULL) {
+ if ( en->getParent()->rtti() == RS2::EntityInsert /**Insert*/
+ || en->getParent()->rtti() == RS2::EntitySpline
+ || en->getParent()->rtti() == RS2::EntityText /**< Text 15*/
+ || en->getParent()->rtti() == RS2::EntityDimAligned /**< Aligned Dimension */
+ || en->getParent()->rtti() == RS2::EntityDimLinear /**< Linear Dimension */
+ || en->getParent()->rtti() == RS2::EntityDimRadial /**< Radial Dimension */
+ || en->getParent()->rtti() == RS2::EntityDimDiametric /**< Diametric Dimension */
+ || en->getParent()->rtti() == RS2::EntityDimAngular /**< Angular Dimension */
+ || en->getParent()->rtti() == RS2::EntityDimLeader /**< Leader Dimension */
+ ){
+ return ret;
+ }
+ }
+ return en;
+}
+
+void RS_ActionDrawCircleTan3::mouseReleaseEvent(QMouseEvent* e) {
+ // Proceed to next status
+ if (e->button()==Qt::LeftButton) {
+
+ switch (getStatus()) {
+ case SetCircle1:
+ case SetCircle2:
+ case SetCircle3: {
+ RS_Entity* en = catchCircle(e);
+ if (en==NULL) return;
+ circles.resize(getStatus());
+ for(int i=0;i<circles.size();i++){
+ if(
+ (circles.at(i)->getCenter() - en->getCenter()).squared() < RS_TOLERANCE*RS_TOLERANCE
+ && fabs( circles.at(i)->getRadius() - en->getRadius())<RS_TOLERANCE
+ ) return;
+ }
+ circles.push_back(static_cast<RS_AtomicEntity*>(en));
+ if(getStatus()<=SetCircle2 || (getStatus()==SetCircle3 && getData())){
+ circles.at(circles.size()-1)->setHighlighted(true);
+ graphicView->redraw(RS2::RedrawDrawing);
+ setStatus(getStatus()+1);
+ }
+ }
+ break;
+ case SetCenter:
+ coord= graphicView->toGraph(e->x(), e->y());
+ if( preparePreview()) trigger();
+ break;
+
+ default:
+ break;
+ }
+ } else if (e->button()==Qt::RightButton) {
+ // Return to last status:
+ if(getStatus()>0){
+ circles[getStatus()-1]->setHighlighted(false);
+ circles.pop_back();
+ graphicView->redraw(RS2::RedrawDrawing);
+ deletePreview();
+ }
+ init(getStatus()-1);
+ }
+}
+
+
+//void RS_ActionDrawCircleTan3::coordinateEvent(RS_CoordinateEvent* e) {
+
+//}
+
+//fixme, support command line
+
+/*
+void RS_ActionDrawCircleTan3::commandEvent(RS_CommandEvent* e) {
+ QString c = e->getCommand().toLower();
+
+ if (checkCommand("help", c)) {
+ if (RS_DIALOGFACTORY!=NULL) {
+ RS_DIALOGFACTORY->commandMessage(msgAvailableCommands()
+ + getAvailableCommands().join(", "));
+ }
+ return;
+ }
+
+ switch (getStatus()) {
+ case SetFocus1: {
+ bool ok;
+ double m = RS_Math::eval(c, &ok);
+ if (ok==true) {
+ ratio = m / major.magnitude();
+ if (!isArc) {
+ trigger();
+ } else {
+ setStatus(SetAngle1);
+ }
+ } else {
+ if (RS_DIALOGFACTORY!=NULL) {
+ RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
+ }
+ }
+ }
+ break;
+
+ case SetAngle1: {
+ bool ok;
+ double a = RS_Math::eval(c, &ok);
+ if (ok==true) {
+ angle1 = RS_Math::deg2rad(a);
+ setStatus(SetAngle2);
+ } else {
+ if (RS_DIALOGFACTORY!=NULL) {
+ RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
+ }
+ }
+ }
+ break;
+
+ case SetAngle2: {
+ bool ok;
+ double a = RS_Math::eval(c, &ok);
+ if (ok==true) {
+ angle2 = RS_Math::deg2rad(a);
+ trigger();
+ } else {
+ if (RS_DIALOGFACTORY!=NULL) {
+ RS_DIALOGFACTORY->commandMessage(tr("Not a valid expression"));
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+*/
+
+
+//void RS_ActionDrawCircleTan3::showOptions() {
+// RS_DEBUG->print("RS_ActionDrawCircleTan3::showOptions");
+// if(RS_DIALOGFACTORY != NULL){
+// RS_ActionInterface::showOptions();
+
+// RS_DIALOGFACTORY->requestOptions(this, true);
+// }
+// RS_DEBUG->print("RS_ActionDrawCircleTan3::showOptions: OK");
+//}
+
+
+
+//void RS_ActionDrawCircleTan3::hideOptions() {
+// if(RS_DIALOGFACTORY != NULL){
+// RS_ActionInterface::hideOptions();
+
+// RS_DIALOGFACTORY->requestOptions(this, false);
+// }
+//}
+
+
+QStringList RS_ActionDrawCircleTan3::getAvailableCommands() {
+ QStringList cmd;
+ return cmd;
+}
+
+
+
+void RS_ActionDrawCircleTan3::updateMouseButtonHints() {
+ if (RS_DIALOGFACTORY!=NULL) {
+ switch (getStatus()) {
+ case SetCircle1:
+ RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the first arc/circle"),
+ tr("Cancel"));
+ break;
+
+ case SetCircle2:
+ RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the second arc/circle"),
+ tr("Back"));
+ break;
+ case SetCircle3:
+ RS_DIALOGFACTORY->updateMouseWidget(tr("Specify the third arc/circle"),
+ tr("Back"));
+ break;
+
+ case SetCenter:
+ RS_DIALOGFACTORY->updateMouseWidget(tr("Select the center of the tangent circle"),
+ tr("Back"));
+ break;
+ default:
+ RS_DIALOGFACTORY->updateMouseWidget("", "");
+ break;
+ }
+ }
+}
+
+
+
+void RS_ActionDrawCircleTan3::updateMouseCursor() {
+ graphicView->setMouseCursor(RS2::CadCursor);
+}
+
+
+
+//void RS_ActionDrawCircleTan3::updateToolBar() {
+// if (RS_DIALOGFACTORY!=NULL) {
+// if (isFinished()) {
+// RS_DIALOGFACTORY->resetToolBar();
+// }
+// }
+//}
+
+// EOF
View
94 librecad/src/actions/rs_actiondrawcircletan3.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+ * Draw a common tangent circle of 3 existing circles
+ * Problem of Appollonius
+
+Copyright (C) 2012 Dongxu Li (dongxuli2011@gmail.com)
+Copyright (C) 2011 R. van Twisk (librecad@rvt.dds.nl)
+
+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 RS_ACTIONDRAWCIRCLETAN3_H
+#define RS_ACTIONDRAWCIRCLETAN3_H
+
+#include <QVector>
+#include "rs_circle.h"
+#include "rs_previewactioninterface.h"
+
+/**
+ * Draw ellipse by foci and a point on ellipse
+ *
+ * @author Dongxu Li
+ */
+class RS_ActionDrawCircleTan3 : public RS_PreviewActionInterface {
+ Q_OBJECT
+public:
+ /**
+ * Action States.
+ */
+ enum Status {
+ SetCircle1, // Setting the First Circle. */
+ SetCircle2, // Setting the Second Circle. */
+ SetCircle3, // Setting the Third Circle. */
+ SetCenter // select the closest tangential Circle. */
+ };
+
+public:
+ RS_ActionDrawCircleTan3(RS_EntityContainer& container,
+ RS_GraphicView& graphicView);
+ ~RS_ActionDrawCircleTan3();
+
+ static QAction* createGUIAction(RS2::ActionType type, QObject* /*parent*/);
+
+ virtual RS2::ActionType rtti() {
+ return RS2::ActionDrawCircleTan3;
+ }
+ virtual void init(int status=0);
+
+ virtual void trigger();
+ virtual bool getData();
+ virtual bool preparePreview();
+
+ virtual void mouseMoveEvent(QMouseEvent* e);
+ virtual void mouseReleaseEvent(QMouseEvent* e);
+
+ // virtual void coordinateEvent(RS_CoordinateEvent* e);
+ // virtual void commandEvent(RS_CommandEvent* e);
+ virtual QStringList getAvailableCommands();
+ virtual void finish(bool updateTB=true);
+ virtual void updateMouseButtonHints();
+ virtual void updateMouseCursor();
+// virtual void updateToolBar();
+
+// virtual void showOptions();
+// virtual void hideOptions();
+
+
+
+protected:
+ RS_Entity* catchCircle(QMouseEvent* e);
+ QVector<RS_AtomicEntity*> circles;
+ private:
+ RS_CircleData cData;
+ RS_Vector coord;
+ bool valid;
+ QVector<RS2::EntityType> enTypeList;
+ //keep a list of centers found
+ QList<RS_Circle> candidates;
+
+};
+
+#endif
View
2 librecad/src/lib/creation/rs_creation.cpp
@@ -825,7 +825,7 @@ RS_Line* RS_Creation::createTangent2(const RS_Vector& coord,
m.push_back(a*a*b*b); //mb11
- auto&& vs0=RS_Math::simultaneusQuadraticSolver(m); //to hold solutions
+ auto&& vs0=RS_Math::simultaneousQuadraticSolver(m); //to hold solutions
if (vs0.getNumber()<1) return NULL;
for(int i=0;i<vs0.getNumber();i++){
// std::cout<<"i="<<i<<"\n";
View
1 librecad/src/lib/engine/rs.h
@@ -237,6 +237,7 @@ class RS2 {
ActionDrawCircleParallel,
ActionDrawCircleInscribe,
ActionDrawCircleTan2,
+ ActionDrawCircleTan3,
ActionDrawEllipseArcAxis,
ActionDrawEllipseAxis,
View
107 librecad/src/lib/engine/rs_circle.cpp
@@ -281,6 +281,113 @@ RS_VectorSolutions RS_Circle::createTan2(const QVector<RS_AtomicEntity*>& circle
}
+QList<RS_Circle> RS_Circle::createTan3(const QVector<RS_AtomicEntity*>& circles)
+{
+ QList<RS_Circle> ret;
+ if(circles.size()!=3) return ret;
+ QList<RS_Circle> cs;
+ for(unsigned short i=0;i<3;i++){
+ cs<<RS_Circle(NULL,RS_CircleData(circles.at(i)->getCenter(),circles.at(i)->getRadius()));
+ }
+ unsigned short flags=0;
+ do{
+ ret.append(solveAppolloniusSingle(cs));
+ flags++;
+ size_t j=0;
+ for(unsigned short i=1u;i<=4u;i<<=1){
+ if(flags & i) {
+ cs[j].setRadius( - fabs(cs[j].getRadius()));
+ }else{
+ cs[j].setRadius( fabs(cs[j].getRadius()));
+ }
+ j++;
+ }
+
+ }while(flags<8u);
+// std::cout<<__FILE__<<" : "<<__FUNCTION__<<" : line "<<__LINE__<<std::endl;
+// std::cout<<"before testing, ret.size()="<<ret.size()<<std::endl;
+ for(int i=0;i<ret.size();){
+ if(ret[i].testTan3(circles) == false) {
+ ret.erase(ret.begin()+i);
+ }else{
+ i++;
+ }
+ }
+// std::cout<<"after testing, ret.size()="<<ret.size()<<std::endl;
+ return ret;
+}
+
+bool RS_Circle::testTan3(const QVector<RS_AtomicEntity*>& circles)
+{
+
+ if(circles.size()!=3) return false;
+
+ const auto itEnd=circles.end();
+// std::cout<<__FILE__<<" : "<<__FUNCTION__<<" : line "<<__LINE__<<std::endl;
+// std::cout<<"to verify Center = ( "<<data.center.x<<" , "<<data.center.y<<" ), r= "<<data.radius<<std::endl;
+ for(auto it=circles.begin();it!=itEnd;it++){
+// std::cout<<"with Center = ( "<<(*it)->getCenter().x<<" , "<<(*it)->getCenter().y<<" ), r= "<<(*it)->getRadius()<<std::endl;
+ const double dist=(data.center - (*it)->getCenter()).magnitude();
+ if( fabs(dist - fabs( data.radius - (*it)->getRadius()))>RS_TOLERANCE && fabs(dist - data.radius - (*it)->getRadius())>RS_TOLERANCE ) return false;
+// std::cout<<"accepted"<<std::endl;
+ }
+ 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
+**/
+QList<RS_Circle> RS_Circle::solveAppolloniusSingle(const QList<RS_Circle>& circles)
+{
+ QList<RS_Circle> ret;
+
+ QList<RS_Vector> centers;
+ QList<double> radii;
+
+ for(size_t i=0;i<3;i++){
+ if(circles[i].getCenter().valid==false) return ret;
+ centers.push_back(circles[i].getCenter());
+ radii.push_back(circles[i].getRadius());
+ }
+/** form the linear equation to solve center in radius **/
+ QVector<QVector<double> > mat(2,QVector<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;
+ // 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]);
+ QVector<QVector<double> > sm(2,QVector<double>(2,0.));
+ if(RS_Math::linearSolver(mat,sm[0])==false){
+ return ret;
+ }
+ // r term
+ mat[0][2]= radii[0]-radii[2];
+ mat[1][2]= radii[1]-radii[2];
+ if(RS_Math::linearSolver(mat,sm[1])==false){
+ return ret;
+ }
+ //form quadratic equation for r
+ RS_Vector vp(sm[0][0],sm[0][1]);
+ RS_Vector vq(sm[1][0],sm[1][1]);
+ RS_Vector cp=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.*(radii[0]+cp.dotP(vq))/a);
+ ce.push_back((cp.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<<RS_Circle(NULL,RS_CircleData(vp+vq*vr.at(i),fabs(vr.at(i))));
+ }
+ return ret;
+}
+
RS_VectorSolutions RS_Circle::getRefPoints() {
RS_Vector v1(data.radius, 0.0);
RS_Vector v2(0.0, data.radius);
View
11 librecad/src/lib/engine/rs_circle.h
@@ -100,7 +100,7 @@ class RS_Circle : public RS_AtomicEntity {
}
/** @return Copy of data that defines the circle. **/
- RS_CircleData getData() {
+ RS_CircleData getData() const {
return data;
}
@@ -155,7 +155,14 @@ class RS_Circle : public RS_AtomicEntity {
bool createInscribe(const RS_Vector& coord, const QVector<RS_Line*>& lines);
virtual QVector<RS_Entity* > offsetTwoSides(const double& distance) const;
RS_VectorSolutions createTan2(const QVector<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 QList<RS_Circle> solveAppolloniusSingle(const QList<RS_Circle>& circles);
+
+ QList<RS_Circle> createTan3(const QVector<RS_AtomicEntity*>& circles);
+bool testTan3(const QVector<RS_AtomicEntity*>& circles);
virtual RS_Vector getMiddlePoint(void)const;
virtual RS_Vector getNearestEndpoint(const RS_Vector& coord,
double* dist = NULL)const;
View
5 librecad/src/lib/engine/rs_vector.cpp
@@ -522,6 +522,11 @@ RS_Vector RS_Vector::operator - () const {
/**
* Scalarproduct (dot product).
*/
+double RS_Vector::dotP(const RS_Vector& v1)
+{
+ return x*v1.x+y*v1.y;
+}
+
double RS_Vector::dotP(const RS_Vector& v1, const RS_Vector& v2) {
#ifdef RS_VECTOR2D
return v1.x * v2.x + v1.y * v2.y;
View
1 librecad/src/lib/engine/rs_vector.h
@@ -81,6 +81,7 @@ class RS_Vector {
RS_Vector scale(const RS_Vector& factor);
RS_Vector scale(const RS_Vector& center, const RS_Vector& factor);
RS_Vector mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2);
+ double dotP(const RS_Vector& v1);
RS_Vector operator + (const RS_Vector& v) const;
RS_Vector operator - (const RS_Vector& v) const;
View
2 librecad/src/lib/information/rs_information.cpp
@@ -695,7 +695,7 @@ RS_VectorSolutions RS_Information::getIntersectionEllipseEllipse(RS_Ellipse* e1,
m.push_back(( y2*tcssi - 2.*x2*cs2)*ia2 - ( y2*tcssi+2*x2*si2)*ib2); //mb10
m.push_back( ( x2*tcssi - 2.*y2*si2)*ia2 - ( x2*tcssi+2*y2*cs2)*ib2); //mb11
m.push_back((ucs - vsi)*(ucs-vsi)*ia2+(usi+vcs)*(usi+vcs)*ib2 -1.); //mc1
- auto&& vs0=RS_Math::simultaneusQuadraticSolver(m);
+ auto&& vs0=RS_Math::simultaneousQuadraticSolver(m);
shifta1 = - shifta1;
shiftc1 = - shiftc1;
for(int i=0; i<vs0.getNumber(); i++) {
View
2 librecad/src/lib/math/rs_math.cpp
@@ -746,7 +746,7 @@ double RS_Math::ellipticIntegral_2(const double& k, const double& phi)
* m[0] m[1] must be positive
*@return a vector contains real roots
*/
-RS_VectorSolutions RS_Math::simultaneusQuadraticSolver(const std::vector<double>& m)
+RS_VectorSolutions RS_Math::simultaneousQuadraticSolver(const std::vector<double>& m)
{
RS_VectorSolutions ret(0);
if(m.size() != 8 ) return ret; // valid m should contain exact 8 elements
View
12 librecad/src/lib/math/rs_math.h
@@ -99,7 +99,15 @@ class RS_Math {
static std::vector<double> cubicSolver(const std::vector<double>& ce);
static std::vector<double> quarticSolver(const std::vector<double>& ce);
//solver for linear equation set
- static bool linearSolver(const QVector<QVector<double> >& m, QVector<double>& dn);
+ /**
+ * Solve linear equation set
+ *@ mt holds the augmented matrix
+ *@ sn holds the solution
+ *@ return true, if the equation set has a unique solution, return false otherwise
+ *
+ *@Author: Dongxu Li
+ */
+ static bool linearSolver(const QVector<QVector<double> >& m, QVector<double>& sn);
/** solver quadratic simultaneous equations of a set of two **/
/* solve the following quadratic simultaneous equations,
@@ -110,7 +118,7 @@ class RS_Math {
ma000 ma011 ma100 ma101 ma111 mb10 mb11 mc1
*@return a RS_VectorSolutions contains real roots (x,y)
*/
- static RS_VectorSolutions simultaneusQuadraticSolver(const std::vector<double>& m);
+ static RS_VectorSolutions simultaneousQuadraticSolver(const std::vector<double>& m);
/** wrapper for elliptic integral **/
/**
View
3 librecad/src/main/qc_applicationwindow.cpp
@@ -1007,6 +1007,9 @@ void QC_ApplicationWindow::initActions(void)
action = actionFactory.createAction(RS2::ActionDrawCircleTan2, actionHandler);
subMenu->addAction(action);
connect(this, SIGNAL(windowsChanged(bool)), action, SLOT(setEnabled(bool)));
+ action = actionFactory.createAction(RS2::ActionDrawCircleTan3, actionHandler);
+ subMenu->addAction(action);
+ connect(this, SIGNAL(windowsChanged(bool)), action, SLOT(setEnabled(bool)));
// Ellipses:
subMenu= menu->addMenu(tr("&Ellipse"));
View
2 librecad/src/src.pro
@@ -311,6 +311,7 @@ HEADERS += actions/rs_actionblocksadd.h \
actions/rs_actiondrawcirclecr.h \
actions/rs_actiondrawcircleinscribe.h \
actions/rs_actiondrawcircletan2.h \
+ actions/rs_actiondrawcircletan3.h \
actions/rs_actiondrawellipseaxis.h \
actions/rs_actiondrawellipsefocipoint.h \
actions/rs_actiondrawellipse4points.h \
@@ -437,6 +438,7 @@ SOURCES += actions/rs_actionblocksadd.cpp \
actions/rs_actiondrawcirclecr.cpp \
actions/rs_actiondrawcircleinscribe.cpp \
actions/rs_actiondrawcircletan2.cpp \
+ actions/rs_actiondrawcircletan3.cpp \
actions/rs_actiondrawellipseaxis.cpp \
actions/rs_actiondrawellipsefocipoint.cpp \
actions/rs_actiondrawellipse4points.cpp \
View
12 librecad/src/ui/forms/qg_cadtoolbarcircles.cpp
@@ -122,6 +122,11 @@ void QG_CadToolBarCircles::drawCircleTan2() {
actionHandler->slotDrawCircleTan2();
}
}
+void QG_CadToolBarCircles::drawCircleTan3() {
+ if (cadToolBar!=NULL && actionHandler!=NULL) {
+ actionHandler->slotDrawCircleTan3();
+ }
+}
void QG_CadToolBarCircles::back() {
if (cadToolBar!=NULL) {
cadToolBar->back();
@@ -159,6 +164,10 @@ void QG_CadToolBarCircles::restoreAction()
actionHandler->slotDrawCircleTan2();
return;
}
+ if ( bCircleTan3 ->isChecked() ) {
+ actionHandler->slotDrawCircleTan3();
+ return;
+ }
//clear all action
bHidden->setChecked(true);
RS_ActionInterface* currentAction =actionHandler->getCurrentAction();
@@ -198,6 +207,9 @@ void QG_CadToolBarCircles::showCadToolBar(RS2::ActionType actionType){
case RS2::ActionDrawCircleTan2:
bCircleTan2->setChecked(true);
return;
+ case RS2::ActionDrawCircleTan3:
+ bCircleTan3->setChecked(true);
+ return;
default:
bHidden->setChecked(true);
return;
View
1 librecad/src/ui/forms/qg_cadtoolbarcircles.h
@@ -52,6 +52,7 @@ public slots:
virtual void drawCircleParallel();
virtual void drawCircleInscribe();
virtual void drawCircleTan2();
+ virtual void drawCircleTan3();
virtual void back();
virtual void resetToolBar();
virtual void showCadToolBar(RS2::ActionType actionType);
View
58 librecad/src/ui/forms/qg_cadtoolbarcircles.ui
@@ -365,6 +365,48 @@ p, li { white-space: pre-wrap; }
<bool>true</bool>
</property>
</widget>
+ <widget class="QToolButton" name="bCircleTan3">
+ <property name="geometry">
+ <rect>
+ <x>33</x>
+ <y>120</y>
+ <width>32</width>
+ <height>32</height>
+ </rect>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32</width>
+ <height>32</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Sans Serif';&quot;&gt;Draw a tangential Circle of three circles&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../../../res/extui/extui.qrc">
+ <normaloff>:/extui/circletan3.png</normaloff>:/extui/circletan3.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>32</width>
+ <height>32</height>
+ </size>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="autoExclusive">
+ <bool>true</bool>
+ </property>
+ </widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
@@ -491,5 +533,21 @@ p, li { white-space: pre-wrap; }
</hint>
</hints>
</connection>
+ <connection>
+ <sender>bCircleTan3</sender>
+ <signal>clicked()</signal>
+ <receiver>QG_CadToolBarCircles</receiver>
+ <slot>drawCircleTan3()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>48</x>
+ <y>135</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>32</x>
+ <y>167</y>
+ </hint>
+ </hints>
+ </connection>
</connections>
</ui>
View
7 librecad/src/ui/qg_actionfactory.cpp
@@ -54,6 +54,7 @@
#include "rs_actiondrawcirclecr.h"
#include "rs_actiondrawcircleinscribe.h"
#include "rs_actiondrawcircletan2.h"
+#include "rs_actiondrawcircletan3.h"
#include "rs_actiondrawellipseaxis.h"
#include "rs_actiondrawellipsefocipoint.h"
#include "rs_actiondrawellipse4points.h"
@@ -668,6 +669,12 @@ QAction* QG_ActionFactory::createAction( RS2::ActionType id, QObject* obj,
obj, SLOT(slotDrawCircleTan2()));
break;
+ case RS2::ActionDrawCircleTan3:
+ action = RS_ActionDrawCircleTan3::createGUIAction(id, mw);
+ connect(action, SIGNAL(triggered()),
+ obj, SLOT(slotDrawCircleTan3()));
+ break;
+
case RS2::ActionDrawArc:
action = RS_ActionDrawArc::createGUIAction(id, mw);
connect(action, SIGNAL(triggered()),
View
7 librecad/src/ui/qg_actionhandler.cpp
@@ -55,6 +55,7 @@
#include "rs_actiondrawcirclecr.h"
#include "rs_actiondrawcircleinscribe.h"
#include "rs_actiondrawcircletan2.h"
+#include "rs_actiondrawcircletan3.h"
#include "rs_actiondrawellipseaxis.h"
#include "rs_actiondrawellipsefocipoint.h"
#include "rs_actiondrawellipse4points.h"
@@ -476,6 +477,9 @@ RS_ActionInterface* QG_ActionHandler::setCurrentAction(RS2::ActionType id) {
case RS2::ActionDrawCircleTan2:
a = new RS_ActionDrawCircleTan2(*doc, *gv);
break;
+ case RS2::ActionDrawCircleTan3:
+ a = new RS_ActionDrawCircleTan3(*doc, *gv);
+ break;
case RS2::ActionDrawArc:
a = new RS_ActionDrawArc(*doc, *gv);
break;
@@ -1354,6 +1358,9 @@ void QG_ActionHandler::slotDrawCircleInscribe() {
void QG_ActionHandler::slotDrawCircleTan2() {
setCurrentAction(RS2::ActionDrawCircleTan2);
}
+void QG_ActionHandler::slotDrawCircleTan3() {
+ setCurrentAction(RS2::ActionDrawCircleTan3);
+}
void QG_ActionHandler::slotDrawArc() {
setCurrentAction(RS2::ActionDrawArc);
}
View
1 librecad/src/ui/qg_actionhandler.h
@@ -125,6 +125,7 @@ public slots:
void slotDrawCircleParallel();
void slotDrawCircleInscribe();
void slotDrawCircleTan2();
+ void slotDrawCircleTan3();
void slotDrawArc();
void slotDrawArc3P();
void slotDrawArcParallel();

0 comments on commit 646b4b9

Please sign in to comment.