Esempio n. 1
0
    def doCompute(self, drawer, landmark, figure, **args):
        fPoints = list(
            math2d.stepAlongLine(figure,
                                 math2d.length(figure) / 100.0))
        gPoints = [math2d.closestPointOnPolygon(landmark, p) for p in fPoints]
        distances = math2d.squareDistances(fPoints, gPoints)
        #[math2d.squaredist(f1, g1) for f1, g1 in zip(fPoints, gPoints)]

        i, dist = math2d.argMin(distances)

        fPoint = fPoints[i]
        gPoint = math2d.closestPointOnPolygon(landmark, fPoint)

        axes = [fPoint, gPoint]

        m = {}

        drawer.drawSegment("angleFigureToPastAxes", fPoint, gPoint)
        bborigin, (width, height) = math2d.boundingBox(figure)
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        if scale == 0:
            bborigin, (width, height) = math2d.boundingBox(landmark)
            scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        if math2d.isDegenerate(axes):
            m['angleFigureToPastAxes'] = 0
        else:
            m['angleFigureToPastAxes'] = math2d.angleBetweenLines(
                axes, [figure[0], figure[-1]])
        drawer.distanceFeature(m, "pastAxesLength", "Axes Start", "Axes End",
                               axes[0], axes[1], scale)

        return m
Esempio n. 2
0
    def doCompute(self, drawer, situation):

        figureA = situation.agent("figure")
        landmarkA = situation.agent("landmark")

        figure = [(x, y) for x, y, theta in figureA.positions]
        landmark = [(x, y) for x, y, theta in landmarkA.positions]

        out = {}
        bborigin, (width, height) = math2d.boundingBox(figure)
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        if scale == 0:
            bborigin, (width, height) = math2d.boundingBox(figure + landmark)
            scale = pow(pow(width, 2) + pow(height, 2), 0.5)

        pt = landmark[-1]

        dFigureStartToGround = math2d.dist(pt, figure[0])
        dFigureEndToGround = math2d.dist(pt, figure[-1])
        drawer.drawDistance("displacementFromGround", [pt, figure[0]])

        drawer.drawDistance("displacementFromGround", [pt, figure[-1]])

        out["endpointDist"] = math2d.dist(figure[-1], landmark[-1]) / scale
        out["startpointDist"] = math2d.dist(figure[0], landmark[0]) / scale

        out["displacementFromGround"] = (dFigureEndToGround -
                                         dFigureStartToGround) / scale
        return out
Esempio n. 3
0
    def doCompute(self, drawer, landmark, figure, **args):
        landmarkCentroid = math2d.centroid(landmark)
        d_f = math2d.length(figure) / 100
        steps = list(math2d.stepAlongLine(figure, d_f))
        slope, intercept, r_value, p_value, std_err = math2d.regression(
            steps[25:])
        axes = math2d.lineEquationToPoints(slope, intercept)

        axes = [
            math2d.closestPointOnSegmentLine(axes, figure[0]),
            math2d.closestPointOnSegmentLine(axes, figure[-1])
        ]

        #axes = [steps[5], steps[-1]]

        point = math2d.closestPointOnSegmentLine(axes, landmarkCentroid)
        bborigin, (width, height) = math2d.boundingBox(figure)
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        if scale == 0:
            bborigin, (width, height) = math2d.boundingBox(landmark)
            scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        m = {}
        drawer.distanceFeature(m, "axesToLandmarkCentroid",
                               "Point on Figure Line", "Landmark Centroid",
                               point, landmarkCentroid, scale)

        segmentStartToLandmark = [axes[0], landmarkCentroid]
        m["axesOrientation"] = math2d.angleBetweenSegments(
            segmentStartToLandmark, axes)
        drawer.drawLine("axesOrientation", segmentStartToLandmark)
        drawer.drawLine("axesOrientation", axes)
        drawer.drawPoint("axesOrientation", point, "Point on Figure Line")

        try:
            slope, intersept = math2d.lineEquation(axes)
        except:
            print "axes", axes
            raise
        intersectPoints = math2d.intersectPolygonAnalytic(
            landmark, slope, intersept)
        for p in intersectPoints:
            drawer.drawPoint("axesIntersectLandmark", p, "")
        if len(intersectPoints) == 0:
            m["axesIntersectLandmark"] = 0
            drawer.drawPoint("axesIntersectLandmark", landmarkCentroid,
                             "No intersect points.")
        else:
            m["axesIntersectLandmark"] = 1

        #intersectPoint =
        #m["axesToLandmark"] =
        return m
