//There are some river configurations that cause problems here,
        //particularly rivers without endpoints (circular river networks)
        //or pretty much any time a cell is completely surrounded by rivers.
        //It's currently thought that this configuration won't arise often
        //enough to warrant a solution. Circular rivers and many "island"
        //cells are probably a map generation bug, anyways.
        public void RefreshRivers()
        {
            rivers.Clear();

            SectionCanon.RefreshRiverSections();

            unassignedSections = new HashSet <RiverSection>(SectionCanon.Sections);

            while (unassignedSections.Count > 0)
            {
                RiverSection unassignedEndpoint = unassignedSections.FirstOrDefault(
                    section => section.HasPreviousEndpoint || section.HasNextEndpoint
                    );

                unassignedSections.Remove(unassignedEndpoint);

                if (unassignedEndpoint == null)
                {
                    break;
                }

                var newRiver = RiverBuilder.BuildRiverFromSection(unassignedEndpoint, unassignedSections);

                rivers.Add(newRiver.AsReadOnly());
            }
        }
        public void SetCurveStatusOfSection(RiverSection section, List <RiverSection> river)
        {
            IHexCell center    = section.AdjacentCellOne;
            IHexCell left      = Grid.GetNeighbor(section.AdjacentCellOne, section.DirectionFromOne.Previous());
            IHexCell nextRight = Grid.GetNeighbor(section.AdjacentCellOne, section.DirectionFromOne.Next());

            var centerLeftSection      = SectionCanon.GetSectionBetweenCells(center, left);
            var centerNextRightSection = SectionCanon.GetSectionBetweenCells(center, nextRight);

            section.PreviousOnInternalCurve = centerLeftSection != null && river.Contains(centerLeftSection);
            section.NextOnInternalCurve     = centerNextRightSection != null && river.Contains(centerNextRightSection);
        }
Пример #3
0
        //The fact that we're building sections only from the NE, E, and SE
        //cell edges is very important. A lot of the code here is structured
        //as it is to deal with this reality.
        public void RefreshRiverSections()
        {
            sections.Clear();
            SectionBetweenCells.Clear();

            CellEdgeContourCanon.Clear();

            foreach (var cell in Grid.Cells)
            {
                for (HexDirection direction = HexDirection.NE; direction <= HexDirection.SE; direction++)
                {
                    if (RiverCanon.HasRiverAlongEdge(cell, direction))
                    {
                        var neighbor = Grid.GetNeighbor(cell, direction);

                        Vector3 start = cell.AbsolutePosition + RenderConfig.GetFirstCorner(direction);
                        Vector3 end   = cell.AbsolutePosition + RenderConfig.GetSecondCorner(direction);

                        RiverFlow flow = RiverCanon.GetFlowOfRiverAtEdge(cell, direction);

                        //Our control points need to operate differently if we're at endpoints,
                        //and both ControlOne and ControlTwo might have alternate behavior.
                        //We need to check both ends of the section for endpoints
                        bool previousCellRiver     = RiverCanon.HasRiverAlongEdge(cell, direction.Previous());
                        bool previousNeighborRiver = RiverCanon.HasRiverAlongEdge(neighbor, direction.Previous2());

                        bool hasPreviousEndpoint =
                            !previousCellRiver &&
                            !previousNeighborRiver;

                        bool hasNextEndpoint =
                            !RiverCanon.HasRiverAlongEdge(cell, direction.Next()) &&
                            !RiverCanon.HasRiverAlongEdge(neighbor, direction.Next2());

                        var newRiverSection = new RiverSection()
                        {
                            AdjacentCellOne  = cell,
                            AdjacentCellTwo  = neighbor,
                            DirectionFromOne = direction,
                            Start            = start,
                            End                 = end,
                            FlowFromOne         = flow,
                            HasPreviousEndpoint = hasPreviousEndpoint,
                            HasNextEndpoint     = hasNextEndpoint
                        };

                        SectionBetweenCells[new Tuple <IHexCell, IHexCell>(cell, neighbor)] = newRiverSection;

                        sections.Add(newRiverSection);
                    }
                }
            }
        }
