/**
         * Fit a circle to three given points on the circumference. The method draws lines (chords)
         * between two pairs of the points, constructs their perpendicular bisectors, and finds the
         * intersection point of those two lines. This is the centre of the circle as radii passing
         * through the centre are perpendicular bisectors of chords in a circle. The radius is the
         * distance between the centre and any one of the initial three points.
         */
        private QDCircle threePointCircleFit(List <QDPoint> pts)
        {
            QDLine         l1     = new QDLine(pts[0], pts[1]);
            QDLine         l2     = new QDLine(pts[1], pts[2]);
            QDInfiniteLine i1     = new QDInfiniteLine(l1.getMidpoint(), l1.getPerpAngleD());
            QDInfiniteLine i2     = new QDInfiniteLine(l2.getMidpoint(), l2.getPerpAngleD());
            QDPoint        centre = i1.intersect(i2);
            float          radius = QDUtils.QDUtils.getPtToPtDist(centre, pts[0]);

            return(new QDCircle(centre, radius));
        }
Beispiel #2
0
        public void getIntrinsicConstraints(QDInputPointSet ptSet)
        {
            if (ptSet.shapeType == QDShapeTypes.LINE)
            {
                QDLine line  = (QDLine)ptSet.initialFit;
                float  angle = line.angleD;
                // Check for vertical line and adjust by midpoint pivot
                if ((90.0f - line_snap_thresh < angle && angle < 90.0f + line_snap_thresh) ||
                    (-90.0f - line_snap_thresh < angle && angle < -90.0f + line_snap_thresh) && !line.vertical)
                {
                    QDPoint newStart, newFinish;

                    if (line.start.y > line.getMidpoint().y)
                    {
                        newStart  = new QDPoint(line.midPoint.x, line.midPoint.y + (0.5f * line.length));
                        newFinish = new QDPoint(line.midPoint.x, line.midPoint.y - (0.5f * line.length));
                    }
                    else
                    {
                        newStart  = new QDPoint(line.midPoint.x, line.midPoint.y - (0.5f * line.length));
                        newFinish = new QDPoint(line.midPoint.x, line.midPoint.y + (0.5f * line.length));
                    }
                    ptSet.fittedShape = new QDLine(newStart, newFinish);
                    ptSet.constraints.Add(QDConstraintTypes.VERTICAL_LINE);
                }
                // Check for horizontal line and adjust by midpoint pivot
                else if ((-line_snap_thresh < angle && angle < line_snap_thresh) ||
                         (180.0f - line_snap_thresh < angle || angle < -180.0f + line_snap_thresh))
                {
                    float   length    = (float)Math.Sqrt(Math.Pow(line.start.x - line.finish.x, 2.0f) + Math.Pow(line.start.y - line.finish.y, 2.0f));
                    QDPoint midPt     = new QDPoint((line.start.x + line.finish.x) * 0.5f, (line.start.y + line.finish.y) * 0.5f);
                    QDPoint newStart  = new QDPoint();
                    QDPoint newFinish = new QDPoint();
                    newStart.y = newFinish.y = midPt.y;
                    if (line.start.x > midPt.x)
                    {
                        newStart.x  = midPt.x + (0.5f * length);
                        newFinish.x = midPt.x - (0.5f * length);
                    }
                    else
                    {
                        newStart.x  = midPt.x - (0.5f * length);
                        newFinish.x = midPt.x + (0.5f * length);
                    }
                    ptSet.fittedShape = new QDLine(newStart, newFinish);
                    ptSet.constraints.Add(QDConstraintTypes.HORIZONTAL_LINE);
                }
            }
        }
