void
FgGridTriangles::intersects(FgVect2F pos,vector<FgTriPoint> & ret) const
{
    ret.clear();
    FgVect2F            gridCoord = m_clientToGridCoords * pos;
    if ((fgMinElem(gridCoord) < 0.0f) ||
        (gridCoord[0] >= float(m_grid.width())) ||
        (gridCoord[1] >= float(m_grid.height())))
        return;
    FgVect2UI           binIdx = FgVect2UI(gridCoord);
    const vector<uint> & bin = m_grid[binIdx];
    for (size_t ii=0; ii<bin.size(); ++ii) {
        FgTriPoint      tp;
        tp.triInd = bin[ii];
        tp.pointInds = m_tris[bin[ii]];
        FgOpt<FgVect3D>    vbc = fgBarycentricCoords(pos,
            m_points[tp.pointInds[0]],
            m_points[tp.pointInds[1]],
            m_points[tp.pointInds[2]]);
        if (vbc.valid()) {
            tp.baryCoord = FgVect3F(vbc.val());
            if (fgMinElem(tp.baryCoord) >= 0.0f)
                ret.push_back(tp);
        }
    }
}
void
FgGuiApi3d::markSurfPoint(
    FgVect2UI           winSize,
    FgVect2I            pos,
    FgMat44F            toOics)         // Transforms frustum to [-1,1] cube (depth & y inverted)
{
    if (g_gg.dg.sinkNode(meshesN))      // feature disabled if node not modifiable
        return;
    vector<Fg3dMesh>            meshes = g_gg.getVal(meshesN);
    const FgVertss &            vertss = g_gg.getVal(vertssN);
    FgOpt<FgMeshesIntersect>    vpt = fgMeshesIntersect(winSize,pos,toOics,meshes,vertss);
    if (vpt.valid()) {
        FgMeshesIntersect       pt = vpt.val();
        pt.surfPnt.label = g_gg.getVal(pointLabel).as_ascii();
        vector<FgSurfPoint> &   surfPoints =  meshes[pt.meshIdx].surfaces[pt.surfIdx].surfPoints;
        if (!pt.surfPnt.label.empty()) {
            for (size_t ii=0; ii<surfPoints.size(); ++ii) {
                if (surfPoints[ii].label == pt.surfPnt.label) {     // Replace SPs of same name:
                    surfPoints[ii] = pt.surfPnt;
                    g_gg.setVal(meshesN,meshes);
                    return;
                }
            }
        }
        // Add new SP:
        surfPoints.push_back(pt.surfPnt);
        g_gg.setVal(meshesN,meshes);
    }
}
static
FgOpt<MeshesSurfPoint>
intersect(
    FgVect2UI                   winSize,
    FgVect2I                    pos,
    FgMat44F                 toOics, // Transforms frustum to [-1,1] cube (depth & y inverted)
    const vector<Fg3dMesh> &    meshes,
    const vector<FgVerts> &     vertss)
{
    MeshesSurfPoint     ret;
    FgValid<float>      minDepth;
    FgVect2D            pnt(pos);
    FgMat32F         oics(-1,1,1,-1,0,1);
    FgMat32F         rcs(-0.5f,winSize[0]-0.5f,-0.5f,winSize[1]-0.5f,0,1);
    FgAffineCw3F        oicsToRcs(oics,rcs);
    FgMat44F         invXform = oicsToRcs.asAffine().asHomogenous() * toOics;
    for (size_t mm=0; mm<meshes.size(); ++mm) {
        const Fg3dMesh &    mesh = meshes[mm];
        const FgVerts &     verts = vertss[mm];
        FgVerts             pvs(verts.size());
        for (size_t ii=0; ii<pvs.size(); ++ii) {
            FgVect4F        v = invXform * fgAsHomogVec(verts[ii]);
            pvs[ii] = v.subMatrix<3,1>(0,0) / v[3];
        }
        for (size_t ss=0; ss<mesh.surfaces.size(); ++ss) {
            const Fg3dSurface & surf = mesh.surfaces[ss];
            for (size_t tt=0; tt<surf.numTriEquivs(); ++tt) {
                FgVect3UI   tri = surf.getTriEquiv(uint(tt));
                FgVect3F    t0 = pvs[tri[0]],
                            t1 = pvs[tri[1]],
                            t2 = pvs[tri[2]];
                FgVect2D    v0 = FgVect2D(t0.subMatrix<2,1>(0,0)),
                            v1 = FgVect2D(t1.subMatrix<2,1>(0,0)),
                            v2 = FgVect2D(t2.subMatrix<2,1>(0,0));
                if (fgPointInTriangle(pnt,v0,v1,v2) == -1) {     // CC winding
                    FgOpt<FgVect3D>    vbc = fgBarycentricCoords(pnt,v0,v1,v2);
                    if (vbc.valid()) {
                        FgVect3D    bc = vbc.val();
                        // Depth value range for unclipped polys is [-1,1]. These correspond to the
                        // negative inverse depth values of the frustum.
                        // Only an approximation to the depth value but who cares:
                        double  dep = fgDot(bc,FgVect3D(t0[2],t1[2],t2[2]));
                        if (!minDepth.valid() || (dep < minDepth.val())) {    // OGL prj inverts depth
                            minDepth = dep;
                            ret.meshIdx = mm;
                            ret.surfIdx = ss;
                            ret.surfPnt.triEquivIdx = uint(tt);
                            ret.surfPnt.weights = FgVect3F(bc);
                        }
                    }
                }
            }
        }
    }
    if (minDepth.valid())
        return FgOpt<MeshesSurfPoint>(ret);
    return FgOpt<MeshesSurfPoint>();
}
void
FgGuiApi3d::ctlClick(FgVect2UI winSize,FgVect2I pos,FgMat44F toOics)
{
    const vector<Fg3dMesh> &    meshes = g_gg.getVal(meshesN);
    const vector<FgVerts> &     vertss = g_gg.getVal(vertssN);
    FgOpt<MeshesSurfPoint> vpt = intersect(winSize,pos,toOics,meshes,vertss);
    if (vpt.valid()) {
        MeshesSurfPoint     pt = vpt.val();
        uint                mi = fgMaxIdx(pt.surfPnt.weights);
        lastCtlClick.meshIdx = pt.meshIdx;
        lastCtlClick.vertIdx = meshes[pt.meshIdx].surfaces[pt.surfIdx].getTriEquiv(pt.surfPnt.triEquivIdx)[mi];
    }
}
void
FgGuiApi3d::markVertex(
    FgVect2UI               winSize,
    FgVect2I                pos,
    FgMat44F             toOics)     // Transforms frustum to [-1,1] cube (depth & y inverted)
{
    if (g_gg.dg.sinkNode(meshesN))      // feature disabled if node not modifiable
        return;
    vector<Fg3dMesh>            meshes = g_gg.getVal(meshesN);
    const FgVertss &            vertss = g_gg.getVal(vertssN);
    FgOpt<FgMeshesIntersect>    vpt = fgMeshesIntersect(winSize,pos,toOics,meshes,vertss);
    if (vpt.valid()) {
        FgMeshesIntersect   pt = vpt.val();
        uint                facetIdx = fgMaxIdx(pt.surfPnt.weights);
        Fg3dMesh &          mesh = meshes[pt.meshIdx];
        uint                vertIdx = mesh.surfaces[pt.surfIdx].getTriEquiv(pt.surfPnt.triEquivIdx)[facetIdx];
        size_t              vertMarkMode = g_gg.getVal(vertMarkModeN);
        if (vertMarkMode == 0) {
            if (!fgContains(mesh.markedVerts,vertIdx)) {
                mesh.markedVerts.push_back(FgMarkedVert(vertIdx));
                g_gg.setVal(meshesN,meshes);
            }
        }
        else if (vertMarkMode < 3) {
            const FgVerts & verts = vertss[pt.meshIdx];
            vector<FgVect3UI>   tris = mesh.getTriEquivs().vertInds;
            Fg3dTopology        topo(verts,tris);
            set<uint>           seam;
            if (vertMarkMode == 1)
                seam = topo.seamContaining(vertIdx);
            else {
                Fg3dSurface         surf;
                surf.tris.vertInds = tris;
                vector<FgBool>      done(verts.size(),false);
                seam = topo.traceFold(fgNormals(fgSvec(surf),verts),done,vertIdx);
            }
            for (set<uint>::const_iterator it=seam.begin(); it != seam.end(); ++it)
                if (!fgContains(mesh.markedVerts,*it))
                    mesh.markedVerts.push_back(FgMarkedVert(*it));
            if (!seam.empty())
                g_gg.setVal(meshesN,meshes);
        }
    }
}