PyObject* GeometryPy::copy(PyObject *args)
{
    if (!PyArg_ParseTuple(args, ""))
        return NULL;

    Part::Geometry* geom = this->getGeometryPtr();
    PyTypeObject* type = this->GetType();
    PyObject* cpy = 0;
    // let the type object decide
    if (type->tp_new)
        cpy = type->tp_new(type, this, 0);
    if (!cpy) {
        PyErr_SetString(PyExc_TypeError, "failed to create copy of geometry");
        return 0;
    }

    Part::GeometryPy* geompy = static_cast<Part::GeometryPy*>(cpy);
    // the PyMake function must have created the corresponding instance of the 'Geometry' subclass
    // so delete it now to avoid a memory leak
    if (geompy->_pcTwinPointer) {
        Part::Geometry* clone = static_cast<Part::Geometry*>(geompy->_pcTwinPointer);
        delete clone;
    }
    geompy->_pcTwinPointer = geom->copy();
    return cpy;
}
void SketcherValidation::on_findReversed_clicked()
{
    std::vector<Base::Vector3d> points;
    const std::vector<Part::Geometry *>& geom = sketch->getExternalGeometry();
    for (std::size_t i=0; i<geom.size(); i++) {
        Part::Geometry* g = geom[i];
        //only arcs of circles need to be repaired. Arcs of ellipse were so broken there should be nothing to repair from.
        if (g->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
            const Part::GeomArcOfCircle *segm = static_cast<const Part::GeomArcOfCircle*>(g);
            if (segm->isReversed()) {
                points.push_back(segm->getStartPoint(/*emulateCCW=*/true));
                points.push_back(segm->getEndPoint(/*emulateCCW=*/true));
            }
        }
    }
    hidePoints();
    if(points.size()>0){
        int nc = sketch->port_reversedExternalArcs(/*justAnalyze=*/true);
        showPoints(points);
        if(nc>0){
            QMessageBox::warning(this, tr("Reversed external geometry"),
                tr("%1 reversed external-geometry arcs were found. Their endpoints are"
                   " encircled in 3d view.\n\n"
                   "%2 constraints are linking to the endpoints. The constraints have"
                   " been listed in Report view (menu View -> Views -> Report view).\n\n"
                   "Click \"Swap endpoints in constraints\" button to reassign endpoints."
                   " Do this only once to sketches created in FreeCAD older than v0.15.???"
                   ).arg(points.size()/2).arg(nc)
                                 );
            ui->swapReversed->setEnabled(true);
        } else {
            QMessageBox::warning(this, tr("Reversed external geometry"),
                tr("%1 reversed external-geometry arcs were found. Their endpoints are "
                   "encircled in 3d view.\n\n"
                   "However, no constraints linking to the endpoints were found.").arg(points.size()/2));
            ui->swapReversed->setEnabled(false);
        }
    } else {
        QMessageBox::warning(this, tr("Reversed external geometry"),
            tr("No reversed external-geometry arcs were found."));
    }
}
PyObject* SketchObjectPy::addGeometry(PyObject *args)
{
    PyObject *pcObj; 
    PyObject* construction; // this is an optional argument default false
    bool isConstruction;
    if (!PyArg_ParseTuple(args, "OO!", &pcObj, &PyBool_Type, &construction)) {
        PyErr_Clear();
        if (!PyArg_ParseTuple(args, "O", &pcObj))
            return 0;
        else
            isConstruction=false;
    }
    else {
        isConstruction = PyObject_IsTrue(construction) ? true : false;
    }

    if (PyObject_TypeCheck(pcObj, &(Part::GeometryPy::Type))) {
        Part::Geometry *geo = static_cast<Part::GeometryPy*>(pcObj)->getGeometryPtr();
        int ret;
        // An arc created with Part.Arc will be converted into a Part.ArcOfCircle
        if (geo->getTypeId() == Part::GeomTrimmedCurve::getClassTypeId()) {
            Handle_Geom_TrimmedCurve trim = Handle_Geom_TrimmedCurve::DownCast(geo->handle());
            Handle_Geom_Circle circle = Handle_Geom_Circle::DownCast(trim->BasisCurve());
            Handle_Geom_Ellipse ellipse = Handle_Geom_Ellipse::DownCast(trim->BasisCurve());
            if (!circle.IsNull()) {
                // create the definition struct for that geom
                Part::GeomArcOfCircle aoc;
                aoc.setHandle(trim);
                ret = this->getSketchObjectPtr()->addGeometry(&aoc,isConstruction);
            }
            else if (!ellipse.IsNull()) {
                // create the definition struct for that geom
                Part::GeomArcOfEllipse aoe;
                aoe.setHandle(trim);
                ret = this->getSketchObjectPtr()->addGeometry(&aoe,isConstruction);
            }             
            else {
                std::stringstream str;
                str << "Unsupported geometry type: " << geo->getTypeId().getName();
                PyErr_SetString(PyExc_TypeError, str.str().c_str());
                return 0;
            }
        }
        else if (geo->getTypeId() == Part::GeomPoint::getClassTypeId() ||
                 geo->getTypeId() == Part::GeomCircle::getClassTypeId() ||
                 geo->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
                 geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
                 geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
                 geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
            ret = this->getSketchObjectPtr()->addGeometry(geo,isConstruction);
        }
        else {
            std::stringstream str;
            str << "Unsupported geometry type: " << geo->getTypeId().getName();
            PyErr_SetString(PyExc_TypeError, str.str().c_str());
            return 0;
        }
        return Py::new_reference_to(Py::Int(ret));
    }
    else if (PyObject_TypeCheck(pcObj, &(PyList_Type)) ||
             PyObject_TypeCheck(pcObj, &(PyTuple_Type))) {
        std::vector<Part::Geometry *> geoList;
        std::vector<boost::shared_ptr <Part::Geometry> > tmpList;
        Py::Sequence list(pcObj);
        for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
            if (PyObject_TypeCheck((*it).ptr(), &(Part::GeometryPy::Type))) {
                Part::Geometry *geo = static_cast<Part::GeometryPy*>((*it).ptr())->getGeometryPtr();

                // An arc created with Part.Arc will be converted into a Part.ArcOfCircle
                if (geo->getTypeId() == Part::GeomTrimmedCurve::getClassTypeId()) {
                    Handle_Geom_TrimmedCurve trim = Handle_Geom_TrimmedCurve::DownCast(geo->handle());
                    Handle_Geom_Circle circle = Handle_Geom_Circle::DownCast(trim->BasisCurve());
                    Handle_Geom_Ellipse ellipse = Handle_Geom_Ellipse::DownCast(trim->BasisCurve());
                    if (!circle.IsNull()) {
                        // create the definition struct for that geom
                        boost::shared_ptr<Part::GeomArcOfCircle> aoc(new Part::GeomArcOfCircle());
                        aoc->setHandle(trim);
                        geoList.push_back(aoc.get());
                        tmpList.push_back(aoc);
                    }
                    else if (!ellipse.IsNull()) {
                        // create the definition struct for that geom
                        boost::shared_ptr<Part::GeomArcOfEllipse> aoe(new Part::GeomArcOfEllipse());
                        aoe->setHandle(trim);
                        geoList.push_back(aoe.get());
                        tmpList.push_back(aoe);
                    }
                    else {
                        std::stringstream str;
                        str << "Unsupported geometry type: " << geo->getTypeId().getName();
                        PyErr_SetString(PyExc_TypeError, str.str().c_str());
                        return 0;
                    }
                }
                else if (geo->getTypeId() == Part::GeomPoint::getClassTypeId() ||
                         geo->getTypeId() == Part::GeomCircle::getClassTypeId() ||
                         geo->getTypeId() == Part::GeomEllipse::getClassTypeId() ||
                         geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() ||
                         geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() ||
                         geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
                    geoList.push_back(geo);
                }
                else {
                    std::stringstream str;
                    str << "Unsupported geometry type: " << geo->getTypeId().getName();
                    PyErr_SetString(PyExc_TypeError, str.str().c_str());
                    return 0;
                }
            }
        }

        int ret = this->getSketchObjectPtr()->addGeometry(geoList,isConstruction) + 1;
        std::size_t numGeo = geoList.size();
        Py::Tuple tuple(numGeo);
        for (std::size_t i=0; i<numGeo; ++i) {
            int geoId = ret - int(numGeo - i);
            tuple.setItem(i, Py::Int(geoId));
        }

        return Py::new_reference_to(tuple);
    }

    std::string error = std::string("type must be 'Geometry' or list of 'Geometry', not ");
    error += pcObj->ob_type->tp_name;
    throw Py::TypeError(error);
}
void SketcherValidation::on_findButton_clicked()
{
    std::vector<VertexIds> vertexIds;
    const std::vector<Part::Geometry *>& geom = sketch->getInternalGeometry();
    for (std::size_t i=0; i<geom.size(); i++) {
        Part::Geometry* g = geom[i];
        if (g->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
            const Part::GeomLineSegment *segm = static_cast<const Part::GeomLineSegment*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint();
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint();
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
            const Part::GeomArcOfCircle *segm = static_cast<const Part::GeomArcOfCircle*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint(/*emulateCCW=*/true);
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint(/*emulateCCW=*/true);
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
            const Part::GeomArcOfEllipse *segm = static_cast<const Part::GeomArcOfEllipse*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint(/*emulateCCW=*/true);
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint(/*emulateCCW=*/true);
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) {
            const Part::GeomArcOfHyperbola *segm = static_cast<const Part::GeomArcOfHyperbola*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint();
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint();
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) {
            const Part::GeomArcOfParabola *segm = dynamic_cast<const Part::GeomArcOfParabola*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint();
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint();
            vertexIds.push_back(id);
        }
    }

    std::set<ConstraintIds, Constraint_Less> coincidences;
    double prec = Precision::Confusion();
    QVariant v = ui->comboBoxTolerance->itemData(ui->comboBoxTolerance->currentIndex());
    if (v.isValid())
        prec = v.toDouble();
    else
        prec = QLocale::system().toDouble(ui->comboBoxTolerance->currentText());
    std::sort(vertexIds.begin(), vertexIds.end(), Vertex_Less(prec));
    std::vector<VertexIds>::iterator vt = vertexIds.begin();
    Vertex_EqualTo pred(prec);
    while (vt < vertexIds.end()) {
        // get first item whose adjacent element has the same vertex coordinates
        vt = std::adjacent_find(vt, vertexIds.end(), pred);
        if (vt < vertexIds.end()) {
            std::vector<VertexIds>::iterator vn;
            for (vn = vt+1; vn != vertexIds.end(); ++vn) {
                if (pred(*vt,*vn)) {
                    ConstraintIds id;
                    id.v = vt->v;
                    id.First = vt->GeoId;
                    id.FirstPos = vt->PosId;
                    id.Second = vn->GeoId;
                    id.SecondPos = vn->PosId;
                    coincidences.insert(id);
                }
                else {
                    break;
                }
            }

            vt = vn;
        }
    }

    std::vector<Sketcher::Constraint*> constraint = sketch->Constraints.getValues();
    for (std::vector<Sketcher::Constraint*>::iterator it = constraint.begin(); it != constraint.end(); ++it) {
        if ((*it)->Type == Sketcher::Coincident) {
            ConstraintIds id;
            id.First = (*it)->First;
            id.FirstPos = (*it)->FirstPos;
            id.Second = (*it)->Second;
            id.SecondPos = (*it)->SecondPos;
            std::set<ConstraintIds, Constraint_Less>::iterator pos = coincidences.find(id);
            if (pos != coincidences.end()) {
                coincidences.erase(pos);
            }
        }
    }

    this->vertexConstraints.clear();
    this->vertexConstraints.reserve(coincidences.size());
    std::vector<Base::Vector3d> points;
    points.reserve(coincidences.size());
    for (std::set<ConstraintIds, Constraint_Less>::iterator it = coincidences.begin(); it != coincidences.end(); ++it) {
        this->vertexConstraints.push_back(*it);
        points.push_back(it->v);
    }

    hidePoints();
    if (this->vertexConstraints.empty()) {
        QMessageBox::information(this, tr("No missing coincidences"),
            tr("No missing coincidences found"));
        ui->fixButton->setEnabled(false);
    }
    else {
        showPoints(points);
        QMessageBox::warning(this, tr("Missing coincidences"),
            tr("%1 missing coincidences found").arg(this->vertexConstraints.size()));
        ui->fixButton->setEnabled(true);
    }
}
void SketcherValidation::on_findButton_clicked()
{
    std::vector<VertexIds> vertexIds;
    const std::vector<Part::Geometry *>& geom = sketch->getInternalGeometry();
    for (std::size_t i=0; i<geom.size(); i++) {
        Part::Geometry* g = geom[i];

        if(g->Construction && ui->checkBoxIgnoreConstruction->isChecked())
            continue;

        if (g->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
            const Part::GeomLineSegment *segm = static_cast<const Part::GeomLineSegment*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint();
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint();
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
            const Part::GeomArcOfCircle *segm = static_cast<const Part::GeomArcOfCircle*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint(/*emulateCCW=*/true);
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint(/*emulateCCW=*/true);
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) {
            const Part::GeomArcOfEllipse *segm = static_cast<const Part::GeomArcOfEllipse*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint(/*emulateCCW=*/true);
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint(/*emulateCCW=*/true);
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) {
            const Part::GeomArcOfHyperbola *segm = static_cast<const Part::GeomArcOfHyperbola*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint();
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint();
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) {
            const Part::GeomArcOfParabola *segm = static_cast<const Part::GeomArcOfParabola*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint();
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint();
            vertexIds.push_back(id);
        }
        else if (g->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) {
            const Part::GeomBSplineCurve *segm = static_cast<const Part::GeomBSplineCurve*>(g);
            VertexIds id;
            id.GeoId = (int)i;
            id.PosId = Sketcher::start;
            id.v = segm->getStartPoint();
            vertexIds.push_back(id);
            id.GeoId = (int)i;
            id.PosId = Sketcher::end;
            id.v = segm->getEndPoint();
            vertexIds.push_back(id);
        }
    }

    double prec = Precision::Confusion();
    QVariant v = ui->comboBoxTolerance->itemData(ui->comboBoxTolerance->currentIndex());
    if (v.isValid())
        prec = v.toDouble();
    else
        prec = QLocale::system().toDouble(ui->comboBoxTolerance->currentText());

    std::sort(vertexIds.begin(), vertexIds.end(), Vertex_Less(prec));
    std::vector<VertexIds>::iterator vt = vertexIds.begin();
    Vertex_EqualTo pred(prec);

    std::list<ConstraintIds> coincidences;
    // Make a list of constraint we expect for coincident vertexes
    while (vt < vertexIds.end()) {
        // get first item whose adjacent element has the same vertex coordinates
        vt = std::adjacent_find(vt, vertexIds.end(), pred);
        if (vt < vertexIds.end()) {
            std::vector<VertexIds>::iterator vn;
            for (vn = vt+1; vn != vertexIds.end(); ++vn) {
                if (pred(*vt,*vn)) {
                    ConstraintIds id;
                    id.v = vt->v;
                    id.First = vt->GeoId;
                    id.FirstPos = vt->PosId;
                    id.Second = vn->GeoId;
                    id.SecondPos = vn->PosId;
                    coincidences.push_back(id);
                }
                else {
                    break;
                }
            }

            vt = vn;
        }
    }

    // Go through the available 'Coincident', 'Tangent' or 'Perpendicular' constraints
    // and check which of them is forcing two vertexes to be coincident.
    // If there is none but two vertexes can be considered equal a coincident constraint is missing.
    std::vector<Sketcher::Constraint*> constraint = sketch->Constraints.getValues();
    for (std::vector<Sketcher::Constraint*>::iterator it = constraint.begin(); it != constraint.end(); ++it) {
        if ((*it)->Type == Sketcher::Coincident ||
            (*it)->Type == Sketcher::Tangent ||
            (*it)->Type == Sketcher::Perpendicular) {
            ConstraintIds id;
            id.First = (*it)->First;
            id.FirstPos = (*it)->FirstPos;
            id.Second = (*it)->Second;
            id.SecondPos = (*it)->SecondPos;
            std::list<ConstraintIds>::iterator pos = std::find_if
                    (coincidences.begin(), coincidences.end(), Constraint_Equal(id));
            if (pos != coincidences.end()) {
                coincidences.erase(pos);
            }
        }
    }

    this->vertexConstraints.clear();
    this->vertexConstraints.reserve(coincidences.size());
    std::vector<Base::Vector3d> points;
    points.reserve(coincidences.size());
    for (std::list<ConstraintIds>::iterator it = coincidences.begin(); it != coincidences.end(); ++it) {
        this->vertexConstraints.push_back(*it);
        points.push_back(it->v);
    }

    hidePoints();
    if (this->vertexConstraints.empty()) {
        QMessageBox::information(this, tr("No missing coincidences"),
            tr("No missing coincidences found"));
        ui->fixButton->setEnabled(false);
    }
    else {
        showPoints(points);
        QMessageBox::warning(this, tr("Missing coincidences"),
            tr("%1 missing coincidences found").arg(this->vertexConstraints.size()));
        ui->fixButton->setEnabled(true);
    }
}