Beispiel #3
0
        // Add a point to the segment list and check for a corner in the segment
        // Returns true if a corner was detected, false otherwise
        public bool addPoint(QDPoint pt)
        {
            // Special case for the first point
            if (pts.Count == 0)
            {
                start = pt;
                pts.Add(pt);
                sampledMarkerPt = pt;
                return(false);
            }

            // Get the pt to pt gradient
            float angle = QDUtils.QDUtils.getPtToPtAngleD(pts.Last(), pt);

            // Store summary statistics
            if (angle > angle_max)
            {
                angle_max = angle;
            }
            if (angle < angle_min)
            {
                angle_min = angle;
            }
            angle_sum += angle;

            // Only stores the angles between points sufficiently far apart. This is the most
            // noise robust, and useful for getting the general form of the input shape
            if (QDUtils.QDUtils.getPtToPtDist(sampledMarkerPt, pt) > SUBSAMP_PT_DIST)
            {
                sampledPtAnglesD.Add(QDUtils.QDUtils.getPtToPtAngleD(sampledMarkerPt, pt));
                sampledMarkerPt = pt;
            }

            // Add values to vectors
            anglesD.Add(angle);
            pts.Add(pt);


            // Step 1: track back to get testPt
            float   TEST_RADIUS = 50.0f;
            float   CNR_RADIUS = 10.0f;
            QDPoint testPt = null, cnrCandidatePt = null, currPt = pt;
            int     midIdx = -1, testIdx = -1, cnrCandidateIdx = -1;

            midIdx = backtrackByDist(pts, pts.Count - 2, TEST_RADIUS / 2.0f);
            if (midIdx < 0)
            {
                return(false);
            }
            testIdx = backtrackByDist(pts, midIdx, TEST_RADIUS / 2.0f);
            if (testIdx < 0)
            {
                return(false);
            }
            testPt = pts[testIdx];

            // Step 2: construct line from testPt to currPt
            QDInfiniteLine testLine = new QDInfiniteLine(testPt, currPt);

            // Step 3: Find perp dist from each pt between test and curr to the line
            float maxDist = 0.0f;
            int   maxIdx  = -1;

            for (int i = testIdx + 1; i < pts.Count - 1; i++)
            {
                float thisDist = testLine.getDistToPoint(pts[i]);
                if (thisDist > maxDist)
                {
                    maxDist = thisDist;
                    maxIdx  = i;
                }
            }

            // Step 4: confirm cnrCandidatePt
            if (maxIdx < 0)
            {
                return(false);
            }
            cnrCandidatePt = pts[maxIdx];
            int cnrIdx = maxIdx;

            if (!(QDUtils.QDUtils.getPtToPtDist(cnrCandidatePt, currPt) > CNR_RADIUS))
            {
                return(false);
            }

            // Step 5: construct lines
            float oldAngleD = new QDLine(testPt, cnrCandidatePt).angleD;
            float newAngleD = new QDLine(cnrCandidatePt, currPt).angleD;

            // Step 6: test angle between lines
            if (Math.Abs(QDUtils.QDUtils.angleDiffMinorD(oldAngleD, newAngleD)) > CNR_ANGLE_THRESH)
            {
                // Step 7: test for continuous curvature
                int strTestIdx = backtrackByDist(pts, testIdx, CNR_RADIUS);
                if (strTestIdx < 0)
                {
                    return(false);
                }
                int strTest2Idx = backtrackByDist(pts, strTestIdx, CNR_RADIUS);
                if (strTest2Idx < 0)
                {
                    return(false);
                }
                float seg1 = new QDLine(pts[strTest2Idx], pts[strTestIdx]).angleD;
                float seg2 = new QDLine(pts[strTestIdx], pts[testIdx]).angleD;
                if (Math.Abs(QDUtils.QDUtils.angleDiffMinorD(seg1, seg2)) < 30.0f)
                {
                    cornerTerminated = true;
                }
                else
                {
                    cornerTerminated = false;
                }
            }

            if (cornerTerminated)
            {
                newSeg = new QDInputPointSet(CORNER_WINDOW, CNR_ANGLE_THRESH);
                newSeg.addPoint(cnrCandidatePt);                // Add the corner point
                int lastIdx = pts.Count - 1;
                for (int i = 0; i < lastIdx - cnrIdx; i++)      // For how many points there are after the corner
                {
                    newSeg.addPoint(this.pts[cnrIdx + 1]);      // Copy points after the corner into new seg
                    this.pts.RemoveAt(cnrIdx + 1);              // Delete points after corner from current set
                    this.anglesD.Remove(cnrIdx);                // There is one less angle than pts since first two pts make 1 angle
                }
                // Remove subsampled angles as well up to before the corner
                int deleteSubSamp = (int)Math.Ceiling(CNR_RADIUS / SUBSAMP_PT_DIST);
                for (int i = 0; i < deleteSubSamp; i++)
                {
                    sampledPtAnglesD.Remove(sampledPtAnglesD.Count - deleteSubSamp + i);
                }
                return(true);
            }

            return(false);
        }