Пример #4
0
        public bool IsNeighborOf(RiverSection thisSection, RiverSection otherSection)
        {
            if (otherSection == null)
            {
                return(false);
            }

            IHexCell thisCenter    = thisSection.AdjacentCellOne;
            IHexCell thisLeft      = Grid.GetNeighbor(thisSection.AdjacentCellOne, thisSection.DirectionFromOne.Previous());
            IHexCell thisRight     = thisSection.AdjacentCellTwo;
            IHexCell thisNextRight = Grid.GetNeighbor(thisSection.AdjacentCellOne, thisSection.DirectionFromOne.Next());

            return((thisLeft != null && GetSectionBetweenCells(thisCenter, thisLeft) == otherSection) ||
                   (thisLeft != null && GetSectionBetweenCells(thisLeft, thisRight) == otherSection) ||
                   (thisNextRight != null && GetSectionBetweenCells(thisCenter, thisNextRight) == otherSection) ||
                   (thisNextRight != null && GetSectionBetweenCells(thisRight, thisNextRight) == otherSection));
        }
        public void RationalizeRiverContoursInCorner(IHexCell center, HexDirection direction)
        {
            IHexCell left = Grid.GetNeighbor(center, direction.Previous());

            //Contours will only fall short or overlap when we're at a river confluence,
            //so we can save ourselves time by checking for that
            if (left != null &&
                RiverCanon.HasRiverAlongEdge(center, direction.Previous()) &&
                RiverCanon.HasRiverAlongEdge(center, direction) &&
                RiverCanon.HasRiverAlongEdge(left, direction.Next())
                )
            {
                IHexCell right = Grid.GetNeighbor(center, direction);

                RiverSection centerLeftSection  = RiverSectionCanon.GetSectionBetweenCells(center, left);
                RiverSection centerRightSection = RiverSectionCanon.GetSectionBetweenCells(center, right);

                bool shouldCullContours = true;

                //Rationalizing contours also doesn't need to occur between segments
                //that are adjacent segments of the same river, which would put us on
                //the inside of a river curve. So we make some checks to exclude that
                //possibility, as well
                foreach (var river in RiverAssemblyCanon.Rivers)
                {
                    int leftIndex  = river.IndexOf(centerLeftSection);
                    int rightIndex = river.IndexOf(centerRightSection);

                    bool areAdjacentInRiver = ((leftIndex + 1) == rightIndex) || ((rightIndex + 1) == leftIndex);

                    if (leftIndex != -1 && rightIndex != -1 && areAdjacentInRiver)
                    {
                        shouldCullContours = false;
                        break;
                    }
                }

                if (shouldCullContours)
                {
                    ContourRationalizer.RationalizeCellContours(center, direction);
                }
            }
        }
