Exemple #1
0
        /*
         * public override void initializeBays(string pName)
         * {
         *      if (parametricObject.isInitialized)
         *              return;
         *
         *      parametricObject.isInitialized = true;
         *
         *      RadialRepeaterTool gener = repeaterToolU as RadialRepeaterTool;
         *
         *      switch(pName)
         *      {
         *      case "Node Shape":
         *              if (repeaterToolU != null)
         *                      gener.radius = 3.5f * nodeSrc_p.parametricObject.bounds.size.x ;
         *              break;
         *      case "Cell Shape":
         *              if (repeaterToolU != null)
         *                      gener.radius = 2.5f * cellSrc_p.parametricObject.bounds.size.x ;
         *              break;
         *      }
         * }
         */



        // SHAPE_REPEATER_2D :: GENERATE
        public override GameObject generate(bool makeGameObjects, AXParametricObject initiator_po, bool isReplica)
        {
            if (parametricObject == null || !parametricObject.isActive)
            {
                return(null);
            }

            if ((P_Corner == null || cornerSrc_p == null) && (P_Node == null || inputSrc_p == null) && (P_Cell == null || cellSrc_p == null))
            {
                if (P_Output != null)
                {
                    P_Output.paths    = null;
                    P_Output.polyTree = null;
                }
                return(null);
            }


            // PRE_GENERATE
            preGenerate();


            planSrc_p  = P_Plan.DependsOn;                         // getUpstreamSourceParameter(P_Plan);
            planSrc_po = (planSrc_p != null)                                                           ? planSrc_p.parametricObject    : null;


            P_Plan.polyTree = null;
            AXShape.thickenAndOffset(ref P_Plan, planSrc_p);
            if (P_Plan.reverse)
            {
                P_Plan.doReverse();
            }


            planPaths = P_Plan.getPaths();

            if (planPaths == null || planPaths.Count == 0)
            {
                return(null);
            }



            // ** CREATE PLAN_SPLINES **

            if (planPaths != null && planPaths.Count > 0)
            {
                planSplines = new Spline[planPaths.Count];


                if (planSplines != null)
                {
                    for (int i = 0; i < planSplines.Length; i++)
                    {
                        planSplines[i] = new Spline(planPaths[i], (P_Plan.shapeState == ShapeState.Closed) ? true : false);
                    }
                }
            }



            AXParameter P_cornerOutput = new AXParameter();

            P_cornerOutput.parametricObject = parametricObject;

            AXParameter P_nodeOutput = new AXParameter();

            P_nodeOutput.parametricObject = parametricObject;


            AXParameter P_cellOutput = new AXParameter();

            P_cellOutput.parametricObject = parametricObject;



            // PROCESS SHAPE INPUTS

            if (cornerSrc_p != null)
            {
                P_Corner.polyTree = null;
                AXShape.thickenAndOffset(ref P_Corner, cornerSrc_p);
            }

            if (inputSrc_p != null)
            {
                P_Node.polyTree = null;
                AXShape.thickenAndOffset(ref P_Node, inputSrc_p);
            }


            if (cellSrc_p != null)
            {
                P_Cell.polyTree = null;
                AXShape.thickenAndOffset(ref P_Cell, cellSrc_p);
            }



            bool doPolyTreeNodes = false;
            //bool doPolyTreeCells = false;


            // NODE
            //Matrix4x4 tm = Matrix4x4.identity;



            //  NODE SHAPES
            Paths nodeSourcePaths = null;

            //Clipper nodeClipper = null;

            if (P_Node != null)
            {
                if (P_Node.polyTree != null)
                {
                    nodeSourcePaths = AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_Node.polyTree));
                    doPolyTreeNodes = true;
                    //nodeClipper =  new Clipper();
                }
                else
                {
                    nodeSourcePaths    = P_Node.getClonePaths();
                    P_nodeOutput.paths = new Paths();
                }
            }


            // CELL SHAPES
            Paths cellSourcePaths = null;

            //Clipper cellClipper = null;

            if (P_Cell != null)
            {
                if (P_Cell.polyTree != null)
                {
                    cellSourcePaths = AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_Cell.polyTree));
                    doPolyTreeNodes = true;
                    //cellClipper =  new Clipper();
                }
                else
                {
                    cellSourcePaths      = P_Cell.getClonePaths();
                    P_cornerOutput.paths = new Paths();
                }
            }



            // BREAK CORNER SHAPES
            Paths   cornerSourcePaths = null;
            Clipper cornerClipper     = null;

            if (P_Corner != null)
            {
                if (P_Corner.polyTree != null)
                {
                    cornerSourcePaths = AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_Corner.polyTree));
                    doPolyTreeNodes   = true;
                    cornerClipper     = new Clipper();
                }
                else
                {
                    cornerSourcePaths    = P_Corner.getClonePaths();
                    P_cornerOutput.paths = new Paths();
                }
            }



            // FOR EACH PATH

            for (int path_i = 0; path_i < planPaths.Count; path_i++)
            {
                // **** PREPARE EACH SPLINE *** //

                Spline planSpline = planSplines[path_i];

                planSpline.breakAngleCorners = cornerBreakAngle;
                planSpline.shapeState        = P_Plan.shapeState;

                // Create subsplines between each break point
                planSpline.getSmoothSubsplineIndicies(0, maxSegmentLength);

//					Debug.Log("planSpline.subsplines.Count="+ planSpline.subsplines.Count );
//					foreach(SubsplineIndices si in planSpline.subsplines)
//						si.print();

                planSpline.groupNearBreakAngleVertices(inset * 2);

                // **** PREPARE EACH SPLINE *** //



                Matrix4x4 localPlacement_mx = Matrix4x4.identity;



                if (planSpline.insetSpanSplines != null && planSpline.insetSpanSplines.Count > 0)
                {
                    for (int si = 0; si < planSpline.insetSpanSplines.Count; si++)
                    {
                        planSpline.insetSpanSplines[si].setRepeaterTransforms(si, inset, bay);
                    }



                    // SPAN NODES - SHAPES ALONG SUBSPLINES
                    if (nodeSrc_p != null && nodeSrc_p.meshes != null)
                    {
                        int endCount = (P_Plan != null && P_Plan.shapeState == ShapeState.Open) ?  planSpline.insetSpanSplines.Count - 1 : planSpline.insetSpanSplines.Count;

                        for (int i = 0; i < endCount; i++)
                        {
                            //SubsplineIndices rsi = planSpline.subsplines[i];
                            Spline           spanSpline   = planSpline.insetSpanSplines[i];
                            List <Matrix4x4> nodeMatrices = spanSpline.repeaterNodeTransforms;


                            // on each of these nodes, place a nodePlug instance.
                            bool spanNodesAtBreakCorners = true;

                            int starter = spanNodesAtBreakCorners ? 0 : 1;
                            int ender   = (inset > 0 || spanNodesAtBreakCorners) ? nodeMatrices.Count : nodeMatrices.Count - 1;

                            //string this_address = "";


                            if (nodeMatrices != null)
                            {
                                for (int ii = starter; ii < ender; ii++)
                                {
                                    //this_address = "node_"+path_i+"_"+i+"_"+ii;

                                    //Debug.Log("this_address="+this_address);
                                    // LOCAL_PLACEMENT

                                    localPlacement_mx = localMatrixFromAddress(RepeaterItem.Node, path_i, i, ii);

                                    if (doPolyTreeNodes)
                                    {
                                        cornerClipper.AddPaths(AX.Generators.Generator2D.transformPaths(nodeSourcePaths, localPlacement_mx), PolyType.ptSubject, true);
                                    }
                                    else
                                    {
                                        Paths tmp = AX.Generators.Generator2D.transformPaths(nodeSourcePaths, localPlacement_mx);
                                        P_cornerOutput.paths.AddRange(tmp);
                                    }
                                }
                            }
                        }
                    }



                    // CELL NODES - SHAPES ALONG SUBSPLINES
                    if (cellSrc_p != null && cellSrc_p.meshes != null)
                    {
                        int endCount = (P_Plan != null && P_Plan.shapeState == ShapeState.Open) ?  planSpline.insetSpanSplines.Count - 1 : planSpline.insetSpanSplines.Count;

                        for (int i = 0; i < endCount; i++)
                        {
                            //SubsplineIndices rsi = planSpline.subsplines[i];
                            Spline           spanSpline   = planSpline.insetSpanSplines[i];
                            List <Matrix4x4> cellMatrices = spanSpline.repeaterCellTransforms;


                            // on each of these nodes, place a nodePlug instance.
                            bool spanNodesAtBreakCorners = true;

                            int starter = spanNodesAtBreakCorners ? 0 : 1;
                            int ender   = (inset > 0 || spanNodesAtBreakCorners) ? cellMatrices.Count : cellMatrices.Count - 1;

                            //string this_address = "";


                            if (cellMatrices != null)
                            {
                                for (int ii = starter; ii < ender; ii++)
                                {
                                    //this_address = "cell_"+path_i+"_"+i+"_"+ii;

                                    //Debug.Log("this_address="+this_address);
                                    // LOCAL_PLACEMENT

                                    localPlacement_mx = localMatrixFromAddress(RepeaterItem.Cell, path_i, i, ii);

                                    if (doPolyTreeNodes)
                                    {
                                        cornerClipper.AddPaths(AX.Generators.Generator2D.transformPaths(cellSourcePaths, localPlacement_mx), PolyType.ptSubject, true);
                                    }
                                    else
                                    {
                                        Paths tmp = AX.Generators.Generator2D.transformPaths(nodeSourcePaths, localPlacement_mx);
                                        P_cornerOutput.paths.AddRange(tmp);
                                    }
                                }
                            }
                        }
                    }
                }



                if (cornerSourcePaths != null && cornerSourcePaths.Count > 0)
                {
                    // CORNERS

                    for (int bi = 0; bi < planSpline.breakIndices.Count; bi++)
                    {
                        //tm = Matrix4x4.TRS(new Vector3(2*i-2, 2*j-2, 0), Quaternion.identity, Vector3.one);
                        localPlacement_mx = localMatrixFromAddress(RepeaterItem.Corner, path_i, planSpline.breakIndices[bi]);


                        if (doPolyTreeNodes)
                        {
                            cornerClipper.AddPaths(AX.Generators.Generator2D.transformPaths(cornerSourcePaths, localPlacement_mx), PolyType.ptSubject, true);
                        }
                        else
                        {
                            Paths tmp = AX.Generators.Generator2D.transformPaths(cornerSourcePaths, localPlacement_mx);
                            P_cornerOutput.paths.AddRange(tmp);
                        }
                    }
                }



                if (doPolyTreeNodes)
                {
                    P_cornerOutput.polyTree = new AXClipperLib.PolyTree();
                    cornerClipper.Execute(ClipType.ctDifference, P_cornerOutput.polyTree, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
                }
            }



            P_Output.polyTree = null;

            if (cornerSrc_p != null || cellSrc_p == null || nodeSrc_p == null)
            {
                // JUST NODES
                AXShape.thickenAndOffset(ref P_Output, P_cornerOutput);
            }

            /*
             * else if (nodeSrc_p != null && (cellSrc_p == null || (P_cellOutput.paths == null && P_cellOutput.polyTree == null)))
             * {
             *      // JUST NODES
             *      AXShape.thickenAndOffset(ref P_Output, P_nodeOutput);
             * }
             *
             * else if (nodeSrc_p == null && cellSrc_p != null)
             * {
             *      // JUST CELLS
             *      AXShape.thickenAndOffset(ref P_Output, P_cellOutput);
             * }
             * else
             * {
             *      // BOTH TO COMBINE
             *      clipper =  new Clipper();
             *
             *      if (P_nodeOutput.polyTree == null)
             *              clipper.AddPaths(P_nodeOutput.paths, PolyType.ptSubject, true);
             *      else
             *              clipper.AddPaths(AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_nodeOutput.polyTree)), PolyType.ptSubject, true);
             *
             *      if (P_cellOutput.polyTree == null)
             *              clipper.AddPaths(P_cellOutput.paths, PolyType.ptSubject, true);
             *      else
             *              clipper.AddPaths(AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_cellOutput.polyTree)), PolyType.ptSubject, true);
             *
             *
             *      P_Output.polyTree = new AXClipperLib.PolyTree();
             *      clipper.Execute(ClipType.ctUnion,   P_Output.polyTree,  PolyFillType.pftNonZero, PolyFillType.pftNonZero);
             *
             *      AXShape.thickenAndOffset(ref P_Output, P_Output);
             * }
             */

            if (P_Output.polyTree != null)
            {
                transformPolyTree(P_Output.polyTree, localMatrix);
            }
            else if (P_nodeOutput.paths != null)
            {
                P_Output.paths = transformPaths(P_nodeOutput.paths, localMatrix);
                P_Output.transformedControlPaths = P_nodeOutput.paths;
            }
            //base.generate(false, initiator_po, isReplica);
            calculateBounds();

            return(null);
        }         // \generate
