// Computes the tangents of the control points of a Catmull Rom spline private void ComputeTangents(ref CatmullRomSplineData spline) { float f = 2.0f; int i; if (spline.tangents == null) spline.tangents = new List<Vector2D>(); else spline.tangents.Clear(); // Special case for the first control point // spline.tangents.Add(new Vector2D((spline.controlpoints[1] - spline.controlpoints[0]) / f)); spline.tangents.Add(new Vector2D(spline.controlpoints[1] - spline.controlpoints[0])); for (i=1; i < spline.controlpoints.Count - 1; i++) { spline.tangents.Add(new Vector2D((spline.controlpoints[i + 1] - spline.controlpoints[i - 1]) / f)); } // Special case for the last control point //spline.tangents.Add(new Vector2D((spline.controlpoints[i] - spline.controlpoints[i - 1]) / f)); spline.tangents.Add(new Vector2D(spline.controlpoints[i] - spline.controlpoints[i - 1])); }
private void InitializeCatmullRomSplines() { List<Linedef> sourceld = new List<Linedef>(General.Map.Map.GetSelectedLinedefs(true)); Line2D innerline, outerline; CatmullRomSplineData innerspline, outerspline; CatmullRomSplineGroup splinegroup; numcontrolpoints = stairsectorbuilderform.NumControlPoints; if (General.Map.Map.GetSelectedLinedefs(true).Count <= 0) { return; } if (catmullromsplinegroups == null) catmullromsplinegroups = new List<CatmullRomSplineGroup>(); else catmullromsplinegroups.Clear(); for (int l1 = 0; l1 < sourceld.Count - 1; l1++) { int l2 = l1 + 1; Vector2D s1, s2, e1, e2; s1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].End.Position : sourceld[l1].Start.Position; e1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].Start.Position : sourceld[l1].End.Position; s2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].End.Position : sourceld[l2].Start.Position; e2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].Start.Position : sourceld[l2].End.Position; //innerline = new Line2D(sourceld[l2].Start.Position, sourceld[l1].Start.Position); //outerline = new Line2D(sourceld[l1].End.Position, sourceld[l2].End.Position); innerline = new Line2D(s2, s1); outerline = new Line2D(e1, e2); innerspline = new CatmullRomSplineData(); innerspline.controlpoints = new List<Vector2D>(); innerspline.line = innerline; innerspline.controlpoints.Add(new Vector2D(innerline.v1)); for (int k = 1; k <= numcontrolpoints - 2; k++) innerspline.controlpoints.Add(new Vector2D(innerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k))); innerspline.controlpoints.Add(new Vector2D(innerline.v2)); ComputeTangents(ref innerspline); outerspline = new CatmullRomSplineData(); outerspline.controlpoints = new List<Vector2D>(); outerspline.line = outerline; outerspline.controlpoints.Add(new Vector2D(outerline.v1)); for (int k = 1; k <= numcontrolpoints - 2; k++) outerspline.controlpoints.Add(new Vector2D(outerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k))); outerspline.controlpoints.Add(new Vector2D(outerline.v2)); ComputeTangents(ref outerspline); splinegroup = new CatmullRomSplineGroup(); splinegroup.splines = new CatmullRomSplineData[2]; splinegroup.splines[INNER_SPLINE] = innerspline; splinegroup.splines[OUTER_SPLINE] = outerspline; splinegroup.sourcelinedef = sourceld[l1]; catmullromsplinegroups.Add(splinegroup); } }
// Redrawing display public override void OnRedrawDisplay() { if (!stairsectorbuilderform.FullyLoaded) return; base.OnRedrawDisplay(); renderer.RedrawSurface(); // Render lines if (renderer.StartPlotter(true)) { renderer.PlotLinedefSet(General.Map.Map.Linedefs); renderer.PlotVerticesSet(General.Map.Map.Vertices); renderer.Finish(); } // Render things if (renderer.StartThings(true)) { renderer.RenderThingSet(General.Map.Map.Things, 1.0f); renderer.Finish(); } // Render overlay if (renderer.StartOverlay(true)) { if (stairsectorbuilderform.NumberOfSectors > 0) { if (stairsectorbuilderform.Tabs.SelectedIndex == 2 && numcontrolpoints != stairsectorbuilderform.NumControlPoints) { ModifyControlpointNumber(stairsectorbuilderform.NumControlPoints); numcontrolpoints = stairsectorbuilderform.NumControlPoints; } // Create sector info based on the settings... UpdateVertexData(); // Render the control points if the Catmull Rom spline tab is selected if (stairsectorbuilderform.Tabs.SelectedIndex == 2) { foreach (List<Vector2D> lv in exactsplines) { for (int i = 0; i < lv.Count - 1; i++) { renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, new PixelColor(255, 128, 128, 128), true); } } foreach (CatmullRomSplineGroup crsg in catmullromsplinegroups) { CatmullRomSplineData[] crsd = new CatmullRomSplineData[2] { crsg.splines[INNER_SPLINE], crsg.splines[OUTER_SPLINE] }; foreach (CatmullRomSplineData c in crsd) { for (int i = 0; i < c.controlpoints.Count; i++) { RectangleF rect = new RectangleF(c.controlpoints[i].x - CONTROLPOINT_SIZE / 2, c.controlpoints[i].y - CONTROLPOINT_SIZE / 2, 10.0f, 10.0f); if (i == 0) renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true); else if (i == c.controlpoints.Count - 1) renderer.RenderRectangle(rect, 2, new PixelColor(128, 0, 255, 0), true); else renderer.RenderRectangle(rect, 2, General.Colors.Indication, true); //renderer.RenderLine(c.controlpoints[i], c.controlpoints[i] + c.tangents[i], LINE_THICKNESS, new PixelColor(255, 128, 128, 128), true); } } } } // ... and draw the preview foreach (StairInfo si in stairsectors) { foreach (List<Vector2D> lv in si.sectors) { FlatVertex[] fvl = new FlatVertex[lv.Count]; for (int i = 0; i < lv.Count - 1; i++) { FlatVertex fv = new FlatVertex(); renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, General.Colors.Highlight, true); fvl[lv.Count - 1 - i].x = fv.u = lv[i].x; fvl[lv.Count - 1 - i].y = fv.v = lv[i].y; fvl[lv.Count - 1 - i].z = 1; fvl[lv.Count - 1 - i].c = 1056964608; RectangleF rect = new RectangleF(lv[i].x - CONTROLPOINT_SIZE / 2, lv[i].y - CONTROLPOINT_SIZE / 2, 10.0f, 10.0f); renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true); } fvl[0] = fvl[lv.Count - 1]; // renderer.RenderGeometry(fvl, null, true); } } } renderer.Finish(); } renderer.Present(); }
// Generats the Catmull Rom spline from the given control points and their tangents. // Returns a list of Vector2D. // The line between two control points is a "section". There is always one section // less than there are control points. // A "hop" is the distance between two to-be-created vertices. The distance is not // in map units, but a relative indication where on the line between two control // points the vertex will be. // First it is calculated in which section the vertex will be in, then the relative // position on the line between the control points of this section is calculated. // This value will be from 0.0 to 0.99999... // A workaround has to be done for the last vertex, which will inevitably be exactly // on the last control point, thus computing the wrong section and relative position private List<Vector2D> GenerateCatmullRom(CatmullRomSplineData crsd, int numverts) { List<Vector2D> vertices = new List<Vector2D>(); int sections = crsd.controlpoints.Count - 1; double hop = (double)sections / numverts; float distance = 0.0f; float unithop = 0; List<float> cpdistance = new List<float>(); // Compute the length of the whole spline and the length of the parts on the // spline between the control points for (int i = 0; i < crsd.controlpoints.Count-1; i++) { int h = (int)Vector2D.Distance(crsd.controlpoints[i], crsd.controlpoints[i + 1]); float dist = 0; List<Vector2D> dc = new List<Vector2D>(); Vector2D p0 = crsd.controlpoints[i]; Vector2D p1 = crsd.controlpoints[i + 1]; Vector2D t0 = crsd.tangents[i]; Vector2D t1 = crsd.tangents[i + 1]; for (int k = 0; k <= h; k++) { dc.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)(((float)k/(float)h)))); } for (int k = 0; k < h; k++) { dist += Vector2D.Distance(dc[k], dc[k + 1]); } distance += dist; cpdistance.Add(dist); exactsplines.Add(dc); } unithop = distance / numverts; for (int i = 0; i <= numverts; i++) { int s = 0; float u; float distancefromstart = i * unithop; float max = 0.0f; // Find out what section the vertex is in while (max < distancefromstart) { max += cpdistance[s]; if (max < distancefromstart) s++; // Work around for rounding errors if (s > cpdistance.Count-1) { s = cpdistance.Count - 1; max = distancefromstart; } } // Compute the u component with special cases for the first // and last vertex if (distancefromstart == 0) u = 0.0f; else if (distancefromstart == distance) u = 1.0f; else u = 1.0f - ((max - distancefromstart) / cpdistance[s]); Vector2D p0 = crsd.controlpoints[s]; Vector2D p1 = crsd.controlpoints[s + 1]; Vector2D t0 = crsd.tangents[s]; Vector2D t1 = crsd.tangents[s + 1]; vertices.Add(Tools.HermiteSpline(p0, t0, p1, t1, u)); } // OLD CODE //for (int x = 0; x <= numverts; x++) //{ // // Compute the section the vertex will be in // int s = (int)(x * hop); // double u; // // Workaround for the last vertex // if ((x * hop) - s < 0.00001f && s == sections) // { // u = 1.0f; // s--; // } // else // { // //u = (summe - x * unithop) / Vector2D.Distance(crsd.controlpoints[cs], crsd.controlpoints[cs+1]); // u = (x * hop) - s; // } // if (u > 1.0f) u = 1.0f; // Vector2D p0 = crsd.controlpoints[s]; // Vector2D p1 = crsd.controlpoints[s + 1]; // Vector2D t0 = crsd.tangents[s]; // Vector2D t1 = crsd.tangents[s + 1]; // vertices.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)u)); //} return vertices; }