Пример #6
0
        public bool AreSectionFlowsCongruous(RiverSection thisSection, RiverSection otherSection)
        {
            if (thisSection.DirectionFromOne == HexDirection.NE)
            {
                switch (otherSection.DirectionFromOne)
                {
                case HexDirection.NE: return(thisSection.FlowFromOne != otherSection.FlowFromOne);

                case HexDirection.E:  return(thisSection.FlowFromOne == otherSection.FlowFromOne);

                case HexDirection.SE: return(thisSection.FlowFromOne != otherSection.FlowFromOne);
                }
            }
            else if (thisSection.DirectionFromOne == HexDirection.E)
            {
                switch (otherSection.DirectionFromOne)
                {
                case HexDirection.NE: return(thisSection.FlowFromOne == otherSection.FlowFromOne);

                case HexDirection.E:  return(false);

                case HexDirection.SE: return(thisSection.FlowFromOne == otherSection.FlowFromOne);
                }
            }
            else if (thisSection.DirectionFromOne == HexDirection.SE)
            {
                switch (otherSection.DirectionFromOne)
                {
                case HexDirection.NE: return(thisSection.FlowFromOne != otherSection.FlowFromOne);

                case HexDirection.E:  return(thisSection.FlowFromOne == otherSection.FlowFromOne);

                case HexDirection.SE: return(thisSection.FlowFromOne != otherSection.FlowFromOne);
                }
            }

            return(false);
        }
        public RiverSection GetNextActiveSectionForRiver(
            RiverSection activeSection, RiverSection lastSection, HashSet <RiverSection> unassignedSections
            )
        {
            IHexCell center    = activeSection.AdjacentCellOne;
            IHexCell left      = Grid.GetNeighbor(activeSection.AdjacentCellOne, activeSection.DirectionFromOne.Previous());
            IHexCell right     = activeSection.AdjacentCellTwo;
            IHexCell nextRight = Grid.GetNeighbor(activeSection.AdjacentCellOne, activeSection.DirectionFromOne.Next());

            RiverSection centerLeftSection = SectionCanon.GetSectionBetweenCells(center, left);

            //For a section to be a valid next-step, it must be non-null and unassigned.
            //But we also don't want to grab a river adjacent to the previous river,
            //since this could cause weird behavior (go up a river, and then immediately
            //back down it). We thus forbid the acquisition of such rivers.
            //We also need to take flow into account, since it's not valid for a river
            //to suddenly flip its direction
            if (centerLeftSection != null &&
                unassignedSections.Contains(centerLeftSection) &&
                !SectionCanon.IsNeighborOf(centerLeftSection, lastSection) &&
                SectionCanon.AreSectionFlowsCongruous(activeSection, centerLeftSection)
                )
            {
                return(centerLeftSection);
            }

            RiverSection leftRightSection = SectionCanon.GetSectionBetweenCells(left, right);

            if (leftRightSection != null &&
                unassignedSections.Contains(leftRightSection) &&
                !SectionCanon.IsNeighborOf(leftRightSection, lastSection) &&
                SectionCanon.AreSectionFlowsCongruous(activeSection, leftRightSection)
                )
            {
                return(leftRightSection);
            }

            RiverSection centerNextRightSection = SectionCanon.GetSectionBetweenCells(center, nextRight);

            if (centerNextRightSection != null &&
                unassignedSections.Contains(centerNextRightSection) &&
                !SectionCanon.IsNeighborOf(centerNextRightSection, lastSection) &&
                SectionCanon.AreSectionFlowsCongruous(activeSection, centerNextRightSection)
                )
            {
                return(centerNextRightSection);
            }

            RiverSection rightNextRightSection = SectionCanon.GetSectionBetweenCells(right, nextRight);

            if (rightNextRightSection != null &&
                unassignedSections.Contains(rightNextRightSection) &&
                !SectionCanon.IsNeighborOf(rightNextRightSection, lastSection) &&
                SectionCanon.AreSectionFlowsCongruous(activeSection, rightNextRightSection)
                )
            {
                return(rightNextRightSection);
            }

            return(null);
        }