Esempio n. 4
0
 def doCompute(self, drawer, situation):
     distances = []
     
     figure = situation.agent("figure")
     landmark = situation.agent("landmark")
     points = [(x, y) for x, y, theta in figure.positions + landmark.positions]
     (minX, minY), (scaleX, scaleY)  = math2d.boundingBox(points)
     
     scale = math.pow(scaleX**2 + scaleY**2, 0.5)
     
     for t in range(situation.startTime, situation.endTime, 100):
         fx, fy, ftheta = figure.location(t)
         lx, ly, ltheta = landmark.location(t)
         floc = fx, fy
         lloc = lx, ly
         distances.append(math2d.dist(floc, lloc))
         drawer.drawDistance("averageDistance", t, floc, lloc)
         drawer.drawDistance("stdDevOfDistance", t, floc, lloc)
         
     if len(distances) == 0:
         raise InsaneExample("Bad figure: " + `figure`)
     if scale == 0:
         scale = 0.000001
     return {"averageDistance":scipy.mean(distances) / scale,
             "stdDevOfDistance":scipy.std(distances) / scale
             }
Esempio n. 5
0
 def doCompute(self, drawer, landmark, figure, **args):
     bborigin, (width, height) = math2d.boundingBox(landmark + figure)
     scale = pow(pow(width, 2) + pow(height, 2), 0.5)
     map = {'normalizedPathLength': math2d.length(figure) / scale}
     drawer.drawPoint("normalizedPathLength", figure[0], "Figure Start")
     drawer.drawPoint("normalizedPathLength", figure[-1], "Figure End")
     return map
Esempio n. 6
0
    def doCompute(self, drawer, landmark, figure, **args):
        (lowerLeft, lowerRight), (width, height) = math2d.boundingBox(landmark)
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)

        scaleFactor = scale * 0.1

        polygon = math2d.boxToPolygon(
            (lowerLeft - scaleFactor, lowerRight - scaleFactor),
            (width + 2 * scaleFactor, height + 2 * scaleFactor))

        out = {}
        figureSteps = list(
            math2d.stepAlongLine(figure,
                                 math2d.length(figure) / 100.0))

        out["endPointsInLandmarkBoundingBox"] = 0
        for p in figureSteps[90:]:
            if math2d.isInteriorPoint(polygon, p):
                out["endPointsInLandmarkBoundingBox"] += 1
                drawer.drawPoint("endPointsInLandmarkBoundingBox", p)
        drawer.drawLine("endPointsInLandmarkBoundingBox",
                        math2d.polygonToLine(polygon))

        out["startPointsInLandmarkBoundingBox"] = 0
        for p in figureSteps[:10]:
            if math2d.isInteriorPoint(polygon, p):
                out["startPointsInLandmarkBoundingBox"] += 1
                drawer.drawPoint("startPointsInLandmarkBoundingBox", p)
        drawer.drawLine("startPointsInLandmarkBoundingBox",
                        math2d.polygonToLine(polygon))

        return out
Esempio n. 7
0
    def doCompute(self, drawer, landmark, figure, **args):
        boundary = math2d.computeBoundaryLine(landmark, figure)
        map = {}
        bborigin, (width, height) = math2d.boundingBox(figure)
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)

        if scale == 0:
            bborigin, (width, height) = math2d.boundingBox(landmark)
            scale = pow(pow(width, 2) + pow(height, 2), 0.5)

        drawer.distanceFeature(map,
                               "distStartLandmarkBoundary",
                               "Boundary",
                               "Figure Start",
                               math2d.closestPointOnLine(boundary, figure[0]),
                               figure[0], scale)
        drawer.distanceFeature(map,
                               "distEndLandmarkBoundary",
                               "Boundary",
                               "Figure End",
                               math2d.closestPointOnLine(boundary, figure[-1]),
                               figure[-1], scale)

        map['averageDistStartEndLandmarkBoundary'] = na.mean([map['distStartLandmarkBoundary'],
                                                              map['distEndLandmarkBoundary']])
        drawer.drawLine('averageDistStartEndLandmarkBoundary', figure[-1],
                      math2d.closestPointOnLine(boundary, figure[-1]))

        drawer.distanceFeature(map,
                               "figureStartToEnd",
                               "Start",
                               "End",
                               figure[0],
                               figure[-1],
                               scale)

        map['figureLength'] = math2d.length(figure) / scale
        drawer.drawPoint('figureLength', figure[0])
        drawer.drawPoint('figureLength', figure[-1])

        if math2d.length(figure) == 0:
            map['figureLengthByCrow'] = 0
        else:
            map['figureLengthByCrow'] = math2d.dist(figure[0], figure[-1]) / math2d.length(figure)
        return map
