/// <summary>
        /// Applies the slopes to the sectors.
        ///
        /// We have:
        /// - theta
        /// - offset angle ("offset")
        /// - horizontal line length ("length")
        ///
        /// What we need to compute:
        /// - x coordinate where the line starts in the circle ("left", this is cos(theta + offset angle))
        /// - x coordinate where the line ends in the circle ("middle", this is cos(offset angle))
        ///
        /// With this data we can calculate some more required variables:
        /// - radius: length / (middle - left)
        /// - left delimiter: cos(offset + theta) * radius
        /// - right delimiter: cos(rotation) * radius (should be same as left delimiter + length)
        /// - section start, in map units: cos(offset + theta) * radius
        /// - base height offset (where the slope starts)
        ///
        /// Then we can simply use pythagoras to compute the y position for an x position on the length
        /// </summary>
        public void ApplySlope()
        {
            double left   = Math.Cos(theta + offsetangle);
            double middle = Math.Cos(offsetangle);

            double radius         = length / (middle - left);
            double leftdelimiter  = Math.Cos(offsetangle + theta);
            double rightdelimiter = Math.Cos(offsetangle);

            double sectionstart = Math.Cos(offsetangle + theta) * radius;

            baseheightoffset = Math.Sqrt(radius * radius - sectionstart * sectionstart) * scale;

            foreach (BaseVisualGeometrySector bvgs in sectors)
            {
                HashSet <Vertex> vertices = new HashSet <Vertex>(bvgs.Sector.Sides.Count * 2);
                double           u1       = 1.0;
                double           u2       = 0.0;

                foreach (Sidedef sd in bvgs.Sector.Sides.Keys)
                {
                    vertices.Add(sd.Line.Start);
                    vertices.Add(sd.Line.End);
                }

                // Get the two points that are the furthest apart on the line between the slope handles
                foreach (Vertex v in vertices)
                {
                    double intersection = handleline.GetNearestOnLine(v.Position);

                    if (intersection < u1)
                    {
                        u1 = intersection;
                    }
                    if (intersection > u2)
                    {
                        u2 = intersection;
                    }
                }

                // Compute the x position and the corrosponding height of the coordinates
                double xpos1   = sectionstart + (u1 * length);
                double xpos2   = sectionstart + (u2 * length);
                double height1 = Math.Sqrt(radius * radius - xpos1 * xpos1) * scale;
                double height2 = Math.Sqrt(radius * radius - xpos2 * xpos2) * scale;

                if (double.IsNaN(height1))
                {
                    height1 = 0.0;
                }

                if (double.IsNaN(height2))
                {
                    height2 = 0.0;
                }

                // Adjust the heights
                height1 = height1 - baseheightoffset + baseheight + heightoffset;
                height2 = height2 - baseheightoffset + baseheight + heightoffset;

                // Get the angle of the slope. We cheat a bit and substitute the y value of the vectors with the height of the points
                double slopeangle = Vector2D.GetAngle(new Vector2D(xpos1, height1), new Vector2D(xpos2, height2));

                // Always let the plane point up, VisualSidedefSlope.ApplySlope will invert it if necessary
                Plane plane = new Plane(new Vector3D(handleline.GetCoordinatesAt(u1), height1), handleline.GetAngle() + Angle2D.PIHALF, slopeangle, true);

                VisualSidedefSlope.ApplySlope(bvgs.Level, plane, mode);

                bvgs.Sector.UpdateSectorGeometry(true);
            }
        }
