Unexpected output of Transform3D.Transform method in WPF - c#

I need to define a 3D transformation and apply it to a point p ={1000, 0, 0}.
For example, say one needs to apply a Pi/2 rotation around the z axis. I defined the transformation using a MatrixTransform3D. From the code below:
EXPECTED OUTPUT: trPoint = {0, 1000, 0}
ACTUAL OUTPUT: trPoint = {0, -1000, 0}.
QUESTION: Perhaps the Transform3D.Transform method applies the inverse transform instead?
private void testTransformationMat() {
Point3D p = new Point3D(1000, 0, 0);
double angle = System.Math.PI / 2;
double cos = System.Math.Cos(angle);
double sin = System.Math.Sin(angle);
Matrix3D mat_z = new Matrix3D(cos, -sin, 0, 0,sin, cos, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
Transform3D tr = new MatrixTransform3D(mat_z);
Point3D trPoint = tr.Transform(p);
Debug.WriteLine(trPoint);
}
EDIT:
To get things straight, as far as I've always known, a homogeneous transformation is applied multiplying a 4x4 matrix with a 4x1 vector. For a +45deg rotation around the z axis, under right-hand convention, we obtain:
0.707 -0.707 0 0 1000 707
0.707 0.707 0 0 X 0 = 707
0 0 1 0 0 0
0 0 0 1 1 0
If we invert the matrix, the multiplication returns a negative y component which is not coherent with a +45deg rotation around Z.
0.707 0.707 0 0 1000 707
-0.707 0.707 0 0 X 0 = -707
0 0 1 0 0 0
0 0 0 1 1 0

From 3-D Transformations Overview
Note:Windows Presentation Foundation (WPF) 3-D is a right-handed
system, which means that a positive angle value for a rotation results
in a counter-clockwise rotation about the axis.

Related

Uniformly sampling points in a cube using a coordinate system [C#, Unity]

I have a cube with six vertices of form (x, y, z), in a 3D coordinate system. All information about cube is available, including vertices, edge lengths, etc.
I would like an efficient way to uniformly return n ^ 3 points, with n points on each edge, something like the coordinate system itself. An image is attached (ignore the colouring) (source: Sebastian Lague's video on marching cubes):
I've more or less been able to implement this in Python, and I'd like a C# version.
An example of what I want:
I've used a unit cube at the origin as an example, what I'm after is pseudocode or logic that works for any cube at any coordinates.
More information: I'd like to have code that works for cubes in any orientation, but failing that code that works for cubes aligned with the grid but in any position.
Thanks to Jon Skeet for the help and clarification!
/*
Input:
(0, 0, 0)
(1, 0, 0)
(1, 1, 0)
(1, 1, 1)
(0, 1, 1)
(0, 0, 1)
(0, 1, 0)
(1, 0, 1)
27 points to be generated: n = 3
3 points per edge (INCLUDING VERTICES)
Output:
(0, 0, 0)
(1, 0, 0)
(1, 1, 0)
(1, 1, 1)
(0, 1, 1)
(0, 0, 1)
(0, 1, 0)
(1, 0, 1)
(0, 0.5, 0)
(0.5, 0, 0)
(0, 0, 0.5)
(0.5, 0.5, 0)
(0.5, 0, 0.5)
(0.5, 0.5, 0.5)
(0, 0.5, 0.5)
(1, 0.5, 0)
... etc
*/
I'm using it in Unity, so answers with Vector3 and such would work too.
Some pseudocode:
// MagicPointGenerator is really what I'm after
float[][] GeneratePoints(float[][] vertices, int pointsPerEdge)
{
float[][] points = new float[(int)Math.Pow(pointsPerEdge, 3)][3];
for (int i = 0; i <= pointsPerEdge; i++)
{
for (int j = 0; j <= pointsPerEdge; j++)
{
for (int k = 0; k <= pointsPerEdge; k++)
{
points[i + j + k] = MagicPointGenerator(i, j, k);
}
}
}
return points;
}

Lines and shapes drawn at incorrect angles

I have a c# program where I need to draw some simple 2D objects on the canvas.
One of these involves drawing a rectangle and lines where I know the start point, the length and I have to calculate the end position. So I have the following code;
private void CalculateEndPoint()
{
double angle = Helper.deg2rad((double)this.StartAngle);
int x = this.StartPoint.X + (int)(Math.Cos(angle) * this.Length * -1);
int y = this.StartPoint.Y + (int)(Math.Sin(angle) * this.Length);
this.EndPoint = new Point(x, y);
}
Now this seems to work OK to calculate the end points. The issue I have is with the angle (this.StartAngle), the value I specify seems not to be how it is drawn and I seem to have the following;
Where as I'm expecting 0 at the top, 90 on the right, 180 at the bottom etc.
So to get a shape to draw straight down the canvas I have to specify 90 degrees, where as I would expect to specify 180.
Have I done something wrong? Or is it just a lack of understanding?
You should change your CalculateEndPoint function to have that:
private static void CalculateEndPoint(double dec)
{
double angle = (Math.PI / 180) * (this.StartAngle + 90); // add PI / 2
int x = StartPoint.X + (int)(Math.Cos(angle) * Length * -1);
double angle2 = (Math.PI / 180) * (this.StartAngle - 90); // minus PI / 2
int y = StartPoint.Y + (int)(Math.Sin(angle2) * Length);
EndPoint = new Point(x, y);
}
Actually, 0 should be on the right. You are multiplying the x-coordinate by -1, so you're moving it to the left.
Just remember these 2 rules:
- The cosine of the angle is the x-coordinate of the unit circle.
- The sine of the angle is the y-coordinate of the unit circle.
Since cos(0) = 1 and sin(0) = 0, the coordinate corresponding to angle 0 is (1, 0).
Whether 90 is on top or on the bottom depends on the canvas.
Some applications/frameworks consider y-coordinate 0 to be at the top of the canvas. That means you go clockwise around the circle and 90 will be at the bottom.
If y-coordinate 0 is at the bottom of the canvas, you go counter-clockwise and 90 will be at the top.

Calculating the Point3Ds of a Cuboid around a Line

There are two Point3D's (A and B) and I want to calculate the points of a cuboid (a,b,c ... h) surrounding the line between A and B like a hull:
There is one degree of freedom, the angle of the cuboid, because it can rotate around the line AB. I am not sure yet if this is a problem.
I tried to calculate a vector normal to AB, D, and then the cross product of AB ⨯ AD = E. In the code, C is A - B so its the offset parallel to AB.
I normalized these three vectors (C, D and E) and multiplied it with an offset to add / subtract them from A and B. It's not quite working yet.
EDIT: see ja72's code for solution
i also implemented a way of finding a normal vector:
double ax = Vector3D.AngleBetween(E, new Vector3D(1, 0, 0));
double ay = Vector3D.AngleBetween(E, new Vector3D(0, 1, 0));
double az = Vector3D.AngleBetween(E, new Vector3D(0, 0, 1));
ax = Math.Abs(ax - 90);
ay = Math.Abs(ay - 90);
az = Math.Abs(az - 90);
if (ax <= ay & ax <= az)
{
n = Vector3D.CrossProduct(E, new Vector3D(1, 0, 0));
}
else if (az <= ax && az <= ay)
{
n = Vector3D.CrossProduct(E, new Vector3D(0, 0, 1));
}
else
{
n = Vector3D.CrossProduct(E, new Vector3D(0, 1, 0));
}
n = normalize(n);
You need two direction vectors. One is along the line AB given by
Vector3D e = Normalize(B-A)
and one to descibe the "up" direction for the cross section. This can be given, or it can be calculated with the following algorithm (with preference towards +y)
if( e.X != 0 || e.Z != 0 )
{
// choose direction perpendicular to line closest to +y direction
Vector3D n = [-e.X*e.Y, e.X*e.X+e.Z*e.Z, -e.Z*e.Y];
} else {
// if line along +y already then choose +z for up vector
Vector3D n = [ 0, 0, 1];
}
Now you can calculate the 3rd direction to form a coordinate system
Vector3D k = Normalize( Cross(e,n) )
And you assemble the 3×3 rotation matrix that transforms local coordinates to world coordinates with
| k.X n.X e.X |
R = | k.Y n.Y e.Y |
| k.Z n.Z e.Z |
The local coordinate have the direction along the line as +z such that
Point3D a = A + R*[w,h,0]
Point3D b = A + R*[-w,h,0]
Point3D c = A + R*[w,-h,0]
Point3D d = A + R*[-w,-h,0]
Point3D e = B + R*[w,h,0]
Point3D f = B + R*[-w,h,0]
Point3D g = B + R*[w,-h,0]
Point3D h = B + R*[-w,-h,0]
where R*[x,y,z] designates a matrix-vector multiplication, w and h are the width and height of the rectangular cross section, and A, B are the point A and B position vectors. Addition here between vectors is element by element.
I have checked the code in my own code and it works. vec3 is alias for a 3D vector, mat3 is alias for 3×3 matrix.
vec3 u=(B-A).Normalized();
vec3 n = vec3.O;
if(Math.Abs(u.X)<=Math.Abs(u.Y)&&Math.Abs(u.X)<=Math.Abs(u.Z))
{
n=new vec3(u.Y*u.Y+u.Z*u.Z, -u.Y*u.X, -u.Z*u.X);
}
else if(Math.Abs(u.Y)<=Math.Abs(u.X)&&Math.Abs(u.Y)<=Math.Abs(u.Z))
{
n=new vec3(-u.X*u.Y, u.X*u.X+u.Z*u.Z, -u.Z*u.Y);
}
else if(Math.Abs(u.Z)<=Math.Abs(u.X)&&Math.Abs(u.Z)<=Math.Abs(u.Y))
{
n=new vec3(-u.X*u.Z, -u.Y*u.Z, u.X*u.X+u.Y*u.Y);
}
vec3 v=n.Cross(u);
mat3 R=mat3.Combine(v, n, u);
var a=A+R*new vec3(wt, ht, 0);
var b=A+R*new vec3(-wt, ht, 0);
var c=A+R*new vec3(wt, -ht, 0);
var d=A+R*new vec3(-wt, -ht, 0);
var e=B+R*new vec3(wt, ht, 0);
var f=B+R*new vec3(-wt, ht, 0);
var g=B+R*new vec3(wt, -ht, 0);
var h=B+R*new vec3(-wt, -ht, 0);
I can't give you a real piece of code, but I can give you an idea.
Ok, suppose you are already have a proper cuboid. I mean it has right width and height.
Lets locate it on plane xy. After that you need to offset it to center (minus offset vector). The last thing is to rotate it according to your line rotation.
Again:
Create cuboid and locate it on xy plane
Move it according to your offsets
Rotate it according to your line rotation
You may use matrix multiplication to achieve this transformations.

How to rotate a 4x4 matrix (C#/XNA)

I've got an array of [4,4]
X is the only one I "know", the rest is calculated with a simple double for-loop.
x 0 0 0
0 0 1 0
0 1 1 0
0 1 0 0
I want a function that take this array, and rotate it 90 degrees + / - while the position of x stays the same. (It's supposed to be tetris)
x 0 0 0
1 1 0 0
0 1 1 0
0 0 0 0
I know some way to hardcode the permutations but what that wouldn't learn me anything and it's frankly quite boring.
Would appreciate the help :>
I'm not sure how exactly you intend to rotate a matrix by 90 degrees and yet still have the top left X in the top left of the rotated version, but to rotate something by 90 degrees, I'd just make a new array, swap rows and columns and flip horisontally.
int[][] start = new int[4][];
start[0] = new int[4] { x, 0, 0, 0 }
start[1] = new int[4] { 0, 0, 1, 0 }
start[2] = new int[4] { 0, 1, 1, 0 }
start[3] = new int[4] { 0, 1, 0, 0 }
int[][] rotate = new int[4][];
for (int i=0; i<4; i++) rotate[i] = new int[4];
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
rotate[i][j] = start[j][i];
Rotate finishes with:
0, 0, 0, 0,
0, 0, 1, 1,
0, 1, 1, 0,
0, 0, 0, 0,
Now this is a diagonal flip (EDIT: It just occurs to me that this will keep x in the same position: perhaps this is what you mean?), but just do a horisontal flip and it should be fine:
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
rotate[i][3-j] = start[j][i];
Rotate finishes with:
0, 0, 0, 0,
1, 1, 0, 0,
0, 1, 1, 0,
0, 0, 0, 0,
(To tilt other way: rotate[i][j] = start[j][3-i];)
:)

Algorithm: How do I fade from Red to Green via Yellow using RGB values?

I want to display a color based on a value from 0 to 100. At one end (100), it's pure Red, the other end (0), pure Green. In the middle (50), I want it to be yellow.
And I want the colors to fade gradually from one to another, such that at 75, the color is half red and half yellow, etc.
How do I program the RGB values to reflect this fading? Thanks.
I had the same need and I just resolved with this:
myColor = new Color(2.0f * x, 2.0f * (1 - x), 0);
Explanation:
Instead of the [0-255] range, let's focus on the [0.0-1.0] range for color components:
Green = 0.0, 1.0, 0.0
Yellow = 1.0, 1.0, 0.0
Red= 1.0, 0.0, 0.0
If you just scale the green component from 0.0 (on one end) to 1.0 (on the other end) and do the same thing with the red component (but going backwards), you'll get ugly and non-uniform color distribution.
To make it look nice, we could write a lot of code, or we could be more clever.
If you look carefully at the single components, you can see that we can split the range in two equal parts: in the first one we increase the red component from 0.0 to 1.0, leaving the green at 1.0 and the blue at 0.0; in the second we decrease the green component, leaving the other 2 as they are. We can take advantage of the fact that any value above 1.0 will be read as 1.0, by maxing out our values to simplify the code. Assuming your x value goes from 0.00 (0%) to 1.00 (100%), you can multiply it by 2 to let it go over the 1.0 limit for color components. Now you have your components going from 0.0 to 2.0 (the red one) and from 2.0 to 0.0 (the green one). Let them be clipped to [0.0-1.0] ranges and there you go.
If your x moves in another range (like [0-100]) you need to choose an appropriate factor instead of 2
The RGB values for the colors:
Red 255, 0, 0
Yellow 255, 255, 0
Green 0, 255, 0
Between Red and Yellow, equally space your additions to the green channel until it reaches 255. Between Yellow and Green, equally space your subtractions from the red channel.
Here is a very simple linear interpolation of the color components. It might serve your needs.
public Color GetBlendedColor(int percentage)
{
if (percentage < 50)
return Interpolate(Color.Red, Color.Yellow, percentage / 50.0);
return Interpolate(Color.Yellow, Color.Lime, (percentage - 50) / 50.0);
}
private Color Interpolate(Color color1, Color color2, double fraction)
{
double r = Interpolate(color1.R, color2.R, fraction);
double g = Interpolate(color1.G, color2.G, fraction);
double b = Interpolate(color1.B, color2.B, fraction);
return Color.FromArgb((int)Math.Round(r), (int)Math.Round(g), (int)Math.Round(b));
}
private double Interpolate(double d1, double d2, double fraction)
{
return d1 + (d2 - d1) * fraction;
}
I don't know C#, so this answer is just a suggested approach. Let x denote the int that ranges from 0 to 100. Something like this should work:
red = (x > 50 ? 1-2*(x-50)/100.0 : 1.0);
green = (x > 50 ? 1.0 : 2*x/100.0);
blue = 0.0
The idea is to start at red: (1.0,0.0,0.0). Then increase the green to get yellow: (1.0,1.0,0.0). Then decrease the red to get green: (0.0,1.0,0.0).
Edit: Here is the code in C#
static Color GetColorFromRedYellowGreenGradient(double percentage)
{
var red = (percentage > 50 ? 1 - 2 * (percentage - 50) / 100.0 : 1.0) * 255;
var green = (percentage > 50 ? 1.0 : 2 * percentage / 100.0) * 255;
var blue = 0.0;
Color result = Color.FromArgb((int)red, (int)green, (int)blue);
return result;
}
Simplified extension method;
public static Color Interpolate(this Color source, Color target, double percent)
{
var r = (byte)(source.R + (target.R - source.R) * percent);
var g = (byte)(source.G + (target.G - source.G) * percent);
var b = (byte)(source.B + (target.B - source.B) * percent);
return Color.FromArgb(255, r, g, b);
}
Usage;
var low = 33.0;
var high = 100.0;
var color = Color.Red.Interpolate(Color.Green, low / high);
After I experimented for a while with more realistic colors, here is my formula:
public Color GetColorOf(double value, double minValue, double maxValue)
{
if (value == 0 || minValue == maxValue) return Color.White;
var g = (int)(240 * value / maxValue);
var r = (int)(240 * value / minValue);
return (value > 0
? Color.FromArgb(240 - g, 255 - (int)(g * ((255 - 155) / 240.0)), 240 - g)
: Color.FromArgb(255 - (int)(r * ((255 - 230) / 240.0)), 240 - r, 240 - r));
}
You get no background (i.e. Color.White) for 0 (or NULL), or when min = max.
For all positive values, you get an evenly distributed green color between RGB(240, 255, 240) and RGB(0, 155, 0).
For all negative values, you get an evenly distributed red color between RGB(255, 240, 240) and RGB(230, 0, 0).
You need to use the HSB or HSV color representation instead, and play with the H ("Hue") value. See this other SO question for transformation betweeen RGB and HSB/HSV: How to change RGB color to HSV?
Take a look at LinearGradientBrush. It should be a complete implementation on what you're looking for.
I wrote a Swift 4 extension for UIColor which converts a percentage (0-100) to a UIColor from red to green.
I'm currently using it for progress bars in an analytics app. Here's an example:
let myNewColor = UIColor().greenRedProgress(percent: Int))
Here's the extension:
extension UIColor{
func greenRedProgress(percent: Int) -> UIColor{
let modVal = CGFloat((Double(percent).truncatingRemainder(dividingBy: 50) / 50) * 255)
if percent <= 0{
return UIColor(red: 1.0, green: 0, blue: 0, alpha: 1)
}else if percent >= 100{
return UIColor(red: 0, green: 1.0, blue: 0, alpha: 1)
}else{
switch percent{
case 1..<50: return UIColor(red: 1.0, green: (modVal/255), blue: 0, alpha: 1)
case 51..<100: return UIColor(red: (255 - modVal)/255, green: 1.0, blue: 0, alpha: 1)
case 50: return UIColor(red: 1.0, green: 1.0, blue: 0, alpha: 1)
default: return UIColor(red: 0, green: 1.0, blue: 0, alpha: 1)
}
}
}}
This method (in c#, but can be easily translated to other languages) will take a percentage and list of colors and return the color on the gradient based on your percentage. When you pass in the colors, they need to be in order from 0 value to 100 value (so you would want to pass Green, Yellow, Red - in that order). If you find that you want different or more colors in the middle, just add them to the list of colors you pass in the order you want them to appear.
public Color ColorBasedOnPercent(decimal percent, params Color[] colors)
{
if (colors.Length == 0)
{
//I am using Transparent as my default color if nothing was passed
return Color.Transparent;
}
if (percent > 1)
{
percent = percent / 100;
}
//find the two colors within your list of colors that the percent should fall between
var colorRangeIndex = (colors.Length - 1) * percent;
var minColorIndex = (int)Math.Truncate(colorRangeIndex);
var maxColorIndex = minColorIndex + 1;
var minColor = colors[minColorIndex];
if (maxColorIndex < colors.Length)
{
var maxColor = colors[maxColorIndex];
//get the differences between all the color values for the two colors you are fading between
var aScale = maxColor.A - minColor.A;
var redScale = maxColor.R - minColor.R;
var greenScale = maxColor.G - minColor.G;
var blueScale = maxColor.B - minColor.B;
//the decimal distance of how "far" this color should be from the minColor in the range
var gradientPct = colorRangeIndex - minColorIndex;
//for each piece of the color (ARGB), add a percentage(gradientPct) of the distance between the two colors
int getRGB(int originalRGB, int scale) => (int)Math.Round(originalRGB + (scale * gradientPct));
return Color.FromArgb(getRGB(minColor.A, aScale), getRGB(minColor.R, redScale), getRGB(minColor.G, greenScale), getRGB(minColor.B, blueScale));
}
return minColor;
}
you just need to create a function with integer parameter
input 100 will return RGB (100, 0, 0)
input 50 will return RGB (50, 50, 0)
input 0 will return RGB (0, 100, 0)
input 99 will return RGB (99, 1, 0)
input 98 will return RGB (98, 2, 0)
input 2 will return RGB (2, 98, 0)
input 1 will return RGB (1, 99, 0)
private Color fader(int v){
return Color.FromArgb(v, 100-v, 0);
}
I had a need for something similar today. Input was percent from 0.0 to 1.0, and output red to green. Implementation based on jterrace's answer:
Color percentToColor(float percent)
{
if (percent<0 || percent>1) { return Color.Black; }
int r, g;
if (percent<0.5)
{
r=255;
g = (int)(255*percent/0.5); //closer to 0.5, closer to yellow (255,255,0)
}
else
{
g=255;
r = 255 - (int)(255*(percent-0.5)/0.5); //closer to 1.0, closer to green (0,255,0)
}
return Color.FromArgb(r, g, 0);
}
I mapped values of CPU speed between 1400 MHz and 3500 MHz to rgb() values to get from green -> yellow -> red with this function
function green_yellow_red(core_MHz, core_id){
var core_color = ~~core_MHz.map(1400, 3500, 0, 510)
if(core_color < 255){
$('#cpu_core_'+core_id).css('background', 'rgb('+core_color+',255 , 0)')
}else{
core_color-=255
$('#cpu_core_'+core_id).css('background', 'rgb(255 ,'+ (255-core_color) +', 0)')
}
}
More of the same. Just Delphi Pascal coded and simplified+locked to semaphore colors (red/yellow/green).
rectangle1.Fill.Color:=DefineColorSemaphore(newcolor);
function TForm1.DefineColorSemaphore(valperc:integer):TAlphaColor;
var
vcol: TAlphaColorRec;
begin
vcol.B := 0; // blue: always 0
vcol.A := 255; // alpha: 255=no
if (valperc < 50) then
begin
// starts # RGB=255,0,0 and increases G 0->255
vcol.R := 255;
vcol.G := trunc(255*valperc/50);
end
else
begin
// starts # RGB=255,255,0 and decreases R 255->0
vcol.R := 255-trunc(255* (valperc - 50)/50);
vcol.G := 255;
end;
result:= TAlphaColor(vcol);
end;
Recently I was planning to do samething using Javascript. I have followed two steps.
First scale the value in 0-1 range(scaledValue = (value - min) / (max - min))
Then increase value of Red channel if the value is less than or equal 0.5 and then decrease value of Green channel if the value is greater than 0.5
Here is the code I used for this purpose.
var blue = 0.0, red = 0.0, green = 0.0;
if(scaledValue <= 0.5)
{
red = (scaledValue * 2) * 255.0;
green = 255.0;
blue = 0;
}else
{
red = 255.0;
green = 255.0 + 255.0 - ((scaledValue * 2)* 255);
blue = 0;
}
A sample code for showing this works in C++
This solution is inspired by #jterrace answer.
// To fill an OwnerDraw control with red-yellow-green gradient upon WM_DRAWITEM
int cx = lpDis->rcItem.right - lpDis->rcItem.left;
for (int x = 0; x < cx; x++)
{
COLORREF cr;
double d = 255 * 2 * (double)x/(double)cx;
cr = x <= cx/2 ? RGB(255, d, 0) :
RGB(255 - d, 255, 0);
HPEN hPen = CreatePen(PS_SOLID, 1, cr);
HPEN hOldPen = (HPEN)SelectObject(lpDis->hDC, hPen);
MoveToEx(lpDis->hDC, x, lpDis->rcItem.top, NULL);
LineTo(lpDis->hDC, x, lpDis->rcItem.bottom);
SelectObject(lpDis->hDC, hOldPen);
DeleteObject(hPen);
}
Illustration
If you are on python then this might help...
def rgb(p):# <-- percentage as parameter
#Starting with color red
d = [255,0,0]
#formula for finding green value by percentage
d[1] = int((510*p)/100)
print(d[1])
#if green value more than 255
#set green value 255
#reduce the red value from remaining green value
if d[1]>255:
d[0] -= d[1]-255
d[1] = 255
return d
print(rgb(0))

Categories

Resources