Esempio n. 8
0
    def generateInstructions(self, path):
        self.newPathLayer(path)
        print path[0]
        print "generating?"
        (minX, minY), (width, height) = math2d.boundingBox(path)
        rect = qgis_utils.qgsRectFromWidthHeight(*math2d.boundingBox(path))
        self.tagLayer.select(rect, False)

        self.descriptionLabel.setText("test")
        return
        for x in self.tagLayer.selectedFeatures():
            nr = named_region.TkNamedRegion(self.tagLayer, x)
            print "name", nr.name()
            print "x", nr.points
            preposition = pathDescriber.describe(nr.points, path)
            if preposition != None:
                self.descriptionLabel.setText("go %s the %s" %
                                              (preposition, nr.name()))
                break
Esempio n. 9
0
    def doCompute(self, drawer, situation):
        out = {}
        figurePath = situation.agent("figure").asPath()

        bborigin, (width, height) = math2d.boundingBox(figurePath)

        scale = pow(pow(width, 2) + pow(height, 2), 0.5)

        drawer.drawDistance("netDisplacement", 0, figurePath[0],
                            figurePath[-1])
        out["netDisplacement"] = math2d.dist(figurePath[0], figurePath[-1])
        return out
Esempio n. 10
0
    def doCompute(self, drawer, landmark, figure, **args):
        (x0,y0), (width, height) = math2d.boundingBox(landmark)

        #point, dims = (x0 + width, y0), (width, height)
        #translatedBox = math2d.boxToPolygon(point, dims)
        translatedBox = math2d.boxToPolygon((x0,y0),(width, height))
        
        drawer.drawRect("intersectBoundingBox", 
                      translatedBox[0], translatedBox[2])
        if math2d.clip(figure, math2d.polygonToLine(translatedBox)) != []:
            return {"intersectBoundingBox":1}
        else:
            return {"intersectBoundingBox":0}
Esempio n. 11
0
 def doCompute(self, drawer, landmark, figure, **args):
     figureCom = math2d.centerOfMass(figure)
     landmarkCom = math2d.centerOfMass(landmark)
     bborigin, (width, height) = math2d.boundingBox(figure)
     scale = pow(pow(width, 2) + pow(height, 2), 0.5)
     #scale = 960
     map = {}
     self.distanceFeature(map,
                          "centroidToTarget",
                          "Figure Centroid",
                          "Landmark Centroid",
                          figureCom, landmarkCom, scale)
     return map
Esempio n. 12
0
    def selectAnnotation(self):
        dc = self.model.selectedData()
        self.editorWindow.setPreposition(dc.engine.name())
        #self.editorWindow.newGeometry(**dc.geometry)
        self.rubberBand.clear()

        minCorner, widthAndHeight = math2d.boundingBox(dc.ground)

        center = math2d.centroid(dc.ground)
        self.canvas.setExtent(
            qgis_utils.qgsRectFromCenterWidthHeight(center, (15, 15)))
        self.rubberBand.setPoints(dc.ground)
        self.canvas.refresh()
        self.pathDescriptionLabel.setText("%s the %s" %
                                          (dc.engine.name(), dc.groundName))
        self.setFigureLayer(dc.figureLayer)
