/// <summary> /// Render an ticked and annotated axis. /// </summary> /// <param name="from">From here.</param> /// <param name="to">To here.</param> /// <param name="tickres">Tick resolution; how often to annotate the axis.</param> /// <param name="name">Axis name, written on top the ticks.</param> /// <param name="vertical">This is a vertical axis, so move the anootations to the left.</param> private void renderAxisAnnotations(double[] from, double[] to, double tickres, string name, bool vertical) { double[] delta = to.Subtract(from); // if too small, don't annotate anything (otherwise it will just be a mess) double[] proj = camera.TransformH(delta).Submatrix(0, 1); if (proj.SquareEuclidean() < 1 * 1) { return; } double[] tickunit = tickres.Multiply(delta.Normalize()); double length = delta.Euclidean(); double limit = length / 2; // [-limit, limit] int nticks = (int)(limit / tickres); double[] zero = to.Add(from).Divide(2); double[] postick = zero.Add(tickunit); double[] negtick = zero.Subtract(tickunit); float a = SceneBuffer.Width / (MapClip[1] - MapClip[0]); float x = SceneBuffer.Width / 2; float y = SceneBuffer.Height / 2; float scale = 1.5f; float rotangle = (float)(Util.NormalizeAngle(-Math.Atan2(proj[1], proj[0]))); bool invertoffset = false; if (Math.Abs(rotangle) > Math.PI / 2 && !vertical) { rotangle += (float)Math.PI; invertoffset = true; } double[] nproj = proj.Normalize(); if (nproj[0] < 0) { nproj = (-1).Multiply(nproj); invertoffset ^= true; } Vector2 t; if (vertical) { t = new Vector2((float)(x + 450 * scale * nproj[0] - 350 * nproj[1]), (float)(y - 450 * scale * nproj[1] - 350 * nproj[0])); } else if (!invertoffset) { t = new Vector2((float)(x + ((name == "x [m]") ? 420 : 600) * scale * nproj[0] + 120 * nproj[1]), (float)(y - ((name == "x [m]") ? 420 : 600) * scale * nproj[1] + 120 * nproj[0])); } else { t = new Vector2((float)(x + 580 * scale * nproj[0] - 120 * nproj[1]), (float)(y - 580 * scale * nproj[1] - 120 * nproj[0])); } Flip.DrawString(fontaxis, name, t, Color.Black, rotangle, new Vector2(0, 0), scale, SpriteEffects.None, 0); for (int i = 1; i <= nticks; i++) { double[] ppos = camera.TransformH(postick); double[] npos = camera.TransformH(negtick); Vector2 p = new Vector2(a * (float)ppos[0] + x, a * (float)-ppos[1] + y); Vector2 n = new Vector2(a * (float)npos[0] + x, a * (float)-npos[1] + y); string ptext = (tickres * i).ToString("F0"); Vector2 psize = fontaxis.MeasureString(ptext) * scale; string ntext = (-tickres * i).ToString("F0"); Vector2 nsize = fontaxis.MeasureString(ntext) * scale; p.X = p.X - psize.X / 2; n.X = n.X - nsize.X / 2; if (vertical) { p.X = p.X - psize.X / 2 - 30 * scale; n.X = n.X - nsize.X / 2 - 30 * scale; } if (SceneBuffer.Bounds.Contains(new Rectangle((int)p.X, (int)p.Y, (int)(psize.X), (int)(psize.Y)))) { Flip.DrawString(fontaxis, ptext, p, Color.Black, 0, new Vector2(0, 0), scale, SpriteEffects.None, 0); } if (SceneBuffer.Bounds.Contains(new Rectangle((int)n.X, (int)n.Y, (int)(nsize.X), (int)(nsize.Y)))) { Flip.DrawString(fontaxis, ntext, n, Color.Black, 0, new Vector2(0, 0), scale, SpriteEffects.None, 0); } postick = postick.Add(tickunit); negtick = negtick.Subtract(tickunit); } }
/// <summary> /// This is called when the manipulator should draw itself. /// </summary> /// <param name="time">Provides a snapshot of timing values.</param> protected override void Draw(GameTime time) { // visualization Graphics.SetRenderTarget(SceneBuffer); Graphics.Clear(Color.White); Graphics.DepthStencilState = DepthStencilState.Default; foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); Navigator.Render(camera); } Graphics.DepthStencilState = DepthStencilState.None; foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); RenderHUD(camera); } Flip.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); double limit = Config.AxisLimit; renderAxisAnnotations(new double[3] { -limit, 0, 0 }, new double[3] { +limit, 0, 0 }, 2, "x [m]", false); renderAxisAnnotations(new double[3] { 0, -limit, 0 }, new double[3] { 0, +limit, 0 }, 2, "y [m]", true); renderAxisAnnotations(new double[3] { 0, 0, -limit }, new double[3] { 0, 0, +limit }, 2, "z [m]", false); Flip.End(); // sidebar Graphics.SetRenderTarget(SideBuffer); Graphics.Clear(Color.Black); Flip.Begin(SpriteSortMode.FrontToBack, BlendState.NonPremultiplied, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); Explorer.RenderSide(); Flip.End(); // HUD foreach (EffectPass pass in hudeffect.CurrentTechnique.Passes) { pass.Apply(); Explorer.RenderSideHUD(); } // text and axes Graphics.SetRenderTarget(null); Flip.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone); Flip.Draw(SceneBuffer, SceneDest, SceneBuffer.Bounds, Color.White); Flip.Draw(SideBuffer, SideDest, SideBuffer.Bounds, Color.White); Flip.DrawString(font, Message, messagepos, Color.White); Flip.DrawString(font, TagMessage, TagMessagePos, TagColor); //double limit = Config.AxisLimit; //renderAxisAnnotations(new double[3] {-limit, 0, 0}, new double[3] {+limit, 0, 0}, 5); //renderAxisAnnotations(new double[3] {0, -limit, 0}, new double[3] {0, +limit, 0}, 5); //renderAxisAnnotations(new double[3] {0, 0, -limit}, new double[3] {0, 0, +limit}, 5); Flip.End(); base.Draw(time); }