I have some sort of a problem. I'm new to XNA and want to draw a polygon shape that looks something like this (In the end, I want these point to be random):
So I read some articles and this is what I ended up with:
private VertexPositionColor[] vertices;
public TextureClass()
{
setupVertices();
}
public override void Render(SpriteBatch spriteBatch)
{
Texture2D texture = createTexture(spriteBatch);
spriteBatch.Draw(texture, new Rectangle((int)vertices[0].Position.X, (int)vertices[0].Position.Y, 30, 30), Color.Brown);
}
private Texture2D createTexture(SpriteBatch spriteBatch)
{
Texture2D texture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
texture.SetData<Color>(new Color[] { Color.Brown });
return texture;
}
When I call Render it's starts drawing some squares as if it where in a loop. I'm just guessing I'm doing it all wrong. I would love it if someones points me into the right direction. Just creating a polygon and drawing it. It seemed so simple...
Here it what I have right now.
A class that generates a BasicEffect with some desired asignments.
public class StandardBasicEffect : BasicEffect
{
public StandardBasicEffect(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
this.VertexColorEnabled = true;
this.Projection = Matrix.CreateOrthographicOffCenter(
0, graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height, 0, 0, 1);
}
public StandardBasicEffect(BasicEffect effect)
: base(effect) { }
public BasicEffect Clone()
{
return new StandardBasicEffect(this);
}
}
Here is my PolygonShape class
/// <summary>
/// A Polygon object that you will be able to draw.
/// Animations are being implemented as we speak.
/// </summary>
/// <param name="graphicsDevice">The graphicsdevice from a Game object</param>
/// <param name="vertices">The vertices in a clockwise order</param>
public PolygonShape(GraphicsDevice graphicsDevice, VertexPositionColor[] vertices)
{
this.graphicsDevice = graphicsDevice;
this.vertices = vertices;
this.triangulated = false;
triangulatedVertices = new VertexPositionColor[vertices.Length * 3];
indexes = new int[vertices.Length];
}
/// <summary>
/// Triangulate the set of VertexPositionColors so it will be drawn correcrly
/// </summary>
/// <returns>The triangulated vertices array</returns>}
public VertexPositionColor[] Triangulate()
{
calculateCenterPoint();{
setupIndexes();
for (int i = 0; i < indexes.Length; i++)
{
setupDrawableTriangle(indexes[i]);
}
triangulated = true;
return triangulatedVertices;
}
/// <summary>
/// Calculate the center point needed for triangulation.
/// The polygon will be irregular, so this isn't the actual center of the polygon
/// but it will do for now, as we only need an extra point to make the triangles with</summary>
private void calculateCenterPoint()
{
float xCount = 0, yCount = 0;
foreach (VertexPositionColor vertice in vertices)
{
xCount += vertice.Position.X;
yCount += vertice.Position.Y;
}
centerPoint = new Vector3(xCount / vertices.Length, yCount / vertices.Length, 0);
}
private void setupIndexes()
{
for (int i = 1; i < triangulatedVertices.Length; i = i + 3)
{
indexes[i / 3] = i - 1;
}
}
private void setupDrawableTriangle(int index)
{
triangulatedVertices[index] = vertices[index / 3]; //No DividedByZeroException?...
if (index / 3 != vertices.Length - 1)
triangulatedVertices[index + 1] = vertices[(index / 3) + 1];
else
triangulatedVertices[index + 1] = vertices[0];
triangulatedVertices[index + 2].Position = centerPoint;
}
/// <summary>
/// Draw the polygon. If you haven't called Triangulate yet, I wil do it for you.
/// </summary>
/// <param name="effect">The BasicEffect needed for drawing</param>
public void Draw(BasicEffect effect)
{
try
{
if (!triangulated)
Triangulate();
draw(effect);
}
catch (Exception exception)
{
throw exception;
}
}
private void draw(BasicEffect effect)
{
effect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
PrimitiveType.TriangleList, triangulatedVertices, 0, vertices.Length);
}
Sorry, it's kind of alot. Now for my next quest. Animation my polygon.
Hope it helped fellow people with the same problem.
this code is useful to draw 2D lines, some calcs can be done into an initilization call, but i prefer for this example to keep all together.
public void DrawLine(VertexPositionColor[] Vertices)
{
Game.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
Vector2 center;
center.X = Game.GraphicsDevice.Viewport.Width * 0.5f;
center.Y = Game.GraphicsDevice.Viewport.Height * 0.5f;
Matrix View = Matrix.CreateLookAt( new Vector3( center, 0 ), new Vector3( center, 1 ), new Vector3( 0, -1, 0 ) );
Matrix Projection = Matrix.CreateOrthographic( center.X * 2, center.Y * 2, -0.5f, 1 );
Effect EffectLines = Game.Content.Load<Effect>( "lines" );
EffectLines.CurrentTechnique = EffectLines.Techniques["Lines"];
EffectLines.Parameters["xViewProjection"].SetValue( View * Projection );
EffectLines.Parameters["xWorld"].SetValue( Matrix.Identity );
foreach ( EffectPass pass in EffectLines.CurrentTechnique.Passes )
{
pass.Apply( );
Game.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>
( PrimitiveType.LineList, Vertices, 0, Vertices.Length/2 );
}
}
LINES.FX
uniform float4x4 xWorld;
uniform float4x4 xViewProjection;
void VS_Basico(in float4 inPos : POSITION, in float4 inColor: COLOR0, out float4 outPos: POSITION, out float4 outColor:COLOR0 )
{
float4 tmp = mul (inPos, xWorld);
outPos = mul (tmp, xViewProjection);
outColor = inColor;
}
float4 PS_Basico(in float4 inColor:COLOR) :COLOR
{
return inColor;
}
technique Lines
{
pass Pass0
{
VertexShader = compile vs_2_0 VS_Basico();
PixelShader = compile ps_2_0 PS_Basico();
FILLMODE = SOLID;
CULLMODE = NONE;
}
}
I worked with XNA in the past on a physics simulation where I had to draw bounding boxes with GraphicsDevice.DrawIndexedPrimitives (You should google or MSDN for this function for more worked examples.)
The below code is what I used in my project for drawing a 3D geometry.
/// <summary>
/// Draw the primitive.
/// </summary>
/// <param name="world">World Matrix</param>
/// <param name="view">View Matrix</param>
/// <param name="projection">Projection Matrix</param>
/// <param name="color">Color of the primitive</param>
public void Draw(Matrix world, Matrix view, Matrix projection, Color color)
{
_mGraphicsDevice.VertexDeclaration = _mVertexDeclaration;
_mGraphicsDevice.Vertices[0].SetSource(_mVertexBuffer, 0, VertexPositionNormal.SizeInBytes);
_mGraphicsDevice.Indices = _mIndexBuffer;
_mBasicEffect.DiffuseColor = color.ToVector3();
_mBasicEffect.World = _mTransform * world;
_mBasicEffect.View = view;
_mBasicEffect.Projection = projection;
int primitiveCount = _mIndex.Count / 3;
_mBasicEffect.Begin();
foreach (EffectPass pass in _mBasicEffect.CurrentTechnique.Passes)
{
pass.Begin();
_mGraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _mVertex.Count, 0, primitiveCount);
pass.End();
}
_mBasicEffect.End();
}
This function is a member method of a geometry object (class) and is called from the Game class' Draw(GameTime) method
Related
Im using a gaussian blur asset that uses this script attached to the camera to make its shader blur what is behind the UI:
public class BlurRenderer : MonoBehaviour
{
[Range(0, 25)]
/// <summary>
/// how many Iterations should the blur be applied for
/// </summary>
public int Iterations;
[Range(0, 5)]
/// <summary>
/// Lowers the resolution of the texture, thus allowing for a larger blur without so many iterations
/// </summary>
public int DownRes;
/// <summary>
/// Weather to update the blur
/// </summary>
public bool UpdateBlur = true;
/// <summary>
/// amount of time that will pass before re-rendering the blur
/// </summary>
public float UpdateRate = 0.02f;
/// <summary>
/// last time we rendered the blur
/// </summary>
private float lastUpdate = 0.0f;
/// <summary>
/// Stores the blur texture between renders
/// </summary>
public RenderTexture BlurTexture;
/// <summary>
/// The material where we'll pass the screen texture though to create the blur
/// </summary>
private Material mat;
private void Start()
{
//set up the blur texture
BlurTexture = new RenderTexture(256, 256, 16, RenderTextureFormat.ARGB32);
/*
//we can swap the code above for a texture that will take up less ram...if needed.
BlurTexture = new RenderTexture(128, 128, 4, RenderTextureFormat.ARGB4444);
*/
//create the texture
BlurTexture.Create();
//create our material if we have not already
if (mat == null)
{
mat = new Material(Shader.Find("Hidden/GaussianBlur_Mobile"));
}
}
#region BlurScaleSlider
//this section will enable a slider that controls both the Iterations and DownRes
//Uncomment the code below to use it
/*
[Range(0, 100)]
/// <summary>
/// The blur scale.
/// </summary>
public int blurScale;
private void Update()
{
int tempBlurScale = blurScale;
Iterations = 0;
DownRes = 0;
while (tempBlurScale > 20 && DownRes < 5)
{
DownRes += 1;
tempBlurScale -= 15;
}
Iterations = tempBlurScale;
tempBlurScale = 0;
}
*/
#endregion
//OnRenderImage will execute before each frame renders
void OnRenderImage(RenderTexture src, RenderTexture dst)
{
// if we need to re-render
if (Time.time - lastUpdate >= UpdateRate
&& UpdateBlur)
{
//set the width and height
int width = src.width >> DownRes;
int height = src.height >> DownRes;
//create a temp texture
RenderTexture rt = RenderTexture.GetTemporary(width, height);
//move the screen image to our temp texture
Graphics.Blit(src, rt);
//loop to add the blur to our temp texture
for (int i = 0; i < Iterations; i++)
{
RenderTexture rt2 = RenderTexture.GetTemporary(width, height);
Graphics.Blit(rt, rt2, mat);
RenderTexture.ReleaseTemporary(rt);
rt = rt2;
}
//store our texture in BlurTexture
Graphics.Blit(rt, BlurTexture);
//set the global texture for our shader to use
Shader.SetGlobalTexture("_MobileBlur", rt);
//remove the temp texture
RenderTexture.ReleaseTemporary(rt);
//set lastUpdate
lastUpdate = Time.time;
}
else
{
//set the global texture again...must be done for each frame
Shader.SetGlobalTexture("_MobileBlur", BlurTexture);
}
//make sure the camera renders as normal.
Graphics.Blit(src, dst);
}
/// <summary>
/// Creates a BlurRenderer_Mobile on the main camera
/// </summary>
static public BlurRenderer Create()
{
BlurRenderer BRM = Camera.main.gameObject.GetComponent<BlurRenderer>();
if (BRM == null)
{
BRM = Camera.main.gameObject.AddComponent<BlurRenderer>();
}
return BRM;
}
//override to allow different camera
static public BlurRenderer Create(Camera ThisCamera)
{
BlurRenderer BRM = ThisCamera.gameObject.GetComponent<BlurRenderer>();
if (BRM == null)
{
BRM = ThisCamera.gameObject.AddComponent<BlurRenderer>();
}
return BRM;
}
}
}
It creates a render texture of what the camera renders in world space. This works with most game objects, but with my skybox background or fade Standard shader (with a transparent image in the slot), nothing renders.
With game object:
With just transparent image:
How can I fix this/what is causing this?
The shader:
Properties
{
[PerRendererData] _MainTex ("_MainTex", 2D) = "white" {}
_Lightness ("_Lightness", Range(0,2)) = 1
_Saturation ("_Saturation", Range(-10,10)) = 1
_TintColor ("_TintColor",Color) = (1.0,1.0,1.0,0.0)
// required for UI.Mask
[HideInInspector] _StencilComp ("Stencil Comparison", Float) = 8
[HideInInspector] _Stencil ("Stencil ID", Float) = 0
[HideInInspector] _StencilOp ("Stencil Operation", Float) = 0
[HideInInspector] _StencilWriteMask ("Stencil Write Mask", Float) = 255
[HideInInspector] _StencilReadMask ("Stencil Read Mask", Float) = 255
[HideInInspector] _ColorMask ("Color Mask", Float) = 15
}
SubShader
{
Tags
{
"Queue" = "Transparent"
"PreviewType" = "Plane"
"DisableBatching" = "True"
}
// required for UI.Mask
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
ColorMask [_ColorMask]
Pass
{
ZWrite Off
Cull Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
half4 color : COLOR;
half4 screenpos : TEXCOORD2;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float2 screenuv : TEXCOORD1;
half4 color : COLOR;
float2 screenpos : TEXCOORD2;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.screenuv = ((o.vertex.xy / o.vertex.w) + 1) * 0.5;
o.color = v.color;
o.screenpos = ComputeScreenPos(o.vertex);
return o;
}
float2 safemul(float4x4 M, float4 v)
{
float2 r;
r.x = dot(M._m00_m01_m02, v);
r.y = dot(M._m10_m11_m12, v);
return r;
}
sampler2D _MainTex;
float4 _MainTex_TexelSize;
// x contains 1.0/width
// y contains 1.0/height
// z contains width
// w contains height
uniform float _Lightness;
uniform float _Saturation;
uniform fixed4 _TintColor;
uniform sampler2D _MobileBlur;
float4 frag(v2f i) : SV_Target
{
float4 m = tex2D(_MainTex, i.uv);
float2 uvWH = float2(_MainTex_TexelSize.z / _ScreenParams.x,_MainTex_TexelSize.w / _ScreenParams.y);
uvWH.x *= _MainTex_TexelSize.x;
uvWH.y *= _MainTex_TexelSize.y;
float2 buv = float2(i.screenpos.x - (uvWH.x / 2),i.screenpos.y - (uvWH.y / 2));
float4 blurColor = float4(0,0,0,0);
blurColor = tex2D(_MobileBlur,buv);
//blurColor = tex2D(_MobileBlur,buv);
blurColor.a *= m.a;
float4 finalColor = blurColor * i.color;
finalColor.a = i.color.a * m.a * blurColor.a;
finalColor.rgb *= _Lightness;
finalColor.rgb *= _TintColor;
float3 intensity = dot(finalColor.rgb, float3(0.299,0.587,0.114));
finalColor.rgb = lerp(intensity, finalColor.rgb , _Saturation);
return finalColor;
}
ENDCG
}
Hello I am trying to make a paint program using XNA and I followed the guide found in here as much as possible: How to create Paint-like app with XNA?
And it worked all great so far however there is one issue: the drawn rectangles dont connect together to form a line. I am out of ideas and I would appreciate any help offered. Heres my code below. Please also take a look at the image attached to get a better understanding.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
namespace ProfAnas
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D canvas;
Vector2 brushPos;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1920; // set this value to the desired width of your window
graphics.PreferredBackBufferHeight = 1080; // set this value to the desired height of your window
graphics.ApplyChanges();
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
Color[] pixel = new Color[1920 * 1080];
for (int i = 0; i < pixel.Length; i++)
{
pixel[i] = Color.White;
}
pixel[1919] = Color.Red;
spriteBatch = new SpriteBatch(GraphicsDevice);
canvas = new Texture2D(this.GraphicsDevice, 1920, 1080);
canvas.SetData<Color>(pixel);
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
MouseState state = Mouse.GetState();
Color[] pixel = new Color[1920 * 1080];
canvas.GetData<Color>(pixel);
if (state.LeftButton == ButtonState.Pressed)
{
brushPos.X = state.X;
brushPos.Y = state.Y;
double piOn4 = Math.PI / 4;
int xComponent;
int yComponent;
int screenWidth = 1920;
int screenHeight = 1080;
int regionXHalfed = (int)Math.Ceiling((10.0 * Math.Cos(piOn4) + 30.0 * Math.Cos(piOn4))/2);
int regionYHalfed = (int)Math.Ceiling((10.0 * Math.Sin(piOn4) + 30.0 * Math.Sin(piOn4))/2);
double angle;
double centerToBoundary;
double pixelToCenter;
List<int> boundedPixel = new List<int>();
for (int row=(state.Y-regionYHalfed); row < (state.Y + regionYHalfed); row++)
{
for (int column= (state.X - regionXHalfed); column < (state.X + regionXHalfed); column++)
{
xComponent=column - state.X+1;
yComponent=row - state.Y+1;
if (xComponent == 0)
{
pixelToCenter = Math.Sqrt((double)xComponent*xComponent + (double)yComponent*yComponent);
if (Math.Abs(pixelToCenter) <= 5*Math.Sqrt(2))
{
boundedPixel.Add(((row) * screenWidth + column) + 1);
}
continue;
}
angle=Math.Atan( (double)yComponent / (double)xComponent);
if (angle>= (piOn4 - Math.Atan(1.0/3)) && angle<= (piOn4 + Math.Atan(1.0/3)))
{
centerToBoundary = 15 / Math.Cos(angle - piOn4);
pixelToCenter= xComponent / Math.Cos(angle);
if( Math.Abs(pixelToCenter) <= Math.Abs(centerToBoundary))
{
boundedPixel.Add(((row)* screenWidth + column)+1);
}
}
if (angle >= (piOn4 + Math.Atan(1.0 / 3)) && angle <= Math.PI/2)
{
centerToBoundary = 5 / Math.Cos(angle + piOn4);
pixelToCenter = xComponent / Math.Cos(angle);
if (Math.Abs(pixelToCenter) <= Math.Abs(centerToBoundary))
{
boundedPixel.Add(((row) * screenWidth + column) + 1);
}
}
if (angle >= 0.0 && angle <= (piOn4 - Math.Atan(1.0 / 3)))
{
centerToBoundary = 5 / Math.Cos(angle + piOn4);
pixelToCenter = xComponent / Math.Cos(angle);
if (Math.Abs(pixelToCenter) <= Math.Abs(centerToBoundary))
{
boundedPixel.Add(((row) * screenWidth + column) + 1);
}
}
if (angle >= -Math.PI / 2 && angle <= 0.0)
{
centerToBoundary = 5 / Math.Cos(angle + piOn4);
pixelToCenter = xComponent / Math.Cos(angle);
if (Math.Abs(pixelToCenter) <= Math.Abs(centerToBoundary))
{
boundedPixel.Add(((row) * screenWidth + column) + 1);
}
}
}
}
foreach (int i in boundedPixel)
{
if(i>=0)
pixel[i] = Color.Red;
}
}
canvas.SetData<Color>(pixel);
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
spriteBatch.Draw(canvas, new Vector2(0, 0));
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Thank you :)
As #adv12 suggested, the Update method of XNA games isn't as fast as the one of MSPaint's, and that's why you'll never have a line if drawing pixel-by-pixel (or in your case rectangle-by-rectangle) if you move your mouse fast across the canvas.
The possible solution is to draw new rectangles when you release left mouse button, between the rectangles that were created when left mouse button was pressed. That will give you twice -1 the rectangles you are currently drawing, and you will have a line, no matter how fast the Update is called.
I'm drawing cylinders using the primitives provided by XNA, and what I want to do is, when the mouse hovers on a cylinder, I detect it. I tried using bounding spheres but it didn't work, as it wasn't accurate. I don't know what to do. Any help?
Summary of what I want to do.
1- Create bounding Box for this Cylinder
2- Rotate this bounding Box.
CODE TO DRAW CYLINDER can be found here
http://xbox.create.msdn.com/en-US/education/catalog/sample/primitives_3d
#region File Description
//-----------------------------------------------------------------------------
// CylinderPrimitive.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion
namespace TheProteinBundle
{
/// <summary>
/// Geometric primitive class for drawing cylinders.
/// </summary>
public class CylinderPrimitive : GeometricPrimitive
{
/// <summary>
/// Constructs a new cylinder primitive, using default settings.
/// </summary>
public CylinderPrimitive(GraphicsDevice graphicsDevice)
: this(graphicsDevice, 1, 1, 32)
{
}
/// <summary>
/// Constructs a new cylinder primitive,
/// with the specified size and tessellation level.
/// </summary>
public CylinderPrimitive(GraphicsDevice graphicsDevice,
float height, float diameter, int tessellation)
{
if (tessellation < 3)
throw new ArgumentOutOfRangeException("tessellation");
height /= 2;
float radius = diameter / 2;
// Create a ring of triangles around the outside of the cylinder.
for (int i = 0; i < tessellation; i++)
{
Vector3 normal = GetCircleVector(i, tessellation);
AddVertex(normal * radius + Vector3.Up * height, normal);
AddVertex(normal * radius + Vector3.Down * height, normal);
AddIndex(i * 2);
AddIndex(i * 2 + 1);
AddIndex((i * 2 + 2) % (tessellation * 2));
AddIndex(i * 2 + 1);
AddIndex((i * 2 + 3) % (tessellation * 2));
AddIndex((i * 2 + 2) % (tessellation * 2));
}
// Create flat triangle fan caps to seal the top and bottom.
CreateCap(tessellation, height, radius, Vector3.Up);
CreateCap(tessellation, height, radius, Vector3.Down);
InitializePrimitive(graphicsDevice);
base.boundingSphere.Center = Vector3.Zero;
if (height > diameter)
base.boundingSphere.Radius = height;
else
base.boundingSphere.Radius = diameter;
}
/// <summary>
/// Helper method creates a triangle fan to close the ends of the cylinder.
/// </summary>
void CreateCap(int tessellation, float height, float radius, Vector3 normal)
{
// Create cap indices.
for (int i = 0; i < tessellation - 2; i++)
{
if (normal.Y > 0)
{
AddIndex(CurrentVertex);
AddIndex(CurrentVertex + (i + 1) % tessellation);
AddIndex(CurrentVertex + (i + 2) % tessellation);
}
else
{
AddIndex(CurrentVertex);
AddIndex(CurrentVertex + (i + 2) % tessellation);
AddIndex(CurrentVertex + (i + 1) % tessellation);
}
}
// Create cap vertices.
for (int i = 0; i < tessellation; i++)
{
Vector3 position = GetCircleVector(i, tessellation) * radius +
normal * height;
AddVertex(position, normal);
}
}
/// <summary>
/// Helper method computes a point on a circle.
/// </summary>
static Vector3 GetCircleVector(int i, int tessellation)
{
float angle = i * MathHelper.TwoPi / tessellation;
float dx = (float)Math.Cos(angle);
float dz = (float)Math.Sin(angle);
return new Vector3(dx, 0, dz);
}
}
}
Using this class
#region File Description
//-----------------------------------------------------------------------------
// GeometricPrimitive.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion
namespace TheProteinBundle
{
/// <summary>
/// Base class for simple geometric primitive models. This provides a vertex
/// buffer, an index buffer, plus methods for drawing the model. Classes for
/// specific types of primitive (CubePrimitive, SpherePrimitive, etc.) are
/// derived from this common base, and use the AddVertex and AddIndex methods
/// to specify their geometry.
/// </summary>
public abstract class GeometricPrimitive : IDisposable
{
#region Fields
// During the process of constructing a primitive model, vertex
// and index data is stored on the CPU in these managed lists.
List<VertexPositionNormal> vertices = new List<VertexPositionNormal>();
List<ushort> indices = new List<ushort>();
// Once all the geometry has been specified, the InitializePrimitive
// method copies the vertex and index data into these buffers, which
// store it on the GPU ready for efficient rendering.
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
BasicEffect basicEffect;
public BoundingSphere boundingsphere2;
public BoundingSphere boundingSphere;
public Matrix world;
#endregion
#region Initialization
/// <summary>
/// Adds a new vertex to the primitive model. This should only be called
/// during the initialization process, before InitializePrimitive.
/// </summary>
protected void AddVertex(Vector3 position, Vector3 normal)
{
vertices.Add(new VertexPositionNormal(position, normal));
}
/// <summary>
/// Adds a new index to the primitive model. This should only be called
/// during the initialization process, before InitializePrimitive.
/// </summary>
protected void AddIndex(int index)
{
if (index > ushort.MaxValue)
throw new ArgumentOutOfRangeException("index");
indices.Add((ushort)index);
}
/// <summary>
/// Queries the index of the current vertex. This starts at
/// zero, and increments every time AddVertex is called.
/// </summary>
protected int CurrentVertex
{
get { return vertices.Count; }
}
/// <summary>
/// Once all the geometry has been specified by calling AddVertex and AddIndex,
/// this method copies the vertex and index data into GPU format buffers, ready
/// for efficient rendering.
protected void InitializePrimitive(GraphicsDevice graphicsDevice)
{
// Create a vertex declaration, describing the format of our vertex data.
// Create a vertex buffer, and copy our vertex data into it.
vertexBuffer = new VertexBuffer(graphicsDevice,
typeof(VertexPositionNormal),
vertices.Count, BufferUsage.None);
vertexBuffer.SetData(vertices.ToArray());
// Create an index buffer, and copy our index data into it.
indexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort),
indices.Count, BufferUsage.None);
indexBuffer.SetData(indices.ToArray());
// Create a BasicEffect, which will be used to render the primitive.
basicEffect = new BasicEffect(graphicsDevice);
basicEffect.EnableDefaultLighting();
}
/// <summary>
/// Finalizer.
/// </summary>
~GeometricPrimitive()
{
Dispose(false);
}
/// <summary>
/// Frees resources used by this object.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Frees resources used by this object.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (vertexBuffer != null)
vertexBuffer.Dispose();
if (indexBuffer != null)
indexBuffer.Dispose();
if (basicEffect != null)
basicEffect.Dispose();
}
}
#endregion
public void TransformBoundingSphere(Matrix TransformToWorld)
{
boundingSphere.Transform(ref TransformToWorld, out boundingsphere2);
}
public bool CheckRayIntersection(Ray ray)
{
if (ray.Intersects(boundingsphere2).HasValue) return true;
return false;
}
#region Draw
/// <summary>
/// Draws the primitive model, using the specified effect. Unlike the other
/// Draw overload where you just specify the world/view/projection matrices
/// and color, this method does not set any renderstates, so you must make
/// sure all states are set to sensible values before you call it.
/// </summary>
public void Draw(Effect effect)
{
GraphicsDevice graphicsDevice = effect.GraphicsDevice;
// Set our vertex declaration, vertex buffer, and index buffer.
graphicsDevice.SetVertexBuffer(vertexBuffer);
graphicsDevice.Indices = indexBuffer;
foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
{
effectPass.Apply();
int primitiveCount = indices.Count / 3;
graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
vertices.Count, 0, primitiveCount);
}
}
/// <summary>
/// Draws the primitive model, using a BasicEffect shader with default
/// lighting. Unlike the other Draw overload where you specify a custom
/// effect, this method sets important renderstates to sensible values
/// for 3D model rendering, so you do not need to set these states before
/// you call it.
/// </summary>
public void Draw(Matrix world, Matrix view, Matrix projection, Color color)
{
// Set BasicEffect parameters.
basicEffect.World = world;
basicEffect.View = view;
basicEffect.Projection = projection;
basicEffect.DiffuseColor = color.ToVector3();
basicEffect.Alpha = color.A / 255.0f;
GraphicsDevice device = basicEffect.GraphicsDevice;
device.DepthStencilState = DepthStencilState.Default;
if (color.A < 255)
{
// Set renderstates for alpha blended rendering.
device.BlendState = BlendState.AlphaBlend;
}
else
{
// Set renderstates for opaque rendering.
device.BlendState = BlendState.Opaque;
}
// Draw the model, using BasicEffect.
Draw(basicEffect);
}
#endregion
}
}
I am trying to make a simple chart application and I have stumbled upon a problem. I have an array of points which represent a 45 degree line (y = x) of 100 points. I then try to draw the line by converting each point's value to its pixel value and then draw lines from one point to another using graphic.DrawLines().
The the basic code:
struct MyPoint
{
double X { set; get; }
double Y { set; get; }
}
List<MyPoint> Values;
List<System.Drawing.Point> Points;
for (int i = 0; i < Values.Count; i++)
{
// convert point value to pixel coordinate
int x = (int)((Values[i].X - MinimumX) * (double)Control.Width / (MaximumX - MinimumX) + 0.5);
int y = (int)((Values[i].Y - MinimumY) * (double)Control.Height / (MaximumY - MinimumY)) + 0.5;
Points.Add(new System.Drawing.Point(x, y));
}
graphic.DrawLines(pen, Points.ToArray());
Note: MaximumX and MaximumY are 100 (number of points) and minimums are 0.
The thing is that this kind of line is not very straight :) So my question is, how would I draw a straight line through those points, so that it would look the same as
graphic.DrawLine(pen, 0, 0, Control.Width-1, Control.Height-1);
Thanks in advance!
Since you are doing a graph, I have a basic framework for fitting values within a control like this example:
public struct MyPoint
{
public double X { set; get; }
public double Y { set; get; }
public PointF ToPoint()
{
return new PointF((float)X, (float)Y);
}
}
/// <summary>
/// For http://stackoverflow.com/questions/19459519/drawing-a-straight-line-from-points
/// </summary>
public partial class Form1 : Form
{
List<MyPoint> points;
public Form1()
{
InitializeComponent();
// Initialize points to draw
points=new List<MyPoint>(100);
for (int i=0; i<=100; i++)
{
double θ=2*Math.PI*(i/100.0);
double x=(1+θ/Math.PI)*Math.Cos(θ);
double y=(1+θ/Math.PI)*Math.Sin(θ);
points.Add(new MyPoint() { X=x, Y=y });
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
// smooth graphics
e.Graphics.SmoothingMode=SmoothingMode.AntiAlias;
// set margins inside the control client area in pixels
var margin=new System.Drawing.Printing.Margins(16, 16, 16, 16);
// set the domain of (x,y) values
var range=new RectangleF(-3, -3, 6, 6);
// scale graphics
ScaleGraphics(e.Graphics, pictureBox1, range, margin);
// convert points to pixels
PointF[] pixels=points.Select((v) => v.ToPoint()).ToArray();
// draw arrow axes
using (var pen=new Pen(Color.Black, 0))
{
pen.EndCap=System.Drawing.Drawing2D.LineCap.ArrowAnchor;
e.Graphics.DrawLine(pen, range.Left, 0.0f, range.Right, 0.0f);
e.Graphics.DrawLine(pen, 0.0f, range.Top, 0.0f, range.Bottom);
}
// draw bounding rectangle (on margin)
using (var pen=new Pen(Color.LightGray, 0))
{
pen.DashStyle=DashStyle.Dash;
e.Graphics.DrawRectangle(pen, Rectangle.Round(range));
}
// draw curve
using (var pen = new Pen(Color.Blue, 0))
{
//e.Graphics.DrawLines(pen, pixels);
e.Graphics.DrawCurve(pen, pixels);
}
}
/// <summary>
/// Scales the Graphics to fit a Control, given a domain of x,y values and side margins in pixels
/// </summary>
/// <param name="g">The Graphics object</param>
/// <param name="control">The Control</param>
/// <param name="domain">The value domain</param>
/// <param name="margin">The margin</param>
void ScaleGraphics(Graphics g, Control control, RectangleF domain, Margins margin)
{
// Find the drawable area in pixels (control-margins)
int W=control.Width-margin.Left-margin.Right;
int H=control.Height-margin.Bottom-margin.Top;
// Ensure drawable area is at least 1 pixel wide
W=Math.Max(1, W);
H=Math.Max(1, H);
// Find the origin (0,0) in pixels
float OX=margin.Left-W*(domain.Left/domain.Width);
float OY=margin.Top+H*(1+domain.Top/domain.Height);
// Find the scale to fit the control
float SX=W/domain.Width;
float SY=H/domain.Height;
// Transform the Graphics
g.TranslateTransform(OX, OY);
g.ScaleTransform(SX, -SY);
}
private void pictureBox1_SizeChanged(object sender, EventArgs e)
{
// redraw on resize
pictureBox1.Refresh();
}
}
I think the problem is because your points are Integers. Precisions are lost.
Trying using
float x = (float)((Values[i] ...
List<System.Drawing.PointF> PointFs;
instead of
int x = (int)((Values[i] ...
List<System.Drawing.Point> Points;
You probably need something like this:
private void DrawLine()
{
int xInitial = 0, yInitial = 0, xFinal = Control.Width - 1, yFinal = Control.Height - 1;
int dx = xFinal - xInitial, dy = yFinal - yInitial, steps, k, xf, yf;
float xIncrement, yIncrement, x = xInitial, y = yInitial;
if (Math.Abs(dx) > Math.Abs(dy))
steps = Math.Abs(dx);
else
steps = Math.Abs(dy);
xIncrement = dx / (float)steps;
yIncrement = dy / (float)steps;
for (k = 0; k < steps; k++)
{
x += xIncrement;
xf = (int)x;
y += yIncrement;
yf = (int)y;
Points.Add(new System.Drawing.Point(xf, yf));
}
graphic.DrawLines(pen, Points.ToArray());
}
For more details see:
Bresenham's line algorithm
If your points already calculated correctly by algorithm, than simple:
int x = (int) Values[i].X;
int y = (int) Values[i].Y;
Should be enough.
here is my solution:
System.Drawing.Pen myPen;
myPen = new System.Drawing.Pen(System.Drawing.Color.Red);
System.Drawing.Graphics formGraphics = this.CreateGraphics();
formGraphics.DrawLine(myPen, 0, 0, 200, 200);
myPen.Dispose();
formGraphics.Dispose();
I am trying to use the CSV below for drawing a plot:
2.364258,3.005366
2.723633,3.009784
3.083008,3.012145
3.442383,3.012705
3.801758,3.010412
4.160156,3.010703
4.518555,3.011985
4.876953,3.012547
5.235352,3.009941
5.592773,3.011252
5.951172,3.010596
6.30957,3.011951
6.667969,3.010613
7.026367,3.008634
7.384766,3.009744
7.743164,3.01062
8.101563,3.00942
8.459961,3.009438
8.818359,3.009478
9.177734,3.010827
What I did so far is that I tried to make a Class to do this! this is the part when I try to draw the curve:
class Plotter
{
#region Fields and variables
private Bitmap plot;
private Graphics g;
public string PlotType {get; set;}
private int iWidth; //Width of the box
private int iHeight; //
private float xMax; //maximum range on X axis
private float yMax; //maximum range on Y axis
private PointF[] points;
#endregion
#region Constructors
/// <summary>
/// Constructor of class
/// </summary>
/// <param name="iWidth">Width of image in pixels</param>
/// <param name="iHeight">Height of image in pixels</param>
/// <param name="xMax">Maximum value of the values on X</param>
/// <param name="yMax">Maximum value of the values on Y</param>
/// <param name="pairs">Pairs of data in an array of PointF[] this is raw data!!</param>
public Plotter(int iWidth, int iHeight, float xMax, float yMax, PointF[] points)
{
this.iWidth = iWidth;
this.iHeight = iHeight;
this.xMax = xMax;
this.yMax = yMax;
this.points = points;
plot = new Bitmap(iWidth, iHeight);
}
public Bitmap DrawPlot()
{
Pen blackPen = new Pen(Color.Black, 1);
g = Graphics.FromImage(plot);
PointF[] p = new PointF[points.GetLength(0)];
//Try to scale input data to pixel coordinates
foreach (PointF point in points)
{
int i = 0;
p[i].X = point.X * iWidth;
p[1].X = point.Y * iHeight;
}
g.DrawCurve(blackPen, p, 0);
return plot;
}
What I get at the end is jsut a stright line! that I think has been drawn on X{0,0} and Y{0,0} to X{0, 400} and Y{0,0}
Can you help me correct the mistakes please?
P.S: http://itools.subhashbose.com/grapher/index.php this site can draw the plot I need pretty good from the CSV data I have (if you need to check).
Thanks!
This seems to be your problem:
foreach (PointF point in points)
{
int i = 0;
p[i].X = point.X * iWidth;
p[1].X = point.Y * iHeight;
}
i is always zero and you are never assigning Y. The "second" assignment isn't even using i, but the 1 index.
Quick fix without error checking:
int i = 0;
foreach (PointF point in points)
{
p[i].X = point.X * iWidth;
p[i].Y = point.Y * iHeight;
i++;
}
Your assigning the x both times.
p[i].X = point.X * iWidth;
p[1].X = point.Y * iHeight;
And as #LarsTech points out you need to fix the counter