Пример #8
0
        private void AssembleRiverSplines()
        {
            RiverAssemblyCanon.RefreshRivers();

            foreach (var river in RiverAssemblyCanon.Rivers)
            {
                RiverSection section = river[0];

                var centerSpline = new BezierSpline(section.FlowFromOne == RiverFlow.Clockwise ? section.Start : section.End);

                Vector3 centerToV1Direction, centerToV4Direction;

                Vector3 v1Tangent, v4Tangent;

                Vector3 controlOne, controlTwo;

                Vector3 v1, v4;

                bool isV1Internal, isV4Internal;

                bool isControlOneNegative, isControlTwoNegative;

                for (int i = 0; i < river.Count; i++)
                {
                    section = river[i];

                    if (section.FlowFromOne == RiverFlow.Clockwise)
                    {
                        v1 = section.Start;
                        v4 = section.End;

                        isV1Internal = section.PreviousOnInternalCurve;
                        isV4Internal = section.NextOnInternalCurve;
                    }
                    else
                    {
                        v1 = section.End;
                        v4 = section.Start;

                        isV1Internal = section.NextOnInternalCurve;
                        isV4Internal = section.PreviousOnInternalCurve;
                    }

                    if (isV1Internal)
                    {
                        centerToV1Direction = (v1 - section.AdjacentCellOne.AbsolutePosition).normalized;
                    }
                    else
                    {
                        centerToV1Direction = (v1 - section.AdjacentCellTwo.AbsolutePosition).normalized;
                    }

                    if (isV4Internal)
                    {
                        centerToV4Direction = (v4 - section.AdjacentCellOne.AbsolutePosition).normalized;
                    }
                    else
                    {
                        centerToV4Direction = (v4 - section.AdjacentCellTwo.AbsolutePosition).normalized;
                    }

                    isControlOneNegative = (section.FlowFromOne == RiverFlow.Clockwise && isV1Internal) ||
                                           (section.FlowFromOne == RiverFlow.Counterclockwise && !isV1Internal);

                    isControlTwoNegative = (section.FlowFromOne == RiverFlow.Clockwise && !isV4Internal) ||
                                           (section.FlowFromOne == RiverFlow.Counterclockwise && isV4Internal);

                    v1Tangent = new Vector3(-centerToV1Direction.z, centerToV1Direction.y, centerToV1Direction.x);
                    v4Tangent = new Vector3(-centerToV4Direction.z, centerToV4Direction.y, centerToV4Direction.x);

                    controlOne = v1 + v1Tangent * RenderConfig.RiverCurveStrength * (isControlOneNegative ? -1f : 1f);
                    controlTwo = v4 + v4Tangent * RenderConfig.RiverCurveStrength * (isControlTwoNegative ? -1f : 1f);

                    centerSpline.AddCubicCurve(controlOne, controlTwo, v4);
                }

                var newRiverSpline = new RiverSpline()
                {
                    CenterSpline = centerSpline,
                    WesternCells = river.Select(riverSection => riverSection.AdjacentCellOne).ToList(),
                    EasternCells = river.Select(riverSection => riverSection.AdjacentCellTwo).ToList(),
                    Directions   = river.Select(riverSection => riverSection.DirectionFromOne).ToList(),
                    Flows        = river.Select(riverSection => riverSection.FlowFromOne).ToList()
                };

                RiverSplines.Add(newRiverSpline);
            }
        }
Пример #9
0
        public List <RiverSection> BuildRiverFromSection(RiverSection startingSection, HashSet <RiverSection> unassignedRiverSections)
        {
            if (startingSection == null)
            {
                throw new ArgumentNullException("startingSection");
            }

            var newRiver = new List <RiverSection>();

            if (startingSection.HasPreviousEndpoint && startingSection.HasNextEndpoint)
            {
                newRiver.Add(startingSection);

                unassignedRiverSections.Remove(startingSection);
            }
            else
            {
                RiverSection lastSection   = null;
                RiverSection activeSection = startingSection;

                do
                {
                    newRiver.Add(activeSection);

                    unassignedRiverSections.Remove(activeSection);

                    RiverSection oldActiveSection = activeSection;

                    activeSection = RiverBuilderUtilities.GetNextActiveSectionForRiver(activeSection, lastSection, unassignedRiverSections);

                    lastSection = oldActiveSection;
                }while(activeSection != null && !activeSection.HasPreviousEndpoint && !activeSection.HasNextEndpoint);

                if (activeSection != null)
                {
                    newRiver.Add(activeSection);
                    unassignedRiverSections.Remove(activeSection);
                }

                //Our construction guarantees that we grabbed at least one
                //valid endpoint to start with. But we don't know if we grabbed
                //the upriver endpoint or the downriver one first. If we grabbed
                //the downriver one, we need to reverse the whole river.
                if ((startingSection.HasPreviousEndpoint && startingSection.FlowFromOne == RiverFlow.Counterclockwise) ||
                    (startingSection.HasNextEndpoint && startingSection.FlowFromOne == RiverFlow.Clockwise)
                    )
                {
                    newRiver.Reverse();
                }
            }

            //Control point calculations vary based on whether the section is
            //on the inside or the outside of a curve. For straight rivers
            //(which contain a single segment) and endpoints this construction
            //causes an S shape to form, which is considered a reasonable result
            foreach (var section in newRiver)
            {
                RiverBuilderUtilities.SetCurveStatusOfSection(section, newRiver);
            }

            return(newRiver);
        }