I'm currently working on a 3d bin packing problem to which I want to represent my results as an image.
I have the results stored as a list of packing objects as follows
public class LoadedPackage
{
private PackingObject packingObject;
private int xloc, yloc, zloc;
private bool flipped = false;
}
public class PackingObject
{
private int ID, checkerMaster, height, width, depth, number;
}
I want to use the xloc,yloc,zloc and dimensions to draw the packages 1 at a time to build up an image. Is there some sort of image library way of doing this or am I going to be forced to use an openGL solution which seems a little overkill to me for just a simple image.
I was thinking of maybe using a isometric method using 2d gdi lib
In the end I opted for an isometric approach and added a method to each package to convert itself from a 3d coordinates over to isometric. I hope the following code helps someone else with a similar predicament!
An Isometric cube has 6 points so I return an array of 6 points. In reality i use 3 sub methods to return the 3 isometric polygons for the GDI lib to process but for the sake of this answer I'll just post the more general 6 point method.
public PointF[] convertCoords()
{
float x;
float y;
PointF p;
PointF[] pointFs = new PointF[7];
int x1 = 0;
int y1 = 0;
int z1 = 0;
double rads = Helpers.DegreeToRadian(30);
//point 0 in iso
x = ((float)Math.Cos(rads) * xloc) + ((float)Math.Cos(rads) * yloc);
y = ((float)Math.Sin(rads) * yloc) + zloc - ((float)Math.Sin(rads) * xloc);
y = (-y) + 250;
p = new PointF(x,y);
pointFs[0] = new PointF(p.X,p.Y);
//point 1 in iso
x1 = xloc + packingObject.Depth;
y1 = yloc;
z1 = zloc;
x = ((float)Math.Cos(rads) * x1) + ((float)Math.Cos(rads) * y1);
y = ((float)Math.Sin(rads) * y1) + z1 - ((float)Math.Sin(rads) * x1);
y = (-y) + 250;
p = new PointF(x, y);
pointFs[1] = new PointF(p.X, p.Y);
//point 2 in iso
x1 = xloc + packingObject.Depth;
y1 = yloc;
z1 = zloc + packingObject.Height;
x = ((float)Math.Cos(rads) * x1) + ((float)Math.Cos(rads) * y1);
y = (-((float)Math.Sin(rads) * x1)) + ((float)Math.Sin(rads) * y1) + z1;
y = (-y) + 250;
p = new PointF(x, y);
pointFs[2] = new PointF(p.X, p.Y);
//point 3 in iso
x1 = xloc;
y1 = yloc;
z1 = zloc + packingObject.Height;
x = ((float)Math.Cos(rads) * x1) + ((float)Math.Cos(rads) * y1);
y = (-((float)Math.Sin(rads) * x1)) + ((float)Math.Sin(rads) * y1) + z1;
y = (-y) + 250;
p = new PointF(x, y);
pointFs[3] = new PointF(p.X, p.Y);
//point 4 in iso
x1 = xloc;
y1 = yloc + packingObject.Width;
z1 = zloc + packingObject.Height;
x = ((float)Math.Cos(rads) * x1) + ((float)Math.Cos(rads) * y1);
y = (-((float)Math.Sin(rads) * x1)) + ((float)Math.Sin(rads) * y1) + z1;
y = (-y) + 250;
p = new PointF(x, y);
pointFs[4] = new PointF(p.X, p.Y);
//point 5 in iso
x1 = xloc + packingObject.Depth;
y1 = yloc + packingObject.Width;
z1 = zloc + packingObject.Height;
x = ((float)Math.Cos(rads) * x1) + ((float)Math.Cos(rads) * y1);
y = (-((float)Math.Sin(rads) * x1)) + ((float)Math.Sin(rads) * y1) + z1;
y = (-y) + 250;
p = new PointF(x, y);
pointFs[5] = new PointF(p.X, p.Y);
//point 6 in iso
x1 = xloc + packingObject.Depth;
y1 = yloc + packingObject.Width;
z1 = zloc;
x = ((float)Math.Cos(rads) * x1) + ((float)Math.Cos(rads) * y1);
y = (-((float)Math.Sin(rads) * x1)) + ((float)Math.Sin(rads) * y1) + z1;
y = (-y) + 250;
p = new PointF(x, y);
pointFs[6] = new PointF(p.X, p.Y);
return pointFs;
}
Related
I'm new to using Unity compute shaders but have implemented a fluid solver in a script and a compute shader but the script is significantly faster even at high numbers of points to be simulated.
Is there anything majorly wrong with how I've implemented the shader? I think it might be the data I'm sending causing a bottleneck but I'm not entirely sure. Here is the function that runs the shader:
public struct dataLaxF
{
//Input Data
public Vector4 heightData;
public Vector4 fUData;
public Vector4 fVData;
public Vector4 uData;
public Vector4 vData;
//Output Data
public float outputHeight;
public float outputFU;
public float outputFV;
public float outputU;
public float outputV;
};
//Runs the algorithm on the GPU
public void SimulateGPU()
{
//Profiler Testing Start
Profiler.BeginSample("GPULaxF");
//Set data to structs for GPU
for (int x = 1; x < xGridSize-1; x++)
for (int y = 1; y < yGridSize-1; y++)
{
dataLaxF inData = new dataLaxF();
inData.heightData = new Vector4(height[x + 1, y], height[x - 1, y], height[x, y + 1], height[x, y - 1]);
inData.fUData = new Vector4(fU[x + 1, y], fU[x - 1, y], fU[x, y + 1], fU[x, y - 1]);
inData.fVData = new Vector4(fV[x + 1, y], fV[x - 1, y], fV[x, y + 1], fV[x, y - 1]);
inData.uData = new Vector4(u[x + 1, y], u[x - 1, y], u[x, y + 1], u[x, y - 1]);
inData.vData = new Vector4(v[x + 1, y], v[x - 1, y], v[x, y + 1], v[x, y - 1]);
inData.outputHeight = height[x, y];
inData.outputFU = fU[x, y];
inData.outputFV = fV[x, y];
inData.outputU = u[x, y];
inData.outputV = v[x, y];
data[(x * xGridSize) + y] = inData;
}
//Send data to GPU
int totalSize = sizeof(float) * 25;
ComputeBuffer dataBuffer = new ComputeBuffer(data.Length, totalSize);
dataBuffer.SetData(data);
computeShader.SetBuffer(0, "vDatas", dataBuffer);
computeShader.Dispatch(0, data.Length / 128, 1, 1);
dataBuffer.GetData(data);
//Recieve and read data
for (int x = 1; x < xGridSize-1; x++)
for (int y = 1; y < xGridSize-1; y++)
{
dataLaxF outData = data[x * xGridSize + y];
height[x, y] = outData.outputHeight;
fU[x, y] = outData.outputFU;
fV[x, y] = outData.outputFV;
u[x, y] = outData.outputU;
v[x, y] = outData.outputV;
}
dataBuffer.Dispose();
Profiler.EndSample();
}
And this is the compute shader:
#pragma kernel CSMain
struct dataLaxF
{
//Input Data
float4 heightData;
float4 fUData;
float4 fVData;
float4 uData;
float4 vData;
//Output Data
float outputHeight;
float outputFU;
float outputFV;
float outputU;
float outputV;
};
//Data struct arrays
RWStructuredBuffer<dataLaxF> vDatas;
//Kernel for Lax Friedrichs
[numthreads(128,1,1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
float alpha = 0.02f;
float g = 9.8f;
dataLaxF data = vDatas[id.x];
//Height
data.outputHeight = 0.25f * (data.heightData.x + data.heightData.y + data.heightData.z + data.heightData.a) - alpha * (data.fUData.x - data.fUData.y) - alpha * (data.fVData.z - data.fVData.a);
//FU
data.outputFU = 0.25f * (data.fUData.x + data.fUData.y + data.fUData.z + data.fUData.a);
data.outputFU = data.outputFU - alpha * ((data.heightData.x * (data.uData.x* data.uData.x) + (0.5f * g * (data.heightData.x* data.heightData.x))) - (data.heightData.y * (data.uData.y* data.uData.y) + (0.5f * g * (data.heightData.y* data.heightData.y))));
data.outputFU = data.outputFU - alpha * (data.heightData.z * data.vData.z * data.uData.z - data.heightData.a * data.vData.a * data.uData.a);
//FV
data.outputFV = 0.25f * (data.fVData.x + data.fVData.y + data.fVData.z + data.fVData.a);
data.outputFV = data.outputFV - alpha * (data.heightData.x * data.vData.x * data.uData.x - data.heightData.y * data.vData.y * data.uData.y);
data.outputFV = data.outputFV - alpha * ((data.heightData.z * (data.vData.z* data.vData.z) + (0.5f * g *(data.heightData.z* data.heightData.z))) - (data.heightData.a * (data.vData.a* data.vData.a) + (0.5f * g * (data.heightData.a* data.heightData.a))));
//U
data.outputU = data.outputFU / data.outputHeight;
//V
data.outputV = data.outputFV / data.outputHeight;
vDatas[id.x] = data;
}
Thanks in advance.
When i call my funtion with a startingAngle=0 it produce a good shape with the correct size.
Example:
var points = GetPolygonVertices(sides:4, radius:5, center:(5, 5), startingAngle:0), produces:
points[0] = {X = 10 Y = 5}
points[1] = {X = 5 Y = 0}
points[2] = {X = 0 Y = 5}
points[3] = {X = 5 Y = 10}
As observed the side length is 10px, which is correct, but produce a rotated square at 45º from human eye prespective.
To fix this i added a switch/case to offset the startAngle so it will put the square at correct angle for human eye, by rotating 45º. The rotation works, but the shape is no longer a square of 10x10px, instead i lose 1 to 2px from sides:
[0] = {X = 9 Y = 1}
[1] = {X = 1 Y = 1}
[2] = {X = 1 Y = 9}
[3] = {X = 9 Y = 9}
and become worse as radius grow, for example with radius=10:
[0] = {X = 17 Y = 3}
[1] = {X = 3 Y = 3}
[2] = {X = 3 Y = 17}
[3] = {X = 17 Y = 17}
I tried with both floor and ceil instead of round, but it always end in lose 1 or 2px...
Is there a way to improve the function to keep the shape size equal no matter the number of sides and rotation angle?
My function:
public static Point[] GetPolygonVertices(int sides, int radius, Point center, double startingAngle = 0)
{
if (sides < 3)
throw new ArgumentException("Polygons can't have less than 3 sides...", nameof(sides));
// Fix rotation
switch (sides)
{
case 3:
startingAngle += 90;
break;
case 4:
startingAngle += 45;
break;
case 5:
startingAngle += 22.5;
break;
}
var points = new Point[sides];
var step = 360.0 / sides;
int i = 0;
for (var angle = startingAngle; angle < startingAngle + 360.0; angle += step) //go in a circle
{
if (i == sides) break; // Fix floating problem
double radians = angle * Math.PI / 180.0;
points[i++] = new(
(int) Math.Round(Math.Cos(radians) * radius + center.X),
(int) Math.Round(Math.Sin(-radians) * radius + center.Y)
);
}
return points;
}
EDIT: I updated the function to get rid of the switch condition and product shapes in correct orientation for human eye when angle is not given. Still it suffer from same "problem"
public static Point[] GetPolygonVertices(int sides, int radius, Point center, double startingAngle = 0, bool flipHorizontally = false, bool flipVertically = false)
{
if (sides < 3)
throw new ArgumentException("Polygons can't have less than 3 sides...", nameof(sides));
var vertices = new Point[sides];
double deg = 360.0 / sides;//calculate the rotation angle
var rad = Math.PI / 180.0;
var x0 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + startingAngle) * rad);
var y0 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + startingAngle) * rad);
var x1 = center.X + radius * Math.Cos(-(((180 - deg) / 2) + deg + startingAngle) * rad);
var y1 = center.Y - radius * Math.Sin(-(((180 - deg) / 2) + deg + startingAngle) * rad);
vertices[0] = new(
(int) Math.Round(x0),
(int) Math.Round(y0)
);
vertices[1] = new(
(int) Math.Round(x1),
(int) Math.Round(y1)
);
for (int i = 0; i < sides - 2; i++)
{
double dsinrot = Math.Sin((deg * (i + 1)) * rad);
double dcosrot = Math.Cos((deg * (i + 1)) * rad);
vertices[i + 2] = new(
(int)Math.Round(center.X + dcosrot * (x1 - center.X) - dsinrot * (y1 - center.Y)),
(int)Math.Round(center.Y + dsinrot * (x1 - center.X) + dcosrot * (y1 - center.Y))
);
}
if (flipHorizontally)
{
var startX = center.X - radius;
var endX = center.X + radius;
for (int i = 0; i < sides; i++)
{
vertices[i].X = endX - (vertices[i].X - startX);
}
}
if (flipVertically)
{
var startY = center.Y - radius;
var endY = center.Y + radius;
for (int i = 0; i < sides; i++)
{
vertices[i].Y = endY - (vertices[i].Y - startY);
}
}
return vertices;
}
EDIT 2: From Tim Roberts anwser here the functions to calculate side length from radius and radius from side length, this solve my problem. Thanks!
public static double CalculatePolygonSideLengthFromRadius(double radius, int sides)
{
return 2 * radius * Math.Sin(Math.PI / sides);
}
public static double CalculatePolygonVerticalLengthFromRadius(double radius, int sides)
{
return radius * Math.Cos(Math.PI / sides);
}
public static double CalculatePolygonRadiusFromSideLength(double length, int sides)
{
var theta = 360.0 / sides;
return length / (2 * Math.Cos((90 - theta / 2) * Math.PI / 180.0));
}
Your problem is one of mathematics. You said "As observed, the side length is 10px". It very definitely is not 10px. The distance from (10,5) to (5,0) is sqrt(5*5 + 5*5), which is 7.07. That's exactly what we expect for a square that is inscribed in a circle of radius 5: 5 x sqrt(2).
And that's what the other squares are as well.
FOLLOWUP
As an added bonus, here is a function that returns the radius of the circle that circumscribes a regular polygon with N sides of length L:
import math
def rad(length,nsides):
theta = 360/nsides
r = length / (2 * math.cos( (90-theta/2) * math.pi / 180))
return r
for s in range(3,9):
print(s, rad(10,s))
I want to draw sin(θ)*cos(θ), but it doesn't work.
I can draw sin or cos,
but I want to draw sin(θ)*cos(θ) together.
Here is my code
private void button1_Click(object sender, EventArgs e)
{
Graphics drw = this.CreateGraphics();
Pen pen = new Pen(Brushes.Black, 7.0f);
float x1 = 0;
float y1 = 0;
float xoy = 200;
float ef = 20;
for (double i=0;i<40;i+=1)
{
double radi = (float)(i * 180 / Math.PI);
float temp = (float)Math.Cos(radi)*(float)Math.Sin(radi);
drw.DrawLine(pen, x1 * ef, y1 * ef + xoy, ef * (float)i, temp * ef + xoy);
x1 = (float)i;
y1 = temp;
}
}
And I want this result:
You may find it easier to look at the corresponding Parametric Equations.
private void Form1_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
double pi = Math.PI;
int n = 100;
var t = Enumerable.Range(0, n).Select(p => p * 2 * pi / n).ToArray();
var x = t.Select(p => Math.Sin(2 * p) * Math.Cos(p)).ToArray();
var y = t.Select(p => Math.Sin(2 * p) * Math.Sin(p)).ToArray();
Pen pen = new Pen(Brushes.Black, 3);
int scale = 100;
int shift = 100;
for (int i = 0; i < n - 1; i++)
{
g.DrawLine(pen, scale*(float)x[i] + shift,
scale*(float)y[i] + shift,
scale*(float)x[i + 1] + shift,
scale*(float)y[i + 1] + shift);
}
}
Actually, the real function you are looking for is a little bit different... see an example here. Looking at this article about polar flowers, I'm sure it will get pointed to the right direction, and it also contains a full working source code.
Just an example, supposing you use a panel in your form on which to draw the polar flower:
panel.OnPaint += Panel_Paint;
private void Panel_Paint(Object sender, PaintEventArgs e)
{
Double scale = ((Panel)sender).Width / 2.0d;
Double repetitions = Math.Round(scale, 0);
Double basis = (2.0d * Math.PI) / scale;
Double petals = 2.0d;
using (Graphics g = e.Graphics)
{
using (Pen pen = new Pen(Brushes.Red, 2.0f))
{
for (Double i = 0.0f; i < (repetitions - 1); ++i)
{
Double t0 = i*basis;
Double t1 = (i + 1)*basis;
Double x0 = Math.Sin(petals * t0) * Math.Cos(t0);
Double x1 = Math.Sin(petals * t1) * Math.Cos(t1);
Double y0 = Math.Sin(petals * t0) * Math.Sin(t0);
Double y1 = Math.Sin(petals * t1) * Math.Sin(t1);
g.DrawLine
(
pen,
(Single) ((scale*x0) + scale),
(Single) ((scale*y0) + scale),
(Single) ((scale*x1) + scale),
(Single) ((scale*y1) + scale)
);
}
}
}
}
The basic formulation states that if the petals variable value is:
even, then it represents half the amount of petals of the polar flower
odd, then it represents the amount of petals of the polar flower
so if you define Double petals = 2.0d;, you will obtain 4 petals... and if you define Double petals = 5.0d;, you will obtain 5 petals.
I am very new to c# form and i would like to write some letters in a specific location. As you can see in the picture i have attached I've drawn a curved Line with a X and Y axis to scale it with. I would like to write the letter X on the edge of the horizontal line and Y on the top Edge of the vertical line. Also is there any possible ways to assign values on the line as well?
protected override void OnPaint(PaintEventArgs e)
{
float a = 1, b = 5, c = -4;
double x1, x2, x3, x4, x5, x6, y1, y2, y3, y4, y5, y6, x7, y7, delta;
delta = (b * b) - (4 * a * c);
x1 = ((b * (-1)) + Math.Sqrt(delta)) / (2 * a);
x6 = ((b * (-1)) - Math.Sqrt(delta)) / (2 * a);
y6 = a * (x6 * x6) + (b * (x6)) + c;
y1 = a * (x1 * x1) + (b * (x1)) + c;
x2 = 3;
y2 = a * (x2 * x2) + (b * (x2)) + c;
x3 = -3;
y3 = a * (x3 * x3) + (b * (x3)) + c;
x4 = 5;
y4 = a * (x4 * x4) + (b * (x4)) + c;
x5 = -10;
y5 = a * (x5 * x5) + (b * (x5)) + c;
x7 = 0;
y7 = a * (x7 * x7) + (b * (x7)) + c;
int cx1 = Convert.ToInt32(x1);
int cx2 = Convert.ToInt32(x2);
int cx3 = Convert.ToInt32(x3);
int cy1 = Convert.ToInt32(y1);
int cy2 = Convert.ToInt32(y2);
int cy3 = Convert.ToInt32(y3);
int cx4 = Convert.ToInt32(x4);
int cy4 = Convert.ToInt32(y4);
int cx5 = Convert.ToInt32(x5);
int cy5 = Convert.ToInt32(y5);
int cx6 = Convert.ToInt32(x6);
int cy6 = Convert.ToInt32(y6);
int cx7 = Convert.ToInt32(x7);
int cy7 = Convert.ToInt32(x7);
Graphics g = e.Graphics;
int deltaX = 300;
int deltaY = 300;
g.TranslateTransform(deltaX, deltaY);
float factor = 2.5f;
Matrix m = new Matrix();
m.Scale(factor, factor);
g.MultiplyTransform(m);
Pen aPen = new Pen(Color.Blue, 1);
aPen.DashStyle = DashStyle.DashDot;
Pen bPen = new Pen(Color.Green, 1);
bPen.EndCap = LineCap.ArrowAnchor;
Pen cPen = new Pen(Color.Green, 1);
cPen.StartCap = LineCap.DiamondAnchor;
Point point1 = new Point(cx1, -cy1);
Point point2 = new Point(cx2, -cy2);
Point point3 = new Point(cx3, -cy3);
Point point4 = new Point(cx4, -cy4);
Point point5 = new Point(cx5, -cy5);
Point point6 = new Point(cx6, -cy6);
Point pointa = new Point(20, -50);
Point pointb = new Point(40, -30);
Point pointc = new Point(60, -70);
Point[] Points = { point5, point3, point1, point2, point4 };
Point[] Pointss = { pointa, pointb,pointc };
g.DrawCurve(new Pen(Color.Red, 1), Pointss);
g.DrawCurve(aPen, Points);
g.DrawLine((cPen), new Point(cx7, -100), new Point(cx7, 100));
g.DrawLine((bPen), -100, 0, 100, 0);
check my sample. I hope it helps.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace GraphicsForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
var w = ClientRectangle.Width;
var h = ClientRectangle.Height;
var midY = h/2;
var midX = w/2;
var linePen = new Pen(Brushes.Red, 1)
{
StartCap = LineCap.DiamondAnchor,
EndCap = LineCap.DiamondAnchor
};
//horizontal line
g.DrawLine(linePen, 0, midY, w, midY);
var font = Font;
var measureStringX = g.MeasureString("x", font);
g.DrawString("x", font, Brushes.Black, w - measureStringX.Width - 2, midY + 2);
//vertical line
g.DrawLine(linePen, midX, 0, midX, h);
g.DrawString("y", font, Brushes.Black, midX + 2, 2);
//horizontals&vertical marks
const float marksCount = 12f;
var wx = w / marksCount;
var hx = h / marksCount;
var markPen = new Pen(Brushes.Red, 1);
for (int i = 1; i < marksCount; i++)
{
g.DrawLine(markPen, i * wx, midY, i * wx, midY + 5);
g.DrawLine(markPen, midX, hx * i, midX + 5, hx * i);
}
}
}
}
"Check out DrawString on the graphics object. https://msdn.microsoft.com/en-us/library/system.drawing.graphics.drawstring.aspx – dbugger"
Answered.
I'm trying to move the cursor using linear interpolation. My problem is that the value of y0 + (y1 - y0) * ((x - x0) / (x1 - x0)) never changes despite the fact that x changes. I can't figure out what I'm missing.
public void MoveCursor(int x1, int y1)
{
int y, y0, x, x0;
y0 = Cursor.Position.Y;
x0 = Cursor.Position.X;
for (x = x0; x > x1; x--)
{
y = y0 + (y1 - y0) * ((x - x0) / (x1 - x0));
this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(x,y);
Cursor.Clip = new Rectangle(this.Location, this.Size);
Console.Out.WriteLine("X:{0} Y:{1}", x, y);
System.Threading.Thread.Sleep(100);
}
}
Try using floats:
y = (int)(y0 + (float)(y1 - y0) * (x - x0) / (x1 - x0));