Exemple #2
0
        public static void thickenAndOffset(ref AXParameter sp, AXParameter src)
        {
            if (sp == null || src == null)
            {
                return;
            }
            //sp.polyTree = null;



            float thickness = sp.thickness;
            float roundness = sp.roundness;
            float offset    = sp.offset;
            bool  flipX     = sp.flipX;

            //Debug.Log(sp.parametricObject.Name + "." + sp.Name +"."+ sp.offset);

            //bool srcIsCC = src.isCCW();



            Paths subjPaths = src.getClonePaths();

            if (subjPaths == null)
            {
                return;
            }


            // FLIP_X
            if (flipX)
            {
                //Debug.Log(subjPaths.Count);
                //AXGeometryTools.Utilities.printPaths(subjPaths);

                subjPaths = AXGeometryTools.Utilities.transformPaths(subjPaths, Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1, 1, 1)));

                sp.transformedControlPaths = AXGeometryTools.Utilities.transformPaths(sp.transformedControlPaths, Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(-1, 1, 1)));
            }



            // SYMMETRY
            if (sp.symmetry)
            {
                if (subjPaths != null && subjPaths.Count > 0)
                {
                    for (int i = 0; i < subjPaths.Count; i++)
                    {
                        Path orig = subjPaths[i];

                        Path sym = new Path();

                        float seperation = sp.symSeperation * AXGeometryTools.Utilities.IntPointPrecision;
                        float apex       = .1f * seperation;


                        for (int j = 0; j < orig.Count; j++)
                        {
                            sym.Add(new IntPoint(orig[j].X + seperation / 2, orig[j].Y));
                        }

                        // midpoint. slightly raised
                        sym.Add(new IntPoint(0, orig[orig.Count - 1].Y + apex));

                        for (int j = orig.Count - 1; j >= 0; j--)
                        {
                            sym.Add(new IntPoint(-orig[j].X - seperation / 2, orig[j].Y));
                        }

                        subjPaths[i] = sym;
                    }
                }
            }



            AX.Generators.Generator2D gener2D = sp.parametricObject.generator as AX.Generators.Generator2D;


            if (subjPaths != null && gener2D != null && (gener2D.scaleX != 1 || gener2D.scaleY != 1))
            {
                sp.transformedButUnscaledOutputPaths = AX.Generators.Generator2D.transformPaths(subjPaths, gener2D.localUnscaledMatrix);
            }
            else
            {
                sp.transformedButUnscaledOutputPaths = null;
            }



            //cleanPolygonPrecision
            IntRect brect = Clipper.GetBounds(subjPaths);

            if ((brect.right - brect.left) < 10000)
            {
                cleanPolygonPrecision = 2;
            }
            else
            {
                cleanPolygonPrecision = 30;
            }

            //Debug.Log("cleanPolygonPrecision="+cleanPolygonPrecision);

            /*
             * if (offset_p.FloatVal == 0 && wallthick_p.FloatVal == 0)
             * {
             *      sp.polyTree = src.polyTree;
             *      sp.paths = src.paths;
             *      return;
             * }
             *
             */
            //sp.polyTree = null;



            //Debug.Log("new count a = " + subjPaths[0].Count);


            bool hasOffset = false;

            sp.hasThickness = false;


            Paths resPaths = new Paths();

            AXClipperLib.PolyTree resPolytree = null;

            ClipperOffset co = new ClipperOffset();


            //co.ArcTolerance = sp.arcTolerance;
            float smooth    = (float)(120 / (sp.arcTolerance * sp.arcTolerance));
            float smoothLOD = ((smooth - .048f) * sp.parametricObject.model.segmentReductionFactor) + .048f;

            co.ArcTolerance = (float)(Mathf.Sqrt(120 / smoothLOD));


            co.MiterLimit = 2.0f;


            //if (offset != 0)


            // 1. Offset? Can't offset an open shape
            if (sp.shapeState == ShapeState.Closed && (sp.endType == AXClipperLib.EndType.etClosedLine || sp.endType == AXClipperLib.EndType.etClosedPolygon))
            {
                //AXClipperLib.JoinType jt = (sp.endType == AXClipperLib.EndType.etClosedLine) ? JoinType.jtMiter : sp.joinType;//output.joinType;
                AXClipperLib.JoinType jt = (sp.parametricObject.model.segmentReductionFactor < .15f) ? AXClipperLib.JoinType.jtSquare  : sp.joinType;                //output.joinType;
                //Debug.Log ("sp.endType="+sp.endType+", jt="+jt);

                if (roundness != 0)
                {
                    // reduce
                    co.Clear();

                    if (subjPaths != null)
                    {
                        co.AddPaths(AXGeometryTools.Utilities.cleanPaths(subjPaths, cleanPolygonPrecision), jt, AXClipperLib.EndType.etClosedPolygon);                           //JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon);
                    }
                    co.Execute(ref subjPaths, (double)(-roundness * AXGeometryTools.Utilities.IntPointPrecision));
                }

                offset += roundness;

                if (subjPaths != null)
                {
                    subjPaths = Clipper.SimplifyPolygons(subjPaths, PolyFillType.pftNonZero);
                }

                co.Clear();

                hasOffset = true;

//				if (offset != 0 || thickness == 0) { // --! PUC ** Removed because the pass thru was causing a problem with Instance2D of a ShapeMerger
                // Do Offset

                // = true;

                if (subjPaths != null)
                {
                    // After changes made mid April to allow for FlipX, this started doubling the localMatrix and thus became redundent, though not sure why.
                    //if (gener2D != null)
                    //	sp.transformedAndScaledButNotOffsetdOutputPaths = AX.Generators.Generator2D.transformPaths(subjPaths, gener2D.localMatrix);

                    co.AddPaths(AXGeometryTools.Utilities.cleanPaths(subjPaths, cleanPolygonPrecision), jt, AXClipperLib.EndType.etClosedPolygon);                              //JoinType.jtSquare, AXClipperLib.EndType.etClosedPolygon);

                    // this resPolytree has transformed curves in it
                    resPolytree = new AXClipperLib.PolyTree();
                    co.Execute(ref resPolytree, (double)(offset * AXGeometryTools.Utilities.IntPointPrecision));
                }
//				}
                if (thickness > 0)
                {                       // No offset, but is closed
                    sp.transformedAndScaledButNotOffsetdOutputPaths = null;
                    if (src.polyTree != null)
                    {
                        if (thickness > 0)
                        {
                            resPaths = subjPaths;                             // Clipper.PolyTreeToPaths(src.polyTree);
                        }
                        else
                        {
                            resPolytree = src.polyTree;
                        }
                    }
                    else
                    {
                        resPaths = subjPaths;
                    }
                }
            }
            else
            {
                //resPolytree = src.polyTree;
                if (src.polyTree != null)
                {
                    if (thickness > 0)
                    {
                        resPaths = subjPaths;                         // Clipper.PolyTreeToPaths(src.polyTree);
                    }
                    else
                    {
                        resPolytree = src.polyTree;
                    }
                }
                else
                {
                    resPaths = subjPaths;
                }
            }


            // 2. Thickness?
            //subjPaths = sp.getPaths();


            if ((sp.endType != AXClipperLib.EndType.etClosedPolygon) && thickness > 0)              //input.endType != AXClipperLib.EndType.etClosedPolygon) {
            {
                // this is a wall
                if (resPaths != null && gener2D != null)
                {
                    sp.transformedFullyAndOffsetdButNotThickenedOutputPaths = AX.Generators.Generator2D.transformPaths(resPaths, gener2D.localMatrix);
                }

                sp.hasThickness = true;


                co.Clear();
                if (resPolytree != null)                                                                                                                     // closed block has happened
                {
                    co.AddPaths(AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(resPolytree), cleanPolygonPrecision), sp.joinType, sp.endType); //input.endType);
                }
                else if (resPaths != null)
                {
                    co.AddPaths(AXGeometryTools.Utilities.cleanPaths(resPaths, cleanPolygonPrecision), sp.joinType, sp.endType);                     //input.endType);
                }

                resPolytree = new AXClipperLib.PolyTree();
                co.Execute(ref resPolytree, (double)(thickness * AXGeometryTools.Utilities.IntPointPrecision));
            }
            else
            {
                sp.transformedFullyAndOffsetdButNotThickenedOutputPaths = null;
            }



            // 3. Update input data
            //Debug.Log(sp.parametricObject.Name  + "." + sp.Name + " here ["+hasOffset+"] " + (! sp.symmetry) + " " +  (! flipX)  + " " + (! hasOffset)  + " " +  (! hasThicken)  + " " +  (roundness == 0));


            // SIMPLE PASSTHRU?
            if (!sp.symmetry && !flipX && !hasOffset && !sp.hasThickness && roundness == 0)
            {
                // SIMPLE PASS THROUGH
                sp.polyTree = src.polyTree;
                sp.paths    = src.paths;
            }

            else
            {
                if (resPolytree == null)
                {
                    //sp.paths      = resPaths; //Generator2D.transformPaths(resPaths, gener2D.localMatrix);
                    //if (Clipper.Orientation(resPaths[0]) != srcIsCC)
                    //	AXGeometryTools.Utilities.reversePaths(ref resPaths);

                    sp.paths = AXGeometryTools.Utilities.cleanPaths(resPaths, cleanPolygonPrecision);
                }
                else
                {
                    //Generator2D.transformPolyTree(resPolytree, gener2D.localMatrix);

                    //if (resPolytree != null && resPolytree.Childs.Count > 0 &&  Clipper.Orientation(resPolytree.Childs[0].Contour) != srcIsCC)
                    //	AXGeometryTools.Utilities.reversePolyTree(resPolytree);


                    sp.polyTree = resPolytree;
                }
            }



            // REVERSE
            if (sp.reverse)
            {
                if (sp.polyTree != null)
                {
                    AXGeometryTools.Utilities.reversePolyTree(sp.polyTree);
                }
                else if (sp.paths != null && sp.paths.Count > 0)
                {
                    for (int i = 0; i < sp.paths.Count; i++)
                    {
                        sp.paths[i].Reverse();
                    }
                }
            }



