vector<PointD> generateSegment(PointD startPoint, PointD endPoint, double step, const char* filename)
{
    vector<PointD> P;
    //AB: y = a x + b where a = dy/dx and b = yA - a(xA)
    double dx = endPoint[0] - startPoint[0] , dy = endPoint[1] - startPoint[1];
    double x, y;
    if(dx == 0) //vertical line
    {
        x = startPoint[0];
        if(startPoint[1]<endPoint[1])
            for(y = startPoint[1]; y <= endPoint[1]; y += step)
                P.push_back(PointD(x,y));
        else
            for(y = startPoint[1]; y >= endPoint[1]; y -= step)
                P.push_back(PointD(x,y));
    }
    else
    {
        double a = dy/dx;
        double b = startPoint[1] - a*startPoint[0];
        if(startPoint[0]<endPoint[0])
            for(x = startPoint[0]; x <= endPoint[0]; x += step)
                P.push_back(PointD(x,a*x+b));
        else
            for(x = startPoint[0]; x >= endPoint[0]; x -= step)
                P.push_back(PointD(x,a*x+b));
    }
    /* save into files */
    std::ofstream FILE(filename);
    for(vector<PointD>::const_iterator it = P.begin(); it != P.end(); it++) {
        FILE << (*it)[0]<<" "<<(*it)[1] <<'\n';
    }
    /* save into files */
    return P;
}
Ejemplo n.º 2
0
void FixSubgraphSizes(ICluster * cluster, RectD  & parentRect)
{
	RectD childRect;
	for(IClusterSet::const_iterator itr = cluster->GetClusters().begin(); itr != cluster->GetClusters().end(); ++itr)
	{
		std::string cluster;
		FixSubgraphSizes(itr->get(), childRect);
	}

	for(IVertexSet::const_iterator itr = cluster->GetVertices().begin(); itr != cluster->GetVertices().end(); ++itr)
	{
		if (childRect.IsEmptyArea())
			childRect = GetBoundingRect(itr->get());
		else
			childRect.Union(GetBoundingRect(itr->get()));
	}

	//if (!GetBoundingRect(cluster).Contains(childRect))
	{
		if (childRect.IsEmptyArea())
			childRect = GetBoundingRect(cluster);
		else
			childRect.Union(GetBoundingRect(cluster));


		if (ElementG * eg = GetElementG(cluster))
		{
			assert(eg->m_polygons.size() == 1);
			if (eg->m_polygons.size() == 1)
			{
				eg->m_polygons[0]->m_points.clear();
				eg->m_polygons[0]->SetBoundingBox(RectD());
				eg->m_polygons[0]->m_points.push_back(PointD(childRect.GetLeft(), childRect.GetBottom()));
				eg->m_polygons[0]->m_points.push_back(PointD(childRect.GetLeft(), childRect.GetTop()));
				eg->m_polygons[0]->m_points.push_back(PointD(childRect.GetRight(), childRect.GetTop()));
				eg->m_polygons[0]->m_points.push_back(PointD(childRect.GetRight(), childRect.GetBottom()));
				eg->m_polygons[0]->m_points.push_back(PointD(childRect.GetLeft(), childRect.GetBottom()));
			}
			//eg->SetBoundingBox(childRect);
			eg->CalcBoundingBox();
		}

		//childRect.Union(GetBoundingRect(cluster));
		//ElementGPtr elementG = new ElementG(childRect);
		//cluster->SetProperty(SVG_PROP_ELEMENTG, elementG);
	}
	if (parentRect.IsEmptyArea())
		parentRect = childRect;
	else
		parentRect.Union(childRect);
}
Ejemplo n.º 3
0
PolygonD Node::get_polygon() const
{
    log("calling get_polygon from a node at position (%f,%f)",cast_to_double(point.x()),cast_to_double(point.y()));
    Arrangement_2::Face_const_handle f;
    Arrangement_2::Halfedge_const_handle h;
    Arrangement_2::Vertex_const_handle v;
    PolygonD poly;
    if (CGAL::assign(f,spl.locate(Point_2(point.x(),point.y()))))
    {
        Arrangement_2::Ccb_halfedge_const_circulator c=f->outer_ccb();
        Arrangement_2::Ccb_halfedge_const_circulator start=c;
        do
        {
            poly.push_back(PointD(cast_to_double(c->source()->point().x()),cast_to_double(c->source()->point().y())));
            c++;
        } while (c!=start);
    }
    else if (CGAL::assign(h,spl.locate(Point_2(point.x(),point.y()))))
    {
        log("error: expected face, not halfedge!");
    }
    else if (CGAL::assign(v,spl.locate(Point_2(point.x(),point.y()))))
    {
        log("error: expected face, not vertex!");
    }
    else
    {
        log("error: expected face, didn't find anything!");
    }
    return poly;
}
vector<PointD> generateArc(PointD center, double radius, double angleStart, double angleEnd, const char* filename)
{
    vector<PointD> P;
    double step = 1/radius;
    if(angleStart<angleEnd)
        for(double theta = angleStart; theta<angleEnd; theta += step)
            P.push_back(PointD(center[0]+radius*sin(theta), center[1]+radius*cos(theta)));
    else
        for(double theta = angleEnd; theta<angleStart; theta += step)
            P.push_back(PointD(center[0]+radius*sin(theta), center[1]+radius*cos(theta)));
    /* save into files */
    std::ofstream FILE(filename);
    for(vector<PointD>::const_iterator it = P.begin(); it != P.end(); it++) {
        FILE << (*it)[0]<<" "<<(*it)[1] <<'\n';
    }
    /* save into files */
    return P;
}
Ejemplo n.º 5
0
bool TextSelection::IsOverGlyph(int pageNo, double x, double y) {
    int textLen;
    RectI* coords;
    textCache->GetData(pageNo, &textLen, &coords);

    int glyphIx = FindClosestGlyph(pageNo, x, y);
    PointI pt = PointD(x, y).ToInt();
    // when over the right half of a glyph, FindClosestGlyph returns the
    // index of the next glyph, in which case glyphIx must be decremented
    if (glyphIx == textLen || !coords[glyphIx].Contains(pt))
        glyphIx--;
    if (-1 == glyphIx)
        return false;
    return coords[glyphIx].Contains(pt);
}
Ejemplo n.º 6
0
// returns the index of the glyph closest to the right of the given coordinates
// (i.e. when over the right half of a glyph, the returned index will be for the
// glyph following it, which will be the first glyph (not) to be selected)
int TextSelection::FindClosestGlyph(int pageNo, double x, double y) {
    int textLen;
    RectI* coords;
    textCache->GetData(pageNo, &textLen, &coords);
    PointD pt = PointD(x, y);

    unsigned int maxDist = UINT_MAX;
    PointI pti = pt.ToInt();
    bool overGlyph = false;
    int result = -1;

    for (int i = 0; i < textLen; i++) {
        if (!coords[i].x && !coords[i].dx)
            continue;
        if (overGlyph && !coords[i].Contains(pti))
            continue;

        unsigned int dist = distSq((int)x - coords[i].x - coords[i].dx / 2, (int)y - coords[i].y - coords[i].dy / 2);
        if (dist < maxDist) {
            result = i;
            maxDist = dist;
        }
        // prefer glyphs the cursor is actually over
        if (!overGlyph && coords[i].Contains(pti)) {
            overGlyph = true;
            result = i;
            maxDist = dist;
        }
    }

    if (-1 == result)
        return 0;
    CrashIf(result < 0 || result >= textLen);

    // the result indexes the first glyph to be selected in a forward selection
    RectD bbox = engine->Transform(coords[result].Convert<double>(), pageNo, 1.0, 0);
    pt = engine->Transform(pt, pageNo, 1.0, 0);
    if (pt.x > bbox.x + 0.5 * bbox.dx) {
        result++;
        // for some (DjVu) documents, all glyphs of a word share the same bbox
        while (result < textLen && coords[result - 1] == coords[result])
            result++;
    }
    CrashIf(result > 0 && result < textLen && coords[result] == coords[result - 1]);

    return result;
}
PointD determineCenter(PointD p1, PointD p2, PointD p3)
{
    double a1, b1, a2, b2, c1, c2;
    double xA, yA, xB, yB, xC, yC;
    xA=p1[0];
    yA=p1[1];
    xB=p2[0];
    yB=p2[1];
    xC=p3[0];
    yC=p3[1];

    a1=xA-xB;
    b1=yA-yB;
    a2=xA-xC;
    b2=yA-yC;
    c1=(xA*xA-xB*xB+yA*yA-yB*yB)/2;
    c2=(xA*xA-xC*xC+yA*yA-yC*yC)/2;
    double x,y,dentaY;
    dentaY=b1*a2-a1*b2;
    if(dentaY!=0) {
        y= (double)(c1*a2-a1*c2)/(double)dentaY;
        if (a1!=0) x=(double)(c1-b1*y)/(double)a1;
        else if(a2!=0) x=(double)(c2-b2*y)/(double)a2;
        else {
            cout<<"Error: 3 points of the arc are colinear."<<endl;
            x=-1;
            y=-1;
        }
    }
    else
    {
        x=-1;
        y=-1;
    }
    return PointD(x,y);
}
vector<PointD> generateCircle(PointD center, double radius, const char* filename)
{
    vector<PointD> P;
    /*
     * x = sin(theta)*r
     * y = cos(theta)*r
    */
    double step = 1/radius;
    for(double theta = 0; theta<2*M_PI; theta += step)
        P.push_back(PointD(center[0]+radius*sin(theta), center[1]+radius*cos(theta)));
    /* save into files */
    /*
    std::ofstream FILE(filename);
    std::ostream_iterator<PointD> output_iterator(FILE, "\n");
    std::copy(P.begin(), P.end(), output_iterator);
    */
    std::ofstream FILE(filename);
    for(vector<PointD>::const_iterator it = P.begin(); it != P.end(); it++) {
        FILE << (*it)[0]<<" "<<(*it)[1] <<'\n';
    }
    /* save into files */

    return P;
}
Ejemplo n.º 9
0
static void GeomTest()
{
    PointD ptD(12.4, -13.6);
    utassert(ptD.x == 12.4 && ptD.y == -13.6);
    PointI ptI = ptD.ToInt();
    utassert(ptI.x == 12 && ptI.y == -14);
    ptD = ptI.Convert<double>();
    utassert(PointD(12, -14) == ptD);
    utassert(PointD(12.4, -13.6) != ptD);

    SizeD szD(7.7, -3.3);
    utassert(szD.dx == 7.7 && szD.dy == -3.3);
    SizeI szI = szD.ToInt();
    utassert(szI.dx == 8 && szI.dy == -3);
    szD = szI.Convert<double>();
    utassert(SizeD(8, -3) == szD);

    utassert(!szD.IsEmpty() && !szI.IsEmpty());
    utassert(SizeI().IsEmpty() && SizeD().IsEmpty());

    struct SRIData {
        int     x1s, x1e, y1s, y1e;
        int     x2s, x2e, y2s, y2e;
        bool    intersect;
        int     i_xs, i_xe, i_ys, i_ye;
        int     u_xs, u_xe, u_ys, u_ye;
    } testData[] = {
        { 0,10, 0,10,   0,10, 0,10,  true,  0,10, 0,10,  0,10, 0,10 }, /* complete intersect */
        { 0,10, 0,10,  20,30,20,30,  false, 0, 0, 0, 0,  0,30, 0,30 }, /* no intersect */
        { 0,10, 0,10,   5,15, 0,10,  true,  5,10, 0,10,  0,15, 0,10 }, /* { | } | */
        { 0,10, 0,10,   5, 7, 0,10,  true,  5, 7, 0,10,  0,10, 0,10 }, /* { | | } */
        { 0,10, 0,10,   5, 7, 5, 7,  true,  5, 7, 5, 7,  0,10, 0,10 },
        { 0,10, 0,10,   5, 15,5,15,  true,  5,10, 5,10,  0,15, 0,15 },
    };

    for (size_t i = 0; i < dimof(testData); i++) {
        struct SRIData *curr = &testData[i];

        RectI rx1(curr->x1s, curr->y1s, curr->x1e - curr->x1s, curr->y1e - curr->y1s);
        RectI rx2 = RectI::FromXY(curr->x2s, curr->y2s, curr->x2e, curr->y2e);
        RectI isect = rx1.Intersect(rx2);
        if (curr->intersect) {
            utassert(!isect.IsEmpty());
            utassert(isect.x == curr->i_xs && isect.y == curr->i_ys);
            utassert(isect.x + isect.dx == curr->i_xe && isect.y + isect.dy == curr->i_ye);
        }
        else {
            utassert(isect.IsEmpty());
        }
        RectI urect = rx1.Union(rx2);
        utassert(urect.x == curr->u_xs && urect.y == curr->u_ys);
        utassert(urect.x + urect.dx == curr->u_xe && urect.y + urect.dy == curr->u_ye);

        /* if we swap rectangles, the results should be the same */
        std::swap(rx1, rx2);
        isect = rx1.Intersect(rx2);
        if (curr->intersect) {
            utassert(!isect.IsEmpty());
            utassert(isect.x == curr->i_xs && isect.y == curr->i_ys);
            utassert(isect.x + isect.dx == curr->i_xe && isect.y + isect.dy == curr->i_ye);
        }
        else {
            utassert(isect.IsEmpty());
        }
        urect = rx1.Union(rx2);
        utassert(RectI::FromXY(curr->u_xs, curr->u_ys, curr->u_xe, curr->u_ye) == urect);

        utassert(!rx1.Contains(PointI(-2, -2)));
        utassert(rx1.Contains(rx1.TL()));
        utassert(!rx1.Contains(PointI(rx1.x, INT_MAX)));
        utassert(!rx1.Contains(PointI(INT_MIN, rx1.y)));
    }
}
Ejemplo n.º 10
0
	virtual void OnStartElement(const XML_Char *pszName, const XML_Char **papszAttrs)
	{
		CStackParser::OnStartElement(pszName, papszAttrs);
		CElementPtr e = m_stack.top();

		if (0 == e->m_tag.compare("svg"))
		{
			m_valid = true;
			for(hpcc::IClusterSet::const_iterator itr = m_graph->GetAllClusters().begin(); itr != m_graph->GetAllClusters().end(); ++itr)
			{
				itr->get()->SetProperty(SVG_PROP_ELEMENTG, (CUnknown *)NULL);
			}
			for(hpcc::IVertexSet::const_iterator itr = m_graph->GetAllVertices().begin(); itr != m_graph->GetAllVertices().end(); ++itr)
			{
				itr->get()->SetProperty(SVG_PROP_ELEMENTG, (CUnknown *)NULL);
			}
			for(hpcc::IEdgeSet::const_iterator itr = m_graph->GetAllEdges().begin(); itr != m_graph->GetAllEdges().end(); ++itr)
			{
				itr->get()->SetProperty(SVG_PROP_ELEMENTG, (CUnknown *)NULL);
			}
		}
		else if (m_valid && 0 == e->m_tag.compare("g"))
		{
			if (m_offset.Empty())
			{
				std::string transform = e->m_attr["transform"];
				if (!transform.empty())
				{
					static const char * const TRANSLATE = "translate(";
					static int TRANSLATE_SIZE = strlen(TRANSLATE);
					const char * start = strstr(transform.c_str(), TRANSLATE);
					const char * mid = strstr(start, " ");
					const char * end = strstr(mid, ")");
					if (start && mid && end)
					{
						char x[8], y[8];
						memset(x, 0, 8);
						memset(y, 0, 8);
						strncpy(x, start + TRANSLATE_SIZE, mid - (start + TRANSLATE_SIZE));
						strncpy(y, mid + 1, end - (mid + 1));
						m_offset = CreatePointD(x, y, PointD(), DOT_DPI);
					}
				}
			}

			GRAPH_TYPE type = GRAPH_TYPE_UNKNOWN;
			if (e->m_attr["class"].compare("graph") == 0)
				type = GRAPH_TYPE_GRAPH;
			else if (e->m_attr["class"].compare("cluster") == 0)
				type = GRAPH_TYPE_CLUSTER;
			else if (e->m_attr["class"].compare("node") == 0)
				type = GRAPH_TYPE_VERTEX;
			else if (e->m_attr["class"].compare("edge") == 0)
				type = GRAPH_TYPE_EDGE;
			m_currentItem = m_graph->GetGraphItem(type, e->m_attr["id"]);
			if (!m_currentItem) {
				throw ParseException();
			}
			m_currentElementG = new ElementG();
			m_currentItem->SetProperty(SVG_PROP_ELEMENTG, m_currentElementG);
		}

		return;
	}