Esempio n. 13
0
    def doCompute(self, drawer, landmark, figure, **args):

        fminX = min([p[0] for p in figure])
        fmaxX = max([p[0] for p in figure])

        fM, fB = math2d.fitLine(figure)

        gM, gB = math2d.fitLine(landmark)

        slope, intercept, r_value, p_value, std_err = math2d.regression(figure)


        m = {}
        bborigin, (width, height) = math2d.boundingBox(figure)
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        #scale = 960
        m["stdErrOfRegression"] = std_err / scale
        # different: 0.59
        # same coordiante system: 0.45

        
        # only angle, identicle? 0.31, 0.21

        
        if na.isnan(fM):
            fpoint = (0, 1)
        else:
            fpoint = (1, fM)
        if na.isnan(gM):
            gpoint = (0, 1)
        else:
            gpoint = (1, gM)
        m['angleBtwnLinearizedObjects'] = math2d.angleBetweenLines([(0, 0), fpoint],
                                                                   [(0, 0), gpoint])            
        
            
        drawer.drawLine("angleBtwnLinearizedObjects", [(fminX, fminX*fM + fB),
                                                        (fmaxX, fmaxX*fM + fB)])
        drawer.drawLine("stdErrOfRegression", [(fminX, fminX*fM + fB),
                                              (fmaxX, fmaxX*fM + fB)])

        #m['averageParallel'] = self.averageParallel(landmark, figure)

        return m
Esempio n. 14
0
    def doCompute(self, drawer, landmark, figure, **args):
        figureSteps = list(
            math2d.stepAlongLine(figure,
                                 math2d.length(figure) / 100.0))
        points = [
            math2d.closestPointOnPolygon(landmark, p) for p in figureSteps
        ]
        interiorPoints = math2d.interiorPoints(landmark, figureSteps)
        landmarkCentroid = math2d.centroid(landmark)
        distances = [math2d.squaredist(p, (0, 0)) for p in points]
        bborigin, (width,
                   height) = math2d.boundingBox(na.append(landmark, figure, 0))
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        out = {}
        i, dist = math2d.argMin(distances)
        drawer.distanceFeature(out, "minimumDistanceToLandmark",
                               "Closest point on landmark", "Figure End",
                               figureSteps[i], points[i], scale)

        for p in interiorPoints:
            drawer.drawPoint("numInteriorPoints", p)
        if len(interiorPoints) == 0:
            drawer.drawText("numInteriorPoints", landmarkCentroid,
                            "No interior points.")
            #if out["numInteriorPoints"] > 0:
            #out["numInteriorPoints"] = 1

        out["numInteriorPoints"] = len(interiorPoints)
        if math2d.isInteriorPoint(landmark, figureSteps[i]):
            out["minimumDistanceToLandmark"] = 0
        if math2d.isInteriorPoint(landmark, figure[-1]):
            out["distFigureEndToLandmark"] = 0

        differences = [d1 - d2 for d1, d2 in zip(distances, distances[1:])]
        differences = [d / d if d != 0 else 0 for d in differences]
        if len(differences) == 0:
            out["averageDistanceDifference"] = 0
        else:
            out["averageDistanceDifference"] = math2d.mean(differences)
        for p1, p2 in zip(points, points[1:]):
            drawer.drawLine("averageDistanceDifference", [p1, p2])

        return out
Esempio n. 15
0
    def doCompute(self, drawer, landmark, figure, **args):
        out = {}
        bborigin, (width,
                   height) = math2d.boundingBox(na.append(landmark, figure, 0))
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        landmarkCentroid = math2d.centroid(landmark)
        drawer.distanceFeature(out, "distFigureEndToLandmarkCentroid",
                               "Landmark center of mass", "Figure",
                               landmarkCentroid, figure[-1], scale)

        drawer.distanceFeature(out, "distFigureStartToLandmarkCentroid",
                               "Landmark center of mass", "Figure",
                               landmarkCentroid, figure[0], scale)

        if math2d.isInteriorPoint(landmark, figure[0]):
            drawer.distanceFeature(out, "distFigureStartToLandmark",
                                   "Closest point on landmark", "Figure Start",
                                   figure[0], figure[0], scale)
        else:
            cp = math2d.closestPointOnPolygon(landmark, figure[0])

            drawer.distanceFeature(out, "distFigureStartToLandmark",
                                   "Closest point on landmark", "Figure Start",
                                   figure[0], cp, scale)

        if math2d.isInteriorPoint(landmark, figure[-1]):
            drawer.distanceFeature(out, "distFigureEndToLandmark",
                                   "Closest point on landmark", "Figure End",
                                   figure[-1], figure[-1], scale)
        else:
            cp = math2d.closestPointOnPolygon(landmark, figure[-1])
            drawer.distanceFeature(out, "distFigureEndToLandmark",
                                   "Closest point on landmark", "Figure End",
                                   cp, figure[-1], scale)

        return out