//			if (sp.paths != null && sp.paths.Count > 0)
//			{
//				// SUBDIVISION
//				Debug.Log("sp.paths.Count="+sp.paths.Count);
//
//				for(int i=0; i<sp.paths.Count; i++)
//				{
//
//
//					Path path = sp.paths[i];
//					Path subdivPath = new Path();
//
//					for (int j=0; j<path.Count-1; j++)
//					{
//						subdivPath.Add(path[j]);
//						Vector2 v0 = new Vector2(path[j].X, path[j].Y);
//						Vector2 v1 = new Vector2(path[j+1].X, path[j+1].Y);
//
//						Debug.Log("["+i+"]["+j+"] " + Vector2.Distance(v0, v1)/10000);
//						 Vector2 newp = Vector2.Lerp(v0, v1, .5f);
//
//						subdivPath.Add(new IntPoint(newp.x, newp.y));
//					}
//					subdivPath.Add(path[path.Count-1]);
//
//
//					sp.paths[i] = subdivPath;
//
//					Debug.Log("------------");
//					AXGeometryTools.Utilities.printPath(sp.paths[i]);
//				}
//					// SUBDIVISION ---
//			}
//
        }
Exemple #3
0
        // SHAPE_REPEATER_2D :: GENERATE
        public override GameObject generate(bool makeGameObjects, AXParametricObject initiator_po, bool isReplica)
        {
            if ((P_Node == null || inputSrc_p == null) && (P_Cell == null || cellSrc_p == null))
            {
                if (P_Output != null)
                {
                    P_Output.paths    = null;
                    P_Output.polyTree = null;
                }
                return(null);
            }

            if (repeaterToolU == null || repeaterToolV == null)
            {
                return(null);
            }

            // PRE_GENERATE
            preGenerate();



            AXParameter P_nodeOutput = new AXParameter();

            P_nodeOutput.parametricObject = parametricObject;


            AXParameter P_cellOutput = new AXParameter();

            P_cellOutput.parametricObject = parametricObject;



            // PROCESS NODE INPUT

            if (inputSrc_p != null)
            {
                P_Node.polyTree = null;
                AXShape.thickenAndOffset(ref P_Node, inputSrc_p);

                //P_nodeOutput.polyTree = null;
                //AXShape.thickenAndOffset(ref P_nodeOutput, P_Node);
            }

            if (cellSrc_p != null)
            {
                P_Cell.polyTree = null;
                AXShape.thickenAndOffset(ref P_Cell, cellSrc_p);

                //P_cellOutput.polyTree = null;
                //AXShape.thickenAndOffset(ref P_cellOutput, P_Cell);
            }



            bool doPolyTreeNodes = false;
            bool doPolyTreeCells = false;


            // NODE
            Matrix4x4 tm = Matrix4x4.identity;

            Paths tmpPaths = null;

            Clipper clipper = null;


            if (nodeSrc_p != null)
            {
                if (P_Node.polyTree != null)
                {
                    tmpPaths        = AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_Node.polyTree));
                    doPolyTreeNodes = true;
                    clipper         = new Clipper();
                }
                else
                {
                    tmpPaths           = P_Node.getClonePaths();
                    P_nodeOutput.paths = new Paths();
                }

                //Debug.Log("tmpPaths="+tmpPaths.Count);
                if (tmpPaths != null && tmpPaths.Count > 0)
                {
                    for (int i = 0; i <= repeaterToolU.cells; i++)
                    {
                        for (int j = 0; j <= repeaterToolV.cells; j++)
                        {
                            if ((i <= repeaterToolU.edgeCount || i >= repeaterToolU.cells - repeaterToolU.edgeCount) || (j <= repeaterToolV.edgeCount || j >= repeaterToolV.cells - repeaterToolV.edgeCount))
                            {
                                //tm = Matrix4x4.TRS(new Vector3(2*i-2, 2*j-2, 0), Quaternion.identity, Vector3.one);
                                tm = localMatrixFromAddress(RepeaterItem.Node, i, j);


                                if (doPolyTreeNodes)
                                {
                                    clipper.AddPaths(AX.Generators.Generator2D.transformPaths(tmpPaths, tm), PolyType.ptSubject, true);
                                }
                                else
                                {
                                    Paths tmp = AX.Generators.Generator2D.transformPaths(tmpPaths, tm);


                                    P_nodeOutput.paths.AddRange(tmp);
                                }
                            }
                        }
                    }

                    if (doPolyTreeNodes)
                    {
                        P_nodeOutput.polyTree = new AXClipperLib.PolyTree();
                        clipper.Execute(ClipType.ctDifference, P_nodeOutput.polyTree, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
                    }
                }
            }



            // CELL
            // PROCESS CELL INPUT

            if (cellSrc_p != null)
            {
                if (P_Cell.polyTree != null)
                {
                    tmpPaths        = AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_Cell.polyTree));
                    doPolyTreeCells = true;
                    clipper         = new Clipper();
                }
                else
                {
                    tmpPaths           = P_Cell.getClonePaths();
                    P_cellOutput.paths = new Paths();
                }


                if (tmpPaths != null && tmpPaths.Count > 0)
                {
                    for (int i = 0; i < repeaterToolU.cells; i++)
                    {
                        for (int j = 0; j < repeaterToolV.cells; j++)
                        {
                            tm = localMatrixFromAddress(RepeaterItem.Cell, i, j);

                            if (doPolyTreeCells)
                            {
                                clipper.AddPaths(AX.Generators.Generator2D.transformPaths(tmpPaths, tm), PolyType.ptSubject, true);
                            }
                            else
                            {
                                Paths tmp = AX.Generators.Generator2D.transformPaths(tmpPaths, tm);
                                P_cellOutput.paths.AddRange(tmp);
                            }
                        }
                    }

                    if (doPolyTreeCells)
                    {
                        P_cellOutput.polyTree = new AXClipperLib.PolyTree();
                        clipper.Execute(ClipType.ctDifference, P_cellOutput.polyTree, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
                    }
                }
            }

            /*
             * if ( P_Output.polyTree != null)
             * {
             *      transformPolyTree(P_Output.polyTree, localMatrix);
             * }
             * else if (P_Output.paths != null)
             * {
             *      P_Output.paths = transformPaths(P_Output.paths, localMatrix);
             *      P_Output.transformedControlPaths = P_Output.paths;
             * }
             */

            P_Output.polyTree = null;

            if (nodeSrc_p != null && cellSrc_p == null)
            {
                // JUST NODES
                AXShape.thickenAndOffset(ref P_Output, P_nodeOutput);
            }

            else if (nodeSrc_p == null && cellSrc_p != null)
            {
                // JUST CELLS
                AXShape.thickenAndOffset(ref P_Output, P_cellOutput);
            }
            else
            {
                // BOTH TO COMBINE
                clipper = new Clipper();

                if (P_nodeOutput.polyTree == null)
                {
                    clipper.AddPaths(P_nodeOutput.paths, PolyType.ptSubject, true);
                }
                else
                {
                    clipper.AddPaths(AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_nodeOutput.polyTree)), PolyType.ptSubject, true);
                }

                if (P_cellOutput.polyTree == null)
                {
                    clipper.AddPaths(P_cellOutput.paths, PolyType.ptSubject, true);
                }
                else
                {
                    clipper.AddPaths(AXGeometryTools.Utilities.cleanPaths(Clipper.PolyTreeToPaths(P_cellOutput.polyTree)), PolyType.ptSubject, true);
                }


                P_Output.polyTree = new AXClipperLib.PolyTree();
                clipper.Execute(ClipType.ctUnion, P_Output.polyTree, PolyFillType.pftNonZero, PolyFillType.pftNonZero);

                AXShape.thickenAndOffset(ref P_Output, P_Output);
            }


            if (P_Output.polyTree != null)
            {
                transformPolyTree(P_Output.polyTree, localMatrix);
            }
            else if (P_nodeOutput.paths != null)
            {
                P_Output.paths = transformPaths(P_nodeOutput.paths, localMatrix);
                P_Output.transformedControlPaths = P_nodeOutput.paths;
            }
            else if (P_cellOutput.paths != null)
            {
                P_Output.paths = transformPaths(P_cellOutput.paths, localMatrix);
                P_Output.transformedControlPaths = P_cellOutput.paths;
            }
            //base.generate(false, initiator_po, isReplica);
            calculateBounds();

            return(null);
        }         // \generate