Ejemplo n.º 11
0
void ScatterWidget::addData(float x, float y, string legend)
{
    addData(PointD(x, y), legend);
}
bool OsmAnd::MapObjectsSymbolsProvider_P::computeSymbolPinPoint(
    const QVector<float>& pathSegmentsLengthInPixels,
    const float pathLengthInPixels,
    const QVector<double>& pathSegmentsLength31,
    const QVector<PointI>& path31,
    const ComputedPinPoint& blockPinPoint,
    const float neededZoomPixelScaleFactor,
    const float offsetFromBlockPinPointInPixelsOnNeededZoom,
    ComputedPinPoint& outComputedSymbolPinPoint) const
{
    const auto pathSegmentsCount = pathSegmentsLengthInPixels.size();
    const auto offsetFromOriginPathPoint =
        (pathSegmentsLengthInPixels[blockPinPoint.basePathPointIndex] * blockPinPoint.normalizedOffsetFromBasePathPoint * neededZoomPixelScaleFactor) +
        offsetFromBlockPinPointInPixelsOnNeededZoom;

    if (offsetFromOriginPathPoint >= 0.0f)
    {
        // In case start point is located after origin point ('on the right'), usual search is used

        auto testPathPointIndex = blockPinPoint.basePathPointIndex;
        auto scannedLength = 0.0f;
        while (scannedLength <= offsetFromOriginPathPoint)
        {
            if (testPathPointIndex >= pathSegmentsCount)
                return false;
            const auto segmentLength = pathSegmentsLengthInPixels[testPathPointIndex] * neededZoomPixelScaleFactor;
            if (scannedLength + segmentLength >= offsetFromOriginPathPoint)
            {
                const auto pathPointIndex = testPathPointIndex;
                const auto offsetFromPathPoint = offsetFromOriginPathPoint - scannedLength;
                const auto nOffsetFromPoint = offsetFromPathPoint / segmentLength;
                const auto& segmentStartPoint31 = path31[testPathPointIndex + 0];
                const auto& segmentEndPoint31 = path31[testPathPointIndex + 1];
                const auto& vSegment31 = segmentEndPoint31 - segmentStartPoint31;

                outComputedSymbolPinPoint.point31 = segmentStartPoint31 + PointI(PointD(vSegment31) * nOffsetFromPoint);
                outComputedSymbolPinPoint.basePathPointIndex = pathPointIndex;
                outComputedSymbolPinPoint.offsetFromBasePathPoint31 = pathSegmentsLength31[testPathPointIndex] * nOffsetFromPoint;
                outComputedSymbolPinPoint.normalizedOffsetFromBasePathPoint = nOffsetFromPoint;

                return true;
            }
            scannedLength += segmentLength;
            testPathPointIndex++;
        }
    }
    else
    {
        // In case start point is located before origin point ('on the left'), reversed search is used
        if (blockPinPoint.basePathPointIndex == 0)
            return false;

        auto testPathPointIndex = blockPinPoint.basePathPointIndex - 1;
        auto scannedLength = 0.0f;
        while (scannedLength >= offsetFromOriginPathPoint)
        {
            const auto& segmentLength = pathSegmentsLengthInPixels[testPathPointIndex] * neededZoomPixelScaleFactor;
            if (scannedLength - segmentLength <= offsetFromOriginPathPoint)
            {
                const auto pathPointIndex = testPathPointIndex;
                const auto offsetFromPathPoint = segmentLength + (offsetFromOriginPathPoint - scannedLength);
                const auto nOffsetFromPoint = offsetFromPathPoint / segmentLength;
                const auto& segmentStartPoint31 = path31[testPathPointIndex + 0];
                const auto& segmentEndPoint31 = path31[testPathPointIndex + 1];
                const auto& vSegment31 = segmentEndPoint31 - segmentStartPoint31;

                outComputedSymbolPinPoint.point31 = segmentStartPoint31 + PointI(PointD(vSegment31) * nOffsetFromPoint);
                outComputedSymbolPinPoint.basePathPointIndex = pathPointIndex;
                outComputedSymbolPinPoint.offsetFromBasePathPoint31 = pathSegmentsLength31[testPathPointIndex] * nOffsetFromPoint;
                outComputedSymbolPinPoint.normalizedOffsetFromBasePathPoint = nOffsetFromPoint;

                return true;
            }
            scannedLength -= segmentLength;
            if (testPathPointIndex == 0)
                return false;
            testPathPointIndex--;
        }
    }

    return false;
}
bool OsmAnd::MapObjectsSymbolsProvider_P::computeBlockPinPoint(
    const QVector<float>& pathSegmentsLengthInPixels,
    const float pathLengthInPixels,
    const QVector<double>& pathSegmentsLength31,
    const QVector<PointI>& path31,
    const float blockWidthInPixels,
    const float offsetFromPathStartInPixels,
    const unsigned int scanOriginPathPointIndex,
    const float scanOriginPathPointOffsetInPixels,
    unsigned int& outNextScanOriginPathPointIndex,
    float& outNextScanOriginPathPointOffsetInPixels,
    ComputedPinPoint& outComputedBlockPinPoint) const
{
    const auto pathSegmentsCount = pathSegmentsLengthInPixels.size();
    const auto startOffset = offsetFromPathStartInPixels;
    const auto pinPointOffset = startOffset + blockWidthInPixels / 2.0f;
    const auto endOffset = startOffset + blockWidthInPixels;
    if (endOffset > pathLengthInPixels)
        return false;

    bool blockPinPointFound = false;
    auto testPathPointIndex = scanOriginPathPointIndex;
    auto scannedLengthInPixels = scanOriginPathPointOffsetInPixels;
    while (scannedLengthInPixels <= pinPointOffset)
    {
        if (testPathPointIndex >= pathSegmentsCount)
        {
            assert(false);
            return false;
        }
        const auto& segmentLengthInPixels = pathSegmentsLengthInPixels[testPathPointIndex];
        if (scannedLengthInPixels + segmentLengthInPixels >= pinPointOffset)
        {
            const auto nOffsetFromPoint = (pinPointOffset - scannedLengthInPixels) / segmentLengthInPixels;
            const auto& segmentStartPoint31 = path31[testPathPointIndex + 0];
            const auto& segmentEndPoint31 = path31[testPathPointIndex + 1];
            const auto& vSegment31 = segmentEndPoint31 - segmentStartPoint31;

            // Compute block pin-point
            outComputedBlockPinPoint.point31 = segmentStartPoint31 + PointI(PointD(vSegment31) * nOffsetFromPoint);
            outComputedBlockPinPoint.basePathPointIndex = testPathPointIndex;
            outComputedBlockPinPoint.offsetFromBasePathPoint31 = pathSegmentsLength31[testPathPointIndex] * nOffsetFromPoint;
            outComputedBlockPinPoint.normalizedOffsetFromBasePathPoint = nOffsetFromPoint;
            blockPinPointFound = true;

            outNextScanOriginPathPointIndex = testPathPointIndex;
            outNextScanOriginPathPointOffsetInPixels = scannedLengthInPixels;
            break;
        }
        scannedLengthInPixels += segmentLengthInPixels;
        testPathPointIndex++;
    }
    while (scannedLengthInPixels < endOffset)
    {
        if (testPathPointIndex >= pathSegmentsCount)
        {
            assert(false);
            return false;
        }
        const auto& segmentLengthInPixels = pathSegmentsLengthInPixels[testPathPointIndex];
        if (scannedLengthInPixels + segmentLengthInPixels > endOffset)
        {
            outNextScanOriginPathPointIndex = testPathPointIndex;
            outNextScanOriginPathPointOffsetInPixels = scannedLengthInPixels;
            break;
        }
        scannedLengthInPixels += segmentLengthInPixels;
        testPathPointIndex++;
    }

    return blockPinPointFound;
}
Ejemplo n.º 14
0
PointD Map::getTurnOuterCorner(int x, int y, const TilePathNode& turn) const
{
	static const double displacement = m_game->getTrackTileMargin() + std::min(m_game->getCarWidth(), m_game->getCarHeight()) / 2;
	static const double bigDisplacement = m_game->getTrackTileSize() - displacement;

	static const std::map<model::TileType, PointD> displacementMap = 
	{
		{ model::LEFT_TOP_CORNER,     PointD(displacement,    displacement) },
		{ model::LEFT_BOTTOM_CORNER,  PointD(displacement,    bigDisplacement) },
		{ model::RIGHT_TOP_CORNER,    PointD(bigDisplacement, displacement) },
		{ model::RIGHT_BOTTOM_CORNER, PointD(bigDisplacement, bigDisplacement) },

		// others not yet implemented
	};

	model::TileType tileType = getTileType(x, y);

	switch (tileType)
	{
	case model::LEFT_HEADED_T:
	case model::RIGHT_HEADED_T:
	case model::TOP_HEADED_T:
	case model::BOTTOM_HEADED_T:
	case model::CROSSROADS:
	{
		// T-turn or crossroads handling is similar to corresponding usual turn

		struct TurnTuple 
		{
			TilePathNode::AbsoluteTurn from, to; 
			bool operator==(const TurnTuple&r) const { return from == r.from && to == r.to; }
		};

		typedef std::pair<TurnTuple, model::TileType> TurnsCornerPair;
		static const TurnsCornerPair turnsToCorner[] =
		{
			{ { AbsoluteDirection::UP,   AbsoluteDirection::RIGHT }, model::LEFT_TOP_CORNER },
			{ { AbsoluteDirection::LEFT, AbsoluteDirection::DOWN },  model::LEFT_TOP_CORNER },

			{ { AbsoluteDirection::UP,    AbsoluteDirection::LEFT},  model::RIGHT_TOP_CORNER },
			{ { AbsoluteDirection::RIGHT, AbsoluteDirection::DOWN }, model::RIGHT_TOP_CORNER },

			{ { AbsoluteDirection::LEFT,  AbsoluteDirection::UP },    model::LEFT_BOTTOM_CORNER },
			{ { AbsoluteDirection::DOWN,  AbsoluteDirection::RIGHT }, model::LEFT_BOTTOM_CORNER },

			{ { AbsoluteDirection::RIGHT, AbsoluteDirection::UP},    model::RIGHT_BOTTOM_CORNER },
			{ { AbsoluteDirection::DOWN,  AbsoluteDirection::LEFT},  model::RIGHT_BOTTOM_CORNER },
		};

		TurnTuple thisTurn = {turn.m_turnAbsoluteFrom, turn.m_turnAbsolute/*to*/};
		auto found = std::find_if(std::begin(turnsToCorner), std::end(turnsToCorner), [&thisTurn](const TurnsCornerPair& tupleCornerPair) 
		{
			return tupleCornerPair.first == thisTurn;
		});

		if (found != std::end(turnsToCorner))
		{
			tileType = found->second;   // assume this crossroad to be usual turn
		}		

		break;
	}
	default:
		break;
	}

	auto displacementIt = displacementMap.find(tileType);
	return getTileCorner(x, y) + (displacementIt != displacementMap.end() ? displacementIt->second : m_tileCenter);
}