Esempio n. 16
0
    def doCompute(self, drawer, ground, figure, **args):
        steps = 100
        fPoints = list(
            math2d.stepAlongLine(figure,
                                 math2d.length(figure) / 100))

        gPoints = [math2d.closestPointOnPolygon(ground, p) for p in fPoints]
        distances = [
            math2d.squaredist(f1, g1) for f1, g1 in zip(fPoints, gPoints)
        ]

        i, minDist = math2d.argMin(distances)
        bborigin, (width, height) = math2d.boundingBox(ground + figure)
        scale = pow(pow(width, 2) + pow(height, 2), 0.5)
        featureMap = {}
        drawer.distanceFeature(self, featureMap, "minimumDistanceToGround",
                               "Figure", "Ground", fPoints[i], gPoints[i],
                               scale)

        for f1, g1 in zip(fPoints, gPoints):
            self.drawSegment("averageDistanceToGround", f1, g1)
        featureMap["averageDistanceToGround"] = math2d.mean(distances) / scale

        return featureMap
Esempio n. 17
0
    def doCompute(self, drawer, landmark, figure, **args):
        major, minor = computeAxes(landmark, figure)

        if (major, minor) == (None, None):
            centroid = math2d.centroid(landmark)
            for x in self.names():
                drawer.drawText(x, centroid, "Couldn't find axes.")
            return None
        else:
            map = {}
            origin = math2d.intersectSegment(major, minor)

            centroid = math2d.centroid(landmark)
            bborigin, (width, height) = math2d.boundingBox(landmark + figure)
            scale = pow(pow(width, 2) + pow(height, 2), 0.5)
            drawer.distanceFeature(map, "centroidToAxesOrigin", "Centroid",
                                   "Axes Origin", centroid, origin, scale)
            drawer.drawAxes("centroidToAxesOrigin", (major, minor))

            sampledFig = [
                x for x in math2d.stepAlongLine(figure,
                                                math2d.length(figure) / 100)
            ]
            figureCentroid = math2d.centerOfMass(sampledFig)
            drawer.distanceFeature(map, "figureCenterOfMassToAxesOrigin",
                                   "Figure Center of Mass", "Axes Origin",
                                   figureCentroid, origin, scale)
            drawer.drawAxes("figureCenterOfMassToAxesOrigin", (major, minor))

            drawer.distanceFeature(map, "figureCenterOfMassToLandmarkCentroid",
                                   "Figure Center of Mass", "Centroid",
                                   figureCentroid, centroid, scale)

            drawer.distanceFeature(
                map, "axesStartToLandmark", "Landmark", "Start of minor axis",
                math2d.closestPointOnPolygon(landmark, minor[0]), minor[0],
                scale)
            drawer.drawAxes("axesStartToLandmark", (major, minor))

            drawer.distanceFeature(
                map, "axesEndToLandmark", "Landmark", "End of minor axis",
                math2d.closestPointOnPolygon(landmark, minor[-1]), minor[-1],
                scale)

            drawer.drawAxes("axesEndToLandmark", (major, minor))

            map['axesToLandmarkSum'] = map['axesEndToLandmark'] + map[
                'axesStartToLandmark']
            drawer.drawAxes("axesToLandmarkSum", (major, minor))

            map['figureLengthByCrow'] = math2d.ratioLengthByCrow(figure)
            drawer.drawDistance("figureLengthByCrow", figure[0], figure[-1],
                                "Start", "End")

            m1 = minor[0]
            f1 = figure[0]
            m2 = minor[-1]
            f2 = figure[-1]
            d1 = math2d.dist(m1, f1) + math2d.dist(m2, f2)
            d2 = math2d.dist(m1, f2) + math2d.dist(m2, f1)
            if (math.fabs(d1 - d2) > math2d.threshold and d1 > d2):
                f1 = figure[-1]
                f2 = figure[0]

            drawer.distanceFeature(map, "axesStartToFigureStart", "Axes Start",
                                   "Figure start", m1, f1, scale)

            drawer.distanceFeature(map, "axesEndToFigureEnd", "Axes End",
                                   "Figure End", m2, f2, scale)

            map['axesToFigureSum'] = map['axesEndToFigureEnd'] + map[
                'axesStartToFigureStart']
            drawer.drawDistance('axesToFigureSum', m2, f2, 'Axes End',
                                'Figure End')
            # from Rao at Winston's group meeting 12-9-2008
            map['distAlongLandmarkBtwnAxes'] = \
                math2d.distBetweenPointsAlongPolygon(landmark,
                                                     minor[0], minor[-1]) / math2d.perimeter(landmark)
            drawer.drawAxes("distAlongLandmarkBtwnAxes", (major, minor))
            drawer.drawPoint("distAlongLandmarkBtwnAxes", figure[0], "",
                             Qt.green, 30)
            drawer.drawPoint("distAlongLandmarkBtwnAxes", figure[-1], "",
                             Qt.red, 30)

            map['ratioFigureToAxes'] = \
                math2d.dist(figure[0], figure[-1]) / math2d.length(minor)
            drawer.drawAxes("ratioFigureToAxes", (major, minor))

            drawer.drawDistance("ratioFigureToAxes", figure[0], figure[-1],
                                "Figure Start", "Figure End")
            drawer.drawDistance("ratioFigureToAxes", minor[0], minor[-1],
                                "Minor Start", "Minor End")


            map['ratioLengthFigureToAxes'] = \
                math2d.length(figure) / math2d.length(minor)
            drawer.drawAxes("ratioLengthFigureToAxes", (major, minor))

            return map
    def doCompute(self, drawer, landmark, figure, **args):
        line = self._lineFunction(landmark, figure)
        for x in self.names():
            drawer.drawLine(x, line)
        if line == None:
            raise InsaneExample("Couldn't get line: %s %s" %
                                (landmark, figure))
        else:
            steps = 100
            figure = math2d.trimLine(figure, line[0], line[-1])
            if len(figure) <= 1:
                print "degenerate figure", figure, landmark
                raise InsaneExample("Figure was too short: %s" % figure)
            d_f = math2d.length(figure) / float(steps)
            fpoints = [x for x in math2d.stepAlongLine(figure, d_f)]
            distances = [
                math2d.dist(f1, math2d.closestPointOnLine(line, f1))
                for f1 in fpoints
            ]
            start, stop = math2d.smallestWindow(distances,
                                                int(len(distances) * 0.75))

            distances = []
            maxDist = 0.0
            maxp1 = None
            maxp2 = None
            bborigin, (width, height) = math2d.boundingBox(figure)
            scaleFactor = pow(pow(width, 2) + pow(height, 2), 0.5)
            for f1 in fpoints[start:stop]:
                g1 = math2d.closestPointOnLine(line, f1)
                drawer.drawSegment("averageDistToAxes", f1, g1)
                drawer.drawSegment("stdDevToAxes", f1, g1)
                d = math2d.dist(f1, g1)
                distances.append(d / scaleFactor)
                if d > maxDist:
                    maxDist = d
                    maxp1 = f1
                    maxp2 = g1

            if len(distances) == 0:
                map = {}
                centroid = math2d.centroid(landmark)
                for x in self.names():
                    drawer.drawText(x, centroid, "Couldn't find axes.")
                    map[x] = -1
                return map

            mean = math2d.mean(distances)
            stdDev = math2d.stdDeviation(distances)

            whitenedMean = math2d.mean([x - mean for x in distances])
            drawer.drawSegment("peakDistToAxes", maxp1, maxp2)
            f1 = math2d.closestPointOnLine(figure, line[0])
            f2 = math2d.closestPointOnLine(figure, line[-1])

            distF1F2 = math2d.distBetweenPointsAlongLine(figure, f1, f2)
            if distF1F2 != 0:
                x = math.fabs(distF1F2 / math2d.length(line))
                relativeLength = min(x, 1.0 / x)
                #relativeLength = 1.0/x
                #x = math2d.length(line) / math2d.length(figure)
                #relativeLength = min(x, 1.0/x)
            else:
                relativeLength = -1

            drawer.drawPoint("relativeLength", f1)
            drawer.drawPoint("relativeLength", f2)
            drawer.drawLine("relativeLength", line)

            return {
                "averageDistToAxes": mean,
                "whitenedAverageDist": whitenedMean,
                "peakDistToAxes": maxDist / scaleFactor,
                "stdDevToAxes": stdDev,
                "relativeLength": relativeLength
            }