void DebugDrawTestTri(PenDrawingPath pdPath, Vector2f offset, int ientry, int iexit, float size = 50f, float dark = .5f, bool inverted = false)
        {
            IsoTriangle <PixelTriData> tri = new IsoTriangle <PixelTriData>
            {
                _base    = offset + Vector2f.AxisX * size / 2f,
                dims     = new Vector2f(size, size * TriUtil.RootThree / 2f),
                inverted = inverted
            };

            tri.Add(new PixelTriData(new PixelV {
                pos   = tri.Centroid,
                color = new Color(dark, dark, dark, 1f)
            }));


            var triCross = new TriCrosshatch <PixelTriData>(this, tri);

            triCross.DrawTriSpiral(pdPath, ientry, iexit);
        }
        void SolveTriTree(PenDrawingPath pdPath)
        {
            //CONSIDER: two passes
            // 1.) Dark very detailed
            // 2.) mid to light don't sweat the details as much

            //split tri tree leaves if not sufficiently populated
            if (generatorConfig.ShouldSplitUnderFilledTris)
            {
                triTree.root.SplitUnderPopulatedLeaves(generatorConfig.TriFilledThreshold, generatorConfig.MaxSplitToFillRecursionDepth);
            }

            triTree.root.CullDataWithMaxMean(bitMapPointGenerator.MaxGrayScale);

            List <IsoTriangle <PixelTriData> > subTrees;

            // split tree into max cities sub trees
            subTrees = triTree.root.NonEmptyChildrenWithMaxLeaves(generatorConfig.MaxTSPTriangleCities);



            //DEBUG
            var meshGO = MeshUtil.MakeGameObject(triTree.getMesh(), "CrosshatchTriTree");

            var pool      = new CMDProcessPool(generatorConfig.MaxTSPThreads);
            var subLeaves = new List <IsoTriangle <PixelTriData> > [subTrees.Count];
            var tsps      = new TSPLibProblem[subTrees.Count];

            //DEBUG delete any previously generated files
            TSPLibProblem.GetDeleteAllCommand(BaseFileName + "*").ToCMDProcess().run();

            for (int i = 0; i < subTrees.Count; ++i)
            {
                var tree = subTrees[i];

                //get leaf centers
                var leaves = tree.GetLeaves(true);

                var centers = new List <Vector2f>(leaves.Count);

                for (int j = 0; j < leaves.Count; ++j)
                {
                    centers.Add(leaves[j].center);
                }

                // solve each sub tree
                var tsp = TSPLibProblem.FromPoints(centers.GetEnumerator(), BaseFileName + ".tri." + i);
                if (!tsp.OutputFileExists())
                {
                    pool.Add(tsp.GetTSPCommand());
                }

                subLeaves[i] = leaves;
                tsps[i]      = tsp;
            }
            pool.run();

            for (int i = 0; i < subTrees.Count; ++i)
            {
                // crosshatch sub trees
                var tsp = tsps[i];
                if (!tsp.setIndicesFromOutputFile())
                {
                    Debug.LogWarning("no tsp indices");
                    continue; //TODO: handle more gracefully
                }
                var leaves = subLeaves[i];

                TriCrosshatch <PixelTriData> tch = null, last = null;

                for (int j = 0; j < leaves.Count; ++j)
                {
                    tch      = new TriCrosshatch <PixelTriData>(this, leaves[tsp.indexAt(j)]);
                    tch.last = last;
                    tch.next = j == leaves.Count - 1 ? null : new TriCrosshatch <PixelTriData>(this, leaves[tsp.indexAt(j + 1)]);

                    tch.AddDrawPoints(pdPath, viewBoxToPaperScale, (float)machineConfig.toolDiameterMM);

                    last = tch;
                }
            }
        }