cl_int2 ObjectClusterer::SearchValidPixelOnLine(const ImLine& border, const cl_int2& centerPixel, const int firstID, const int secondID)
{
    int majorAxis=0, minorAxis=1;
    if(border.IsXAxisMajor()==false)
    {
        majorAxis=1;
        minorAxis=0;
    }
    cl_int2 linePixel = centerPixel;
    int pxidx, move=1;
    int rangeMin = smin(border.endPixels[0].s[majorAxis], border.endPixels[1].s[majorAxis]);
    int rangeMax = smax(border.endPixels[0].s[majorAxis], border.endPixels[1].s[majorAxis]);
    cl_float4 coef = (cl_float4){border.a, border.b, border.c, 0.f};

    while(linePixel.s[majorAxis] >= rangeMin && linePixel.s[majorAxis] <= rangeMax)
    {
        linePixel.s[majorAxis] = centerPixel.s[majorAxis]-move;
        linePixel.s[minorAxis] = (int)(-coef.s[majorAxis]/coef.s[minorAxis]*linePixel.s[majorAxis] + coef.s[2]/coef.s[minorAxis]);
        pxidx = PIXIDX(linePixel);
        if(nullityMap[pxidx] < NullID::PointNull && (objectMap[pxidx]==firstID || objectMap[pxidx]==secondID))
            return linePixel;

        linePixel.s[majorAxis] = centerPixel.s[majorAxis]+move;
        linePixel.s[minorAxis] = (int)(-coef.s[majorAxis]/coef.s[minorAxis]*linePixel.s[majorAxis] + coef.s[2]/coef.s[minorAxis]);
        pxidx = PIXIDX(linePixel);
        if(nullityMap[pxidx] < NullID::PointNull && (objectMap[pxidx]==firstID || objectMap[pxidx]==secondID))
            return linePixel;
        move++;
    }
    throw 1;
    return centerPixel;
}
cl_int2 PointSmoother::SearchLinearDirection(cl_float4* pointCloud, const cl_int2& pixel, cl_float4& normal)
{
    // direc.x, y <= MG
    static const cl_int2 direc[] = {{2,0}, {2,2}, {0,2}, {-2,2}};
    const int szdir = 4;
    const cl_float4& herept = pointCloud[PIXIDX(pixel)];
    const float distUppLimit = clDot(normal, herept)*0.002f;
    float flatness, minDist = distUppLimit;
    int minIndex = -1;

    // search flattest direction
    for(int i=0; i<szdir; i++)
    {
        const cl_float4& leftpt = pointCloud[IMGIDX(pixel.y-direc[i].y, pixel.x-direc[i].x)];
        const cl_float4& righpt = pointCloud[IMGIDX(pixel.y+direc[i].y, pixel.x+direc[i].x)];
        if(clIsNull(leftpt) || clIsNull(righpt))
            continue;

        flatness = fabs(clDot(normal, (leftpt - righpt)));
        if(flatness < minDist)
        {
            minDist = flatness;
            minIndex = i;
        }
    }

    if(minIndex < 0)
        return (cl_int2) {
        0,0
    };
    else
        return direc[minIndex];
}
cl_float4 ObjectClusterer::VirtualPointOnPlaneAroundBorder(const Segment& plane, const ImLine& border, const cl_int2& borderCenter, bool upperPlane)
{
    const int pixelDist = 10;
    cl_int2 vpixel1st, vpixel2nd, virtualPixel;
    ImLine orthLine;
    orthLine.OrthogonalTo(border, borderCenter);
    if(orthLine.IsXAxisMajor())
    {
        vpixel1st.x = borderCenter.x + pixelDist;
        vpixel1st.y = orthLine.GetY(vpixel1st.x);
        vpixel2nd.x = borderCenter.x - pixelDist;
        vpixel2nd.y = orthLine.GetY(vpixel2nd.x);
    }
    else
    {
        vpixel1st.y = borderCenter.y + pixelDist;
        vpixel1st.x = orthLine.GetX(vpixel1st.y);
        vpixel2nd.y = borderCenter.y - pixelDist;
        vpixel2nd.x = orthLine.GetX(vpixel2nd.y);
    }

    if(upperPlane)
        virtualPixel = (border.IsAboveLine(vpixel1st)) ? vpixel1st : vpixel2nd;
    else
        virtualPixel = (border.IsAboveLine(vpixel1st)) ? vpixel2nd : vpixel1st;
#ifdef DEBUG_ObjectClusterBase
    virutalPixels.push_back(virtualPixel);
#endif

    const float normalDistBorder = fabsf(clDot(pointCloud[PIXIDX(borderCenter)], plane.normal));
    const cl_float4 rayDir = ImageConverter::ConvertPixelToPoint(virtualPixel.x, virtualPixel.y, 1.f);
    cl_float4 pointOnPlane = rayDir/fabsf(clDot(rayDir, plane.normal))*normalDistBorder;
        assert(fabsf(normalDistBorder - fabsf(clDot(pointOnPlane, plane.normal))) < 0.001f);
    return pointOnPlane;
}
float ObjectClusterer::InnerAngleBetweenPlanes(const Segment& firstPlane, const Segment& secondPlane, const vecPairOfPixels& connPixels)
{
    ImLine border = FitLine2D(connPixels);

    bool firstPlaneUp = IsFirstUpperPlane(border, connPixels);
    cl_int2 borderCenter = PickBorderCenter(border, firstPlane.id, secondPlane.id);
    const cl_float4& pointOnBorder = pointCloud[PIXIDX(borderCenter)];
    cl_float4 borderDirection = clCross(firstPlane.normal, secondPlane.normal);

    Segment scFirstPlane = ScalePlaneToIncludePoint(firstPlane, pointOnBorder);
    Segment scSecondPlane = ScalePlaneToIncludePoint(secondPlane, pointOnBorder);

    cl_float4 pointOnFirst = VirtualPointOnPlaneAroundBorder(scFirstPlane, border, borderCenter, firstPlaneUp);
    cl_float4 pointOnSecond = VirtualPointOnPlaneAroundBorder(scSecondPlane, border, borderCenter, !firstPlaneUp);

    cl_float4 firstDirFromBorder = PlaneDirectionFromBorder(scFirstPlane.normal, borderDirection, pointOnFirst - pointOnBorder);
    cl_float4 secondDirFromBorder = PlaneDirectionFromBorder(scSecondPlane.normal, borderDirection, pointOnSecond - pointOnBorder);
    float angleDegree = AngleBetweenVectorsDegree(firstDirFromBorder, secondDirFromBorder);

#ifdef DEBUG_ObjectClusterBase
    IdPairs.emplace_back(firstPlane.id, secondPlane.id);
    pointPairs.emplace_back(pointOnFirst, pointOnSecond);
    borderPoints.push_back(pointOnBorder);
    borderLines.push_back(border);
    betweenAngles.push_back(angleDegree);
    heights.emplace_back(0,0);
#endif
    return angleDegree;
}
cl_int2 ObjectClusterer::PickBorderCenter(const ImLine& border, const int firstID, const int secondID)
{
    const cl_int2 centerPixel = (border.endPixels[0] + border.endPixels[1])/2;
    const int pxidx = PIXIDX(centerPixel);
    if(nullityMap[pxidx] < NullID::PointNull && (objectMap[pxidx]==firstID || objectMap[pxidx]==secondID))
        return centerPixel;
    else
        return SearchValidPixelOnLine(border, centerPixel, firstID, secondID);
}
cl_float4 PointSmoother::SmoothePoint(cl_float4* pointCloud, const cl_int2& pixel, cl_float4& normal)
{
    // find only linear direction
    const cl_int2 linearDir = SearchLinearDirection(pointCloud, pixel, normal);
    if(linearDir.x==0 && linearDir.y==0)
        return pointCloud[PIXIDX(pixel)];

    // smoothe and debug
    return SmoothePointByMeanDepth(pointCloud, pixel, linearDir);
}
cl_float4 PointSmoother::SmoothePointByMeanDepth(cl_float4* pointCloud, const cl_int2& pixel, const cl_int2& linearDir)
{
    const cl_float4& herept = pointCloud[PIXIDX(pixel)];
    const cl_float4& righpt = pointCloud[IMGIDX(pixel.y+linearDir.y, pixel.x+linearDir.x)];
    const cl_float4& leftpt = pointCloud[IMGIDX(pixel.y-linearDir.y, pixel.x-linearDir.x)];

    float meanDepth = (DEPTH(herept) + DEPTH(leftpt) + DEPTH(righpt)) / 3.f;
    cl_float4 smoothedpt = herept / DEPTH(herept) * meanDepth;

    // Test:: normal distances of herept righpt leftpt smoothedpt

    return smoothedpt;
}