Пример #2
0
    // This clips a polygon and returns the triangles
    // The polygon may not have any holes or islands
    // See: http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
    private int DoEarClip(EarClipPolygon poly, List <Vector2D> verticeslist, List <Sidedef> sidedefslist)
    {
        LinkedList <EarClipVertex>     verts    = new LinkedList <EarClipVertex>();
        List <EarClipVertex>           convexes = new List <EarClipVertex>(poly.Count);
        LinkedList <EarClipVertex>     reflexes = new LinkedList <EarClipVertex>();
        LinkedList <EarClipVertex>     eartips  = new LinkedList <EarClipVertex>();
        LinkedListNode <EarClipVertex> n2;

        EarClipVertex[] t;
        int             countvertices = 0;

        // Go for all vertices to fill list
        foreach (EarClipVertex vec in poly)
        {
            vec.SetVertsLink(verts.AddLast(vec));
        }

        // Remove any zero-length lines, these will give problems
        LinkedListNode <EarClipVertex> n1 = verts.First;

        do
        {
            // Continue until adjacent zero-length lines are removed
            n2 = n1.Next ?? verts.First;
            Vector2D d = n1.Value.Position - n2.Value.Position;
            while ((Math.Abs(d.x) < 0.00001f) && (Math.Abs(d.y) < 0.00001f))
            {
                n2.Value.Remove();
                n2 = n1.Next ?? verts.First;
                if (n2 != null)
                {
                    d = n1.Value.Position - n2.Value.Position;
                }
                else
                {
                    break;
                }
            }

            // Next!
            n1 = n2;
        }while (n1 != verts.First);

        // Optimization: Vertices which have lines with the
        // same angle are useless. Remove them!
        n1 = verts.First;
        while (n1 != null)
        {
            // Get the next vertex
            n2 = n1.Next;

            // Get triangle for v
            t = GetTriangle(n1.Value);

            // Check if both lines have the same angle
            Line2D a = new Line2D(t[0].Position, t[1].Position);
            Line2D b = new Line2D(t[1].Position, t[2].Position);
            if (Math.Abs(Angle2D.Difference(a.GetAngle(), b.GetAngle())) < 0.00001f)
            {
                // Same angles, remove vertex
                n1.Value.Remove();
            }

            // Next!
            n1 = n2;
        }

        // Go for all vertices to determine reflex or convex
        foreach (EarClipVertex vv in verts)
        {
            // Add to reflex or convex list
            if (IsReflex(GetTriangle(vv)))
            {
                vv.AddReflex(reflexes);
            }
            else
            {
                convexes.Add(vv);
            }
        }

        // Go for all convex vertices to see if they are ear tips
        foreach (EarClipVertex cv in convexes)
        {
            // Add when this is a valid ear
            t = GetTriangle(cv);
            if (CheckValidEar(t, reflexes))
            {
                cv.AddEarTip(eartips);
            }
        }

/*#if DEBUG
 *      if (OnShowPolygon != null) OnShowPolygon(verts);
 #endif*/

        // Process ears until done
        while ((eartips.Count > 0) && (verts.Count > 2))
        {
            // Get next ear
            EarClipVertex v = eartips.First.Value;
            t = GetTriangle(v);

            // Only save this triangle when it has an area
            if (TriangleHasArea(t))
            {
                // Add ear as triangle
                AddTriangleToList(t, verticeslist, sidedefslist, (verts.Count == 3));
                countvertices += 3;
            }

            // Remove this ear from all lists
            v.Remove();
            EarClipVertex v1 = t[0];
            EarClipVertex v2 = t[2];

/*#if DEBUG
 *          if (TriangleHasArea(t))
 *          {
 *              if (OnShowEarClip != null) OnShowEarClip(t, verts);
 *          }
 #endif*/

            // Test first neighbour
            EarClipVertex[] t1 = GetTriangle(v1);
            //bool t1a = true;	//TriangleHasArea(t1);
            if (/*t1a && */ IsReflex(t1))
            {
                // List as reflex if not listed yet
                if (!v1.IsReflex)
                {
                    v1.AddReflex(reflexes);
                }
                v1.RemoveEarTip();
            }
            else
            {
                // Remove from reflexes
                v1.RemoveReflex();
            }

            // Test second neighbour
            EarClipVertex[] t2 = GetTriangle(v2);
            //bool t2a = true;	//TriangleHasArea(t2);
            if (/*t2a && */ IsReflex(t2))
            {
                // List as reflex if not listed yet
                if (!v2.IsReflex)
                {
                    v2.AddReflex(reflexes);
                }
                v2.RemoveEarTip();
            }
            else
            {
                // Remove from reflexes
                v2.RemoveReflex();
            }

            // Check if any neightbour have become a valid or invalid ear
            if (!v1.IsReflex && (/*!t1a || */ CheckValidEar(t1, reflexes)))
            {
                v1.AddEarTip(eartips);
            }
            else
            {
                v1.RemoveEarTip();
            }
            if (!v2.IsReflex && (/*!t2a || */ CheckValidEar(t2, reflexes)))
            {
                v2.AddEarTip(eartips);
            }
            else
            {
                v2.RemoveEarTip();
            }
        }

/*#if DEBUG
 *      if (OnShowRemaining != null) OnShowRemaining(verts);
 #endif*/

        // Dispose remaining vertices
        foreach (EarClipVertex ecv in verts)
        {
            ecv.Dispose();
        }

        // Return the number of vertices in the result
        return(countvertices);
    }
Пример #3
0
 public float GetAngle()
 {
     return(l2d.GetAngle());
 }
		// This generates the vertices to split the line with, from start to end
		private List<Vector2D> GenerateCurve(Line2D line, int vertices, float angle, bool backwards, float distance, bool fixedcurve)
		{

			// Make list
			List<Vector2D> points = new List<Vector2D>(vertices);

			//Added by Anders Åstrand 2008-05-18
			//The formulas used are taken from http://mathworld.wolfram.com/CircularSegment.html
			//c and theta are known (length of line and angle parameter). d, R and h are
			//calculated from those two
			//If the curve is not supposed to be a circular segment it's simply deformed to fit
			//the value set for distance.

			//The vertices are generated to be evenly distributed (by angle) along the curve
			//and lastly they are rotated and moved to fit with the original line

			//calculate some identities of a circle segment (refer to the graph in the url above)
			double c = line.GetLength();
			double theta = angle;

			double d = (c / Math.Tan(theta / 2)) / 2;
			double R = d / Math.Cos(theta / 2);
			double h = R - d;

			double yDeform = fixedcurve ? 1 : distance / h;
			if (backwards)
				yDeform = -yDeform;

			double a, x, y;
			Vector2D vertex;

			for (int v = 1; v <= vertices; v++)
			{
				//calculate the angle for this vertex
				//the curve starts at PI/2 - theta/2 and is segmented into vertices+1 segments
				//this assumes the line is horisontal and on y = 0, the point is rotated and moved later

				a = (Math.PI - theta) / 2 + v * (theta / (vertices + 1));

				//calculate the coordinates of the point, and distort the y coordinate
				//using the deform factor calculated above
				x = Math.Cos(a) * R;
				y = (Math.Sin(a) * R - d) * yDeform;

				//rotate and transform to fit original line
				vertex = new Vector2D((float)x, (float)y).GetRotated(line.GetAngle() + Angle2D.PIHALF);
				vertex = vertex.GetTransformed(line.GetCoordinatesAt(0.5f).x, line.GetCoordinatesAt(0.5f).y, 1, 1);

				points.Add(vertex);
			}


			// Done
			return points;
		}