I want to draw Arc in Vieport3D using MeshGeometry3D.I have searched for it a lot but not found anything ,what I found is using PathGeometry.I am new to WPF so don't know much about 3D graphics.How can I do this?
thanks
here is the code which I wrote to create mesh-geometry for Arc.I hope this will help someone.
private GeometryModel3D GetModel(double radius, Vector3D normal, Point3D center, int resolution, double StartAngle, double EndAngle)
{
var mod = new GeometryModel3D();
var geo = new MeshGeometry3D();
// Generate the circle in the XZ-plane
// Add the center first
geo.Positions.Add(new Point3D(0, 0, 0));
// Iterate from angle 0 to 2*PI
double dev = (2 * Math.PI) / resolution;
double thik = 0.02;
//float spaceangle = StartAngle + 1;
if (StartAngle != EndAngle)
{
for (double i = StartAngle; i < EndAngle; i += dev)
{
geo.Positions.Add(new Point3D(radius * Math.Cos(i), 0, -radius * Math.Sin(i)));
geo.Positions.Add(new Point3D((radius-thik) * Math.Cos(i), 0, (-(radius-thik)) * Math.Sin(i)));
}
for (int i = 3; i < geo.Positions.Count; i += 1)
{
geo.TriangleIndices.Add(i - 3);
geo.TriangleIndices.Add(i - 1);
geo.TriangleIndices.Add(i - 2);
geo.TriangleIndices.Add(i - 1);
geo.TriangleIndices.Add(i);
geo.TriangleIndices.Add(i - 2);
}
}
mod.Geometry = geo;
// Create transforms
var trn = new Transform3DGroup();
// Up Vector (normal for XZ-plane)
var up = new Vector3D(0, 1, 0);
// Set normal length to 1
normal.Normalize();
var axis = Vector3D.CrossProduct(up, normal); // Cross product is rotation axis
var angle = Vector3D.AngleBetween(up, normal); // Angle to rotate
trn.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(axis, angle)));
trn.Children.Add(new TranslateTransform3D(new Vector3D(center.X, center.Y, center.Z)));
mod.Transform = trn;
return mod;
}
Related
I am currently a bit stuck! Lets say, have a grid of shapes (nested For-Loop) and I want to use a wave to animate it. The wave should have an offset. So far, i can achieve it. Currently the offset affects the Y-axis … But how can I manage to have a RADIAL offset – you know – like the clock hand, or a radar line… I really would like the offset to start from (width/2, height/2) – and then walks around clockwise. Here is my code and the point where I am stuck:
void setup() {
size(600, 600);
}
void draw () {
background(255);
float tiles = 60;
float tileSize = width/tiles;
for (int x = 0; x < tiles; x++) {
for (int y = 0; y < tiles; y++) {
float waveOffset = map(y, 0, 60, 0, 300);
float sin = sin(radians(frameCount + waveOffset));
float wave = map(sin, -1, 1, 0, tileSize);
fill(0);
noStroke();
pushMatrix();
translate(tileSize/2, tileSize/2);
ellipse(x*tileSize, y*tileSize, wave, wave);
popMatrix();
}
}
}
I tried different things – like the rotate(); function etc. but I can't manage to combine it!
Thank you for any kind of help!
Right now, you're defining the size of the ellipses based on a transformation of sin(y). A transformation means it looks like a * sin(b * y + c) + d, and in this case you have
a = tileSize / 2
b = 300 / 60 = 5
c = frameCount
d = tileSize / 2
If you want to do a different pattern, you need to use a transformation of sin(theta) where theta is the "angle" of the dot (I put "angle" in quotes because it's really the angle from the vector from the center to the dot and some reference vector).
I suggest using the atan2() function.
Solution:
float waveOffset = 2*(atan2(y - tiles/2, x - tiles/2));
float sin = sin((frameCount/20.0 + waveOffset));
void setup() {
size(600, 600);
}
void draw () {
background(255);
float tiles = 60;
float tileSize = width/tiles;
for (int x = 0; x < tiles; x++) {
for (int y = 0; y < tiles; y++) {
float waveOffset = atan2(y - tiles/2, x - tiles/2)*0.5;
float sin = sin((frameCount*0.05 + waveOffset));
float wave = map(sin, -1, 1, 0, tileSize);
fill(0);
noStroke();
pushMatrix();
translate(tileSize/2, tileSize/2);
ellipse(x*tileSize, y*tileSize, wave, wave);
popMatrix();
}
}
}
I am working on a countdown project that involves an arc. I've been struggling with the math for a few hours now and hope someone can help me. I have a 150px circle on which I want to draw arc that runs over starting at the top. The circle (ellipse really) is at 160,4
<Ellipse Height="150" HorizontalAlignment="Left" Margin="160,4,0,0"
Name="minutesClock" Stroke="Gainsboro" StrokeThickness="20"
VerticalAlignment="Top" Width="150" />
I have a method that is supposed to calculate the arc from the center top of the circle to whatever number of seconds is left
private void drawArc(int timevalue, int maxvalue)
{
Ellipse element = (Ellipse)LayoutRoot.FindName("minutesClock");
double top = element.Margin.Top;
double left = element.Margin.Left;
double width = element.Width;
double height = element.Height;
double strokeWidth = element.StrokeThickness;
double startX = left + (width / 2);
double startY = top + (strokeWidth / 2);
double radius = (width / 2) - (strokeWidth / 2);
double percent = (double)timevalue / (double)maxvalue;
Point center = new Point();
center.X = top + (height / 2);
center.Y = left + (width / 2);
Point newEnd = calculatePoint(radius, percent, center);
Path arc = (Path)LayoutRoot.FindName(what + "Arc");
PathFigure path = new PathFigure();
path.StartPoint = new Point(startX, startY);
ArcSegment arcSegment = new ArcSegment();
arcSegment.IsLargeArc = false;
Size arcSize = new Size(radius,radius);
arcSegment.Size = arcSize;
arcSegment.SweepDirection = SweepDirection.Clockwise;
arcSegment.Point = newEnd;
arcSegment.RotationAngle = 45;
path.Segments.Add(arcSegment);
PathGeometry pg = new PathGeometry();
pg.Figures = new PathFigureCollection();
pg.Figures.Add(path);
arc.Data = pg;
}
The arc starts at the right place but doesn't end up in the right place (the end point is all over the place). My calculatePoint code has to be where the error is. I figure it has something to do with
private Point calculatePoint(double radius, double percent, Point center)
{
double degrees = 90 - (360 * percent);
double radians = (Math.PI * degrees) / 180;
Point endPoint = new Point();
endPoint.X = center.X + (radius * Math.Sin(radians));
endPoint.Y = center.Y + (radius * Math.Cos(radians));
return endPoint;
}
Where am I going wrong?
You need to subtract the sinus (to go from the center "upwards" on the UI canvas):
endPoint.X = center.X - (radius * Math.Sin(radians));
Origin 0,0 is Top Left corner, not bottom left.
[Edit]
Oh and you are confusding x and y: think x is horizontal coordinated and y is vertical, so this is wrong:
center.X = top + (height / 2);
center.Y = left + (width / 2);
and this is wrong:
endPoint.X = center.X + (radius * Math.Sin(radians));
endPoint.Y = center.Y + (radius * Math.Cos(radians));
Corrected:
center.Y = top + (height / 2);
center.X = left + (width / 2);
and (with the subtraction fix I mentioned)
endPoint.Y = center.Y - (radius * Math.Sin(radians));
endPoint.X = center.X + (radius * Math.Cos(radians));
I am creating a document based application and i want to draw a horizontal line underlying the text. But, line should not be straight. i want to draw a line like this.
Currently i am using System.Graphics object to draw any object.
private void DrawLine(Graphics g, Point Location, int iWidth)
{
iWidth = Convert.ToInt16(iWidth / 2);
iWidth = iWidth * 2;
Point[] pArray = new Point[Convert.ToInt16(iWidth / 2)];
int iNag = 2;
for (int i = 0; i < iWidth; i+=2)
{
pArray[(i / 2)] = new Point(Location.X + i , Location.Y + iNag);
if (iNag == 0)
iNag = 2;
else
iNag = 0;
}
g.DrawLines(Pens.Black, pArray);
}
UPDATED:
Above code is working fine and line draws perfectly but, this code effects on application performance. Is there another way to do this thing.
If you want fast drawing just make a png image of the line you want, with width larger than you need and then draw the image:
private void DrawLine(Graphics g, Point Location, int iWidth)
{
Rectangle srcRect = new Rectangle(0, 0, iWidth, zigzagLine.Height);
Rectangle dstRect = new Rectangle(Location.X, Location.Y, iWidth, zigzagLine.Height);
g.DrawImage(zigzagLine, dstRect, srcRect, GraphicsUnit.Pixel);
}
zigzagLine is the bitmap.
valter
I have this texture representing internal area of a pipe:
I have created a simple cylinder model (GeometryModel3D) by dissecting the above texture to a grid of points and than wrapping this to form a cylinder, then mapping the above texture to it (each square on a grid having two triangles):
The left and right edges of the texture meet at the above black seam line, which is unwanted.
I could not use single vertex point for smooth continuation there, because that point corresponds to both 0 and 360 degrees which is single angle, but two distinct points on edges of the 2D texture.
So I have used pairs of vertices on the same location but each corresponding to different point in the source texture.
Is there anything I can do to hide the seam line?
Model creation code:
private void SceneViewerWindowOnLoaded(object sender, RoutedEventArgs e)
{
var imageSource = new BitmapImage(new Uri("pack://application:,,,/Resources/out1.jpg"));
var meshGeometry3D = new MeshGeometry3D();
double circumference = imageSource.PixelWidth;
double radius = (circumference / (2.0 * Math.PI));
double angleSegment = (2.0 * Math.PI / SegmentCountTrans);
double sizeSegmentLong = ((double)imageSource.PixelHeight / SegmentCountLong);
double sizeSegmentTrans = ((double)imageSource.PixelWidth / SegmentCountTrans);
for (int indexSegmentLong = 0; indexSegmentLong < SegmentCountLong; indexSegmentLong++)
{
double depth = (indexSegmentLong * sizeSegmentLong);
for (int indexSegmentTrans = 0; indexSegmentTrans < SegmentCountTrans; indexSegmentTrans++)
{
meshGeometry3D.Positions.Add(GetVertexPosition(indexSegmentTrans, depth, radius, angleSegment));
meshGeometry3D.TextureCoordinates.Add(new Point(indexSegmentTrans * sizeSegmentTrans, depth));
}
// add one extra vertex representing 360 degrees for complete wrap-around
meshGeometry3D.Positions.Add(GetVertexPosition(SegmentCountTrans, depth, radius, angleSegment));
meshGeometry3D.TextureCoordinates.Add(new Point(SegmentCountTrans * sizeSegmentTrans, depth));
}
meshGeometry3D.TriangleIndices.Clear();
for (int indexSegmentLong = 0; indexSegmentLong < (SegmentCountLong - 1); indexSegmentLong++)
{
for (int indexSegmentTrans = 0; indexSegmentTrans < SegmentCountTrans; indexSegmentTrans++)
{
int indexCurrent = (indexSegmentLong * (SegmentCountTrans + 1) + indexSegmentTrans);
meshGeometry3D.TriangleIndices.Add(indexCurrent);
meshGeometry3D.TriangleIndices.Add(indexCurrent + 1);
meshGeometry3D.TriangleIndices.Add(indexCurrent + SegmentCountTrans + 1);
meshGeometry3D.TriangleIndices.Add(indexCurrent + SegmentCountTrans + 1);
meshGeometry3D.TriangleIndices.Add(indexCurrent + 1);
meshGeometry3D.TriangleIndices.Add(indexCurrent + SegmentCountTrans + 2);
}
}
var geometryModel3D = new GeometryModel3D
{
Geometry = meshGeometry3D,
Material = new DiffuseMaterial
{
Brush = new ImageBrush(imageSource)
}
};
this.SceneViewer.Model = geometryModel3D;
}
private static Point3D GetVertexPosition(int indexSegmentAngle, double depth, double radius, double angleSegment)
{
double angle = (StartAngle + indexSegmentAngle * angleSegment);
return new Point3D(
radius * Math.Cos(angle),
radius * Math.Sin(angle),
-depth);
}
XAML code for the Viewport3D element containing the model:
<Viewport3D x:Name="Viewport3D">
<Viewport3D.Camera>
<PerspectiveCamera x:Name="Camera" Position="0.0,0.0,0.0" LookDirection="0.0,0.0,-1.0" UpDirection="0.0,1.0,0.0" FieldOfView="90"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="White"/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D x:Name="ModelVisualModel"/>
</Viewport3D>
When you create your image brush try setting the TileMode to TileMode.Tile and ViewportUnits to BrushMappingMode.Absolute. If that still doesn't work then post a follow-up comment here and I'll try to reproduce it on my end.
I used DrawingBrush instead of Imagebrush. This fixed the seams and I also get an fps boost when setting the cachinghint.
Brush brush = new DrawingBrush(new ImageDrawing(_texture, new Rect(0,0,1,1)));
RenderOptions.SetCachingHint(brush , CachingHint.Cache);
RenderOptions.SetCacheInvalidationThresholdMinimum(brush , 0.5);
RenderOptions.SetCacheInvalidationThresholdMaximum(brush , 2.0);
How to create a circle in 3D with known center point, radius and it is on a plane which perpendicular to a line (vector) in WPF?
Following is an example based on the comment I posted earlier.
First we define a function to generate the model of the circle:
/// <summary>
/// Generates a model of a Circle given specified parameters
/// </summary>
/// <param name="radius">Radius of circle</param>
/// <param name="normal">Vector normal to circle's plane</param>
/// <param name="center">Center position of the circle</param>
/// <param name="resolution">Number of slices to iterate the circumference of the circle</param>
/// <returns>A GeometryModel3D representation of the circle</returns>
private GeometryModel3D GetCircleModel(double radius, Vector3D normal, Point3D center, int resolution)
{
var mod = new GeometryModel3D();
var geo = new MeshGeometry3D();
// Generate the circle in the XZ-plane
// Add the center first
geo.Positions.Add(new Point3D(0, 0, 0));
// Iterate from angle 0 to 2*PI
double t = 2 * Math.PI / resolution;
for (int i = 0; i < resolution; i++)
{
geo.Positions.Add(new Point3D(radius * Math.Cos(t * i), 0, -radius * Math.Sin(t * i)));
}
// Add points to MeshGeoemtry3D
for (int i = 0; i < resolution; i++)
{
var a = 0;
var b = i + 1;
var c = (i < (resolution - 1)) ? i + 2 : 1;
geo.TriangleIndices.Add(a);
geo.TriangleIndices.Add(b);
geo.TriangleIndices.Add(c);
}
mod.Geometry = geo;
// Create transforms
var trn = new Transform3DGroup();
// Up Vector (normal for XZ-plane)
var up = new Vector3D(0, 1, 0);
// Set normal length to 1
normal.Normalize();
var axis = Vector3D.CrossProduct(up, normal); // Cross product is rotation axis
var angle = Vector3D.AngleBetween(up, normal); // Angle to rotate
trn.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(axis, angle)));
trn.Children.Add(new TranslateTransform3D(new Vector3D(center.X, center.Y, center.Z)));
mod.Transform = trn;
return mod;
}
Setting up our ViewPort3D:
<Grid Background="Black">
<Viewport3D Name="mainViewPort">
<Viewport3D.Camera>
<PerspectiveCamera NearPlaneDistance="0.1" FarPlaneDistance="100" UpDirection="0,1,0" Position="0,0,10"
LookDirection="0,0,-1" FieldOfView="60" />
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="#40FFFFFF" />
<DirectionalLight Color="#FFFFFF" Direction="1,-1,-1" />
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
</Grid>
Then to test:
private void AddCircleModel()
{
var mod = GetCircleModel(1.5, new Vector3D(0, 1, 1), new Point3D(0, -1, 0), 20);
mod.Material = new DiffuseMaterial(Brushes.Silver);
var vis = new ModelVisual3D() { Content = mod };
mainViewPort.Children.Add(vis);
}
Load up your window, call AddCircleModel(); and enjoy. Adjust view/parameters to your heart's content.
old thread but still, if anyone comes accross.
The "newb" Answer would work but is quitte lazy math and time consuming wize, here's why :
there are 2 for loops where really 1 is needed.
using transform after building a circle on (x,z) plane centered on (0,0,0) is overkill since you know the maths and vectors constraints.
I would suggest you implement it that way (i made it so it works for ellipses too, just set uSize = vSize for circles, u and v really just need to no be parallel for it to work but for mesh quality purposes i force them to be perpendicular):
public static Mesh CreateEllipse3D(Vector3 center, Vector3 normal, Vector3 u,
Vector3 v, float uSize, float vSize, int
subdiv = 30)
{
if (Math.Abs(Vector3.Dot(u, v)) != 0)
{
Debug.LogError("Vectors u and v must be orthogonals");
return new Mesh();
}
var mesh = new Mesh();
var vertices = new List<Vector3>();
var normals = new List<Vector3>();
var triangles = new List<int>();
vertices.Add(center);
float t = 2 * Mathf.PI / subdiv;
int a = 0, b, c;
for (int i = 0; i < subdiv; i++)
{
vertices.Add(center + u.normalized * uSize * Mathf.Cos(t * i) -
v.normalized * vSize * Mathf.Sin(t * i));
b = i + 1;
c = (i < (subdiv - 1)) ? i + 2 : 1;
triangles.Add(a);
triangles.Add(b);
triangles.Add(c);
normals.Add(normal);
normals.Add(normal);
normals.Add(normal);
}
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.normals = normals.ToArray();
return mesh;
}