Beispiel #4
0
        public List <QDShape> getExtrinsicConstraints(QDInputPointSet ptSet)
        {
            List <QDShape> modifiedShapes = new List <QDShape>();

            if (ptSet.shapeType == QDShapeTypes.LINE)
            {
                QDLine line = (QDLine)ptSet.fittedShape;
                List <QDShapeDBPoint> nearStartPts = shapeDB.getShapesNearPoint(line.start, SEARCH_RADIUS);
                foreach (QDShapeDBPoint pt in nearStartPts)
                {
                    if (pt.type == QDPointTypes.LINE_START || pt.type == QDPointTypes.LINE_FINISH)
                    {
                        QDLine nearLine = (QDLine)pt.shape;

                        if (!modifiedShapes.Contains(nearLine))
                        {
                            modifiedShapes.Add(nearLine);
                        }

                        QDInfiniteLine thisInf = new QDInfiniteLine(line.start, line.finish);
                        QDInfiniteLine nearInf = new QDInfiniteLine(nearLine.start, nearLine.finish);
                        // Check for nearly parallel lines
                        if (Math.Abs(QDUtils.QDUtils.angleDiffMinorD(line.angleD, nearLine.angleD)) > 10f)
                        {
                            QDPoint intersectPt = thisInf.intersect(nearInf);
                            if (QDUtils.QDUtils.getPtToPtDist(line.start, intersectPt) < SEARCH_RADIUS * 1.25)
                            {
                                line.start = intersectPt;
                                if (pt.type == QDPointTypes.LINE_START)
                                {
                                    nearLine.start = intersectPt;
                                }
                                else
                                {
                                    nearLine.finish = intersectPt;
                                }
                            }
                        }
                    }
                }

                /**
                 * GOTTA BE A BETTER WAY OF DOING THIS RATHER THAN DOING IT ONCE FOR START AND ONCE FOR FINISH
                 */
                List <QDShapeDBPoint> nearFinishPts = shapeDB.getShapesNearPoint(line.finish, SEARCH_RADIUS);
                foreach (QDShapeDBPoint pt in nearFinishPts)
                {
                    if (pt.type == QDPointTypes.LINE_START || pt.type == QDPointTypes.LINE_FINISH)
                    {
                        QDLine nearLine = (QDLine)pt.shape;

                        if (!modifiedShapes.Contains(nearLine))
                        {
                            modifiedShapes.Add(nearLine);
                        }

                        QDInfiniteLine thisInf = new QDInfiniteLine(line.start, line.finish);
                        QDInfiniteLine nearInf = new QDInfiniteLine(nearLine.start, nearLine.finish);
                        // Check for nearly parallel lines
                        if (Math.Abs(QDUtils.QDUtils.angleDiffMinorD(line.angleD, nearLine.angleD)) > 10f)
                        {
                            QDPoint intersectPt = thisInf.intersect(nearInf);
                            if (QDUtils.QDUtils.getPtToPtDist(line.finish, intersectPt) < SEARCH_RADIUS * 1.25)
                            {
                                line.finish = intersectPt;
                                if (pt.type == QDPointTypes.LINE_START)
                                {
                                    nearLine.start = intersectPt;
                                }
                                else
                                {
                                    nearLine.finish = intersectPt;
                                }
                            }
                        }
                    }
                }
            }
            return(modifiedShapes);
        }
        private QDLine fitLine(QDInputPointSet pointSet)
        {
            int n = pointSet.pts.Count - 1;

            float xsum = 0.0f;
            float ysum = 0.0f;

            for (int i = 0; i < n; i++)
            {
                xsum += pointSet.pts[i].x;
                ysum += pointSet.pts[i].y;
            }
            float xbar = xsum / n;
            float ybar = ysum / n;
            float denom, numer, gradient, diffx;

            denom = numer = 0.0f;
            for (int i = 0; i < n; i++)
            {
                diffx  = pointSet.pts[i].x - xbar;
                numer += diffx * (pointSet.pts[i].y - ybar);
                denom += diffx * diffx;
            }

            QDLine line = new QDLine();

            if (denom < 1e-2f)
            {                                         // Check for vertical case
                line.vertical = true;
                line.start.x  = line.finish.x = xbar; // x coords at the average

                line.start.y  = pointSet.pts[0].y;    // y coords are limits of drawn line
                line.finish.y = pointSet.pts[n].y;

                // Check direction
                if (line.finish.y > line.start.y)
                {
                    line.angleD = 90.0f;
                }
                else
                {
                    line.angleD = -90.0f;
                }
            }
            else
            {
                // Otherwise choose start and finish values as limits of line
                gradient       = numer / denom;
                line.angleD    = ((float)Math.Atan2(numer, denom)) * 180.0f / ((float)Math.PI);
                line.intercept = ybar - (gradient * xbar);

                if (Math.Abs(line.angleD) < 45.0f)
                {
                    line.start.x = pointSet.pts[0].x;
                    line.start.y = line.start.x * gradient + line.intercept;

                    line.finish.x = pointSet.pts[n].x;
                    line.finish.y = pointSet.pts[n].y;
                }
                else
                {
                    line.start.y = pointSet.pts[0].y;
                    line.start.x = (line.start.y - line.intercept) / gradient;

                    line.finish.y = pointSet.pts[n].y;
                    line.finish.x = (line.finish.y - line.intercept) / gradient;
                }
            }
            return(new QDLine(line.start, line.finish));
        }