libgeometry: simplify rframes

i got rid of redundant code, and added routines
to get the xform matrix out of an rframe, so it
can be stored and used separately or as part of
a composition of xforms.

also replaced the big example in the man page for
more concise, useful ones.
This commit is contained in:
rodri 2024-05-30 21:11:33 +00:00
parent 33572bd895
commit 9bbbc13ce2
3 changed files with 99 additions and 207 deletions

View file

@ -124,6 +124,8 @@ Quaternion slerp(Quaternion, Quaternion, double);
Point3 qrotate(Point3, Point3, double); Point3 qrotate(Point3, Point3, double);
/* RFrame */ /* RFrame */
void rframematrix(Matrix, RFrame);
void rframematrix3(Matrix3, RFrame3);
Point2 rframexform(Point2, RFrame); Point2 rframexform(Point2, RFrame);
Point3 rframexform3(Point3, RFrame3); Point3 rframexform3(Point3, RFrame3);
Point2 invrframexform(Point2, RFrame); Point2 invrframexform(Point2, RFrame);

View file

@ -1,6 +1,6 @@
.TH GEOMETRY 2 .TH GEOMETRY 2
.SH NAME .SH NAME
Flerp, fberp, fclamp, Pt2, Vec2, addpt2, subpt2, mulpt2, divpt2, lerp2, berp2, dotvec2, vec2len, normvec2, edgeptcmp, ptinpoly, Pt3, Vec3, addpt3, subpt3, mulpt3, divpt3, lerp3, berp3, dotvec3, crossvec3, vec3len, normvec3, identity, addm, subm, mulm, smulm, transposem, detm, tracem, minorm, cofactorm, adjm, invm, xform, identity3, addm3, subm3, mulm3, smulm3, transposem3, detm3, tracem3, minorm3, cofactorm3, adjm3, invm3, xform3, Quat, Quatvec, addq, subq, mulq, smulq, sdivq, dotq, invq, qlen, normq, slerp, qrotate, rframexform, rframexform3, invrframexform, invrframexform3, centroid, barycoords, centroid3, vfmt, Vfmt, GEOMfmtinstall \- computational geometry library Flerp, fberp, fclamp, Pt2, Vec2, addpt2, subpt2, mulpt2, divpt2, lerp2, berp2, dotvec2, vec2len, normvec2, edgeptcmp, ptinpoly, Pt3, Vec3, addpt3, subpt3, mulpt3, divpt3, lerp3, berp3, dotvec3, crossvec3, vec3len, normvec3, identity, addm, subm, mulm, smulm, transposem, detm, tracem, minorm, cofactorm, adjm, invm, xform, identity3, addm3, subm3, mulm3, smulm3, transposem3, detm3, tracem3, minorm3, cofactorm3, adjm3, invm3, xform3, Quat, Quatvec, addq, subq, mulq, smulq, sdivq, dotq, invq, qlen, normq, slerp, qrotate, rframematrix, rframematrix3, rframexform, rframexform3, invrframexform, invrframexform3, centroid, barycoords, centroid3, vfmt, Vfmt, GEOMfmtinstall \- computational geometry library
.SH SYNOPSIS .SH SYNOPSIS
.de PB .de PB
.PP .PP
@ -135,6 +135,8 @@ Quaternion slerp(Quaternion q, Quaternion r, double t);
Point3 qrotate(Point3 p, Point3 axis, double θ); Point3 qrotate(Point3 p, Point3 axis, double θ);
.PB .PB
/* RFrame */ /* RFrame */
void rframematrix(Matrix, RFrame);
void rframematrix3(Matrix3, RFrame3);
Point2 rframexform(Point2 p, RFrame rf); Point2 rframexform(Point2 p, RFrame rf);
Point3 rframexform3(Point3 p, RFrame3 rf); Point3 rframexform3(Point3 p, RFrame3 rf);
Point2 invrframexform(Point2 p, RFrame rf); Point2 invrframexform(Point2 p, RFrame rf);
@ -615,7 +617,7 @@ by
.I θ .I θ
radians. radians.
.SS Frames of reference .SS Frames of reference
A frame of reference in a A frame of reference (or rframe) in a
.IR n -dimensional .IR n -dimensional
space is made out of n+1 points, one being the origin space is made out of n+1 points, one being the origin
.IR p , .IR p ,
@ -623,33 +625,33 @@ and the remaining being the
basis vectors basis vectors
.I b1,⋯,bn .I b1,⋯,bn
that define the metric within that frame. that define the metric within that frame.
.PP
The origin point and the bases are all defined in terms of an origin
frame of reference O. Applying a forward transformation
.RI ( rframexform
and
.IR rframexform3 )
to a point relative to O will result in a point relative to the new frame.
Applying an inverse transformation
.RI ( invrframexform
and
.IR invrframexform3 )
to that same point—now defined in terms of the new frame—will bring it back to O.
.TP .TP
Name Name
Description Description
.TP .TP
.B rframematrix(\fIm\fP,\fIrf\fP)
Initializes
.I m
as an rframe transformation matrix based on
.IR rf .
.TP
.B rframematrix3(\fIm\fP,\fIrf\fP)
Initializes
.I m
as an rframe transformation matrix based on
.IR rf .
.TP
.B rframexform(\fIp\fP,\fIrf\fP) .B rframexform(\fIp\fP,\fIrf\fP)
Transforms the point Transforms the point
.IR p , .IR p ,
relative to some origin frame of reference O, into the frame of reference relative to some origin frame of reference O, into the rframe
.IR rf . .IR rf .
It then returns the new 2D point. It then returns the new 2D point.
.TP .TP
.B rframexform3(\fIp\fP,\fIrf\fP) .B rframexform3(\fIp\fP,\fIrf\fP)
Transforms the point Transforms the point
.IR p , .IR p ,
relative to some origin frame of reference O, into the frame of reference relative to some origin frame of reference O, into the rframe
.IR rf . .IR rf .
It then returns the new 3D point. It then returns the new 3D point.
.TP .TP
@ -658,7 +660,7 @@ Transforms the point
.IR p , .IR p ,
relative to relative to
.IR rf , .IR rf ,
into a point relative to the origin frame of reference O. into a point relative to the origin rframe O.
It then returns the new 2D point. It then returns the new 2D point.
.TP .TP
.B invrframexform3(\fIp\fP,\fIrf\fP) .B invrframexform3(\fIp\fP,\fIrf\fP)
@ -666,7 +668,7 @@ Transforms the point
.IR p , .IR p ,
relative to relative to
.IR rf , .IR rf ,
into a point relative to the origin frame of reference O. into a point relative to the origin rframe O.
It then returns the new 3D point. It then returns the new 3D point.
.SS Triangles .SS Triangles
.TP .TP
@ -689,7 +691,53 @@ relative to the triangle
Returns the geometric center of Returns the geometric center of
.B Triangle3 .B Triangle3
.IR t . .IR t .
.SH EXAMPLE .SH EXAMPLES
.PP
Rotate a point p by θ, scale it 2x, then translate it by vector v:
.IP
.EX
Matrix R = {
cos(θ), -sin(θ), 0,
sin(θ), cos(θ), 0,
0, 0, 1,
}, S = {
2, 0, 0,
0, 2, 0,
0, 0, 1,
}, T = {
1, 0, v.x,
0, 1, v.y,
0, 0, 1,
};
mulm(S, R);
mulm(T, S);
p = xform(p, T); /* p' = T·S·R·p */
.EE
.PP
Given a space with two observers, A and B, and a point p relative to
A, find its location relative to B:
.IP
.EX
pb = rframexform(invrframexform(p, A), B);
.EE
.PP
Now let's say observer C is located relative to A; find the point's
location in C:
.IP
.EX
pc = rframexform(p, C);
.EE
.PP
Finally, to obtain its location relative to the space itself (its
global position):
.IP
.EX
pg = invrframexform(p, A);
.EE
.PP
The following is a common example of an The following is a common example of an
.B RFrame .B RFrame
being used to define the coordinate system of a being used to define the coordinate system of a
@ -701,13 +749,8 @@ pointing upwards, to contrast with the window system where
.IR y -values .IR y -values
grow downwards (see grow downwards (see
.IR graphics (2)). .IR graphics (2)).
.PP .IP
.EX .EX
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <geometry.h>
RFrame worldrf; RFrame worldrf;
/* from screen... */ /* from screen... */
@ -733,112 +776,6 @@ main(void)
worldrf.by = Vec2(0,-1); worldrf.by = Vec2(0,-1);
.EE .EE
.PP
The following snippet shows how to use the
.B RFrame
declared earlier to locate and draw a ship based on its orientation,
for which we use matrix translation
.B T
and rotation
.BR R
transformations.
.PP
.EX
typedef struct Ship Ship;
typedef struct Shipmdl Shipmdl;
struct Ship
{
RFrame;
double θ; /* orientation (yaw) */
Shipmdl mdl;
};
struct Shipmdl
{
Point2 pts[3]; /* a free-form triangle */
};
Ship *ship;
void
redraw(void)
{
int i;
Point pts[3+1];
Point2 *p;
Matrix T = {
1, 0, ship->p.x,
0, 1, ship->p.y,
0, 0, 1,
}, R = {
cos(ship->θ), -sin(ship->θ), 0,
sin(ship->θ), cos(ship->θ), 0,
0, 0, 1,
};
mulm(T, R); /* rotate, then translate */
p = ship->mdl.pts;
for(i = 0; i < nelem(pts)-1; i++)
pts[i] = fromworld(xform(p[i], T));
pts[i] = pts[0];
draw(screen, screen->r, display->white, nil, ZP);
poly(screen, pts, nelem(pts), 0, 0, 0, display->black, ZP);
}
main(void)
ship = malloc(sizeof(Ship));
ship->p = Pt2(0,0,1); /* place it at the origin */
ship->θ = 45*DEG; /* counter-clockwise */
ship->mdl.pts[0] = Pt2( 10, 0,1);
ship->mdl.pts[1] = Pt2(-10, 5,1);
ship->mdl.pts[2] = Pt2(-10,-5,1);
redraw();
.EE
.PP
Notice how we could've used the
.B RFrame
embedded in the
.B ship
to transform the
.B Shipmdl
into the window. Instead of applying the matrices to every point, the
ship's local frame of reference can be rotated, effectively changing
the model coordinates after an
.IR invrframexform .
We are also getting rid of the
.B θ
variable, since it's no longer needed.
.PP
.EX
struct Ship
{
RFrame;
Shipmdl mdl;
};
redraw(void)
pts[i] = fromworld(invrframexform(p[i], *ship));
main(void)
Matrix R = {
cos(45*DEG), -sin(45*DEG), 0,
sin(45*DEG), cos(45*DEG), 0,
0, 0, 1,
};
//ship->θ = 45*DEG; /* counter-clockwise */
ship->bx = xform(ship->bx, R);
ship->by = xform(ship->by, R);
.EE
.SH SOURCE .SH SOURCE
.B /sys/src/libgeometry .B /sys/src/libgeometry
.SH SEE ALSO .SH SEE ALSO
@ -857,6 +794,8 @@ Jonathan Blow,
The Inner Product, April 2004. The Inner Product, April 2004.
.br .br
https://www.3dgep.com/understanding-quaternions/ https://www.3dgep.com/understanding-quaternions/
.br
https://motion.cs.illinois.edu/RoboticSystems/CoordinateTransformations.html
.SH BUGS .SH BUGS
No care is taken to avoid numeric overflows. No care is taken to avoid numeric overflows.
.SH HISTORY .SH HISTORY

View file

@ -2,106 +2,57 @@
#include <libc.h> #include <libc.h>
#include <geometry.h> #include <geometry.h>
/* void
* implicit identity origin rframes rframematrix(Matrix m, RFrame rf)
*
* static RFrame IRF2 = {
* .p = {0,0,1},
* .bx = {1,0,0},
* .by = {0,1,0},
* };
*
* static RFrame3 IRF3 = {
* .p = {0,0,0,1},
* .bx = {1,0,0,0},
* .by = {0,1,0,0},
* .bz = {0,0,1,0},
* };
*
* these rframes are used on every xform to keep the points in the
* correct plane (i.e. with proper w values); they are written here as a
* reference for future changes. the bases are ignored since they turn
* into an unnecessary identity xform.
*
* the implicitness comes from the fact that using the _irf* filters
* makes the rframexform equivalent to:
* rframexform(invrframexform(p, IRF), rf);
* and the invrframexform to:
* rframexform(invrframexform(p, rf), IRF);
*/
static Point2
_irfxform(Point2 p)
{ {
p.w--; m[0][0] = rf.bx.x; m[0][1] = rf.by.x; m[0][2] = rf.p.x;
return p; m[1][0] = rf.bx.y; m[1][1] = rf.by.y; m[1][2] = rf.p.y;
m[2][0] = 0; m[2][1] = 0; m[2][2] = 1;
} }
static Point2 void
_irfxform¹(Point2 p) rframematrix3(Matrix3 m, RFrame3 rf)
{ {
p.w++; m[0][0] = rf.bx.x; m[0][1] = rf.by.x; m[0][2] = rf.bz.x; m[0][3] = rf.p.x;
return p; m[1][0] = rf.bx.y; m[1][1] = rf.by.y; m[1][2] = rf.bz.y; m[1][3] = rf.p.y;
} m[2][0] = rf.bx.z; m[2][1] = rf.by.z; m[2][2] = rf.bz.z; m[2][3] = rf.p.z;
m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1;
static Point3
_irfxform3(Point3 p)
{
p.w--;
return p;
}
static Point3
_irfxform3¹(Point3 p)
{
p.w++;
return p;
} }
Point2 Point2
rframexform(Point2 p, RFrame rf) rframexform(Point2 p, RFrame rf)
{ {
Matrix m = { Matrix m;
rf.bx.x, rf.by.x, 0,
rf.bx.y, rf.by.y, 0, rframematrix(m, rf);
0, 0, 1,
};
invm(m); invm(m);
return xform(subpt2(_irfxform¹(p), rf.p), m); return xform(p, m);
} }
Point3 Point3
rframexform3(Point3 p, RFrame3 rf) rframexform3(Point3 p, RFrame3 rf)
{ {
Matrix3 m = { Matrix3 m;
rf.bx.x, rf.by.x, rf.bz.x, 0,
rf.bx.y, rf.by.y, rf.bz.y, 0, rframematrix3(m, rf);
rf.bx.z, rf.by.z, rf.bz.z, 0,
0, 0, 0, 1,
};
invm3(m); invm3(m);
return xform3(subpt3(_irfxform3¹(p), rf.p), m); return xform3(p, m);
} }
Point2 Point2
invrframexform(Point2 p, RFrame rf) invrframexform(Point2 p, RFrame rf)
{ {
Matrix m = { Matrix m;
rf.bx.x, rf.by.x, 0,
rf.bx.y, rf.by.y, 0, rframematrix(m, rf);
0, 0, 1, return xform(p, m);
};
return _irfxform(addpt2(xform(p, m), rf.p));
} }
Point3 Point3
invrframexform3(Point3 p, RFrame3 rf) invrframexform3(Point3 p, RFrame3 rf)
{ {
Matrix3 m = { Matrix3 m;
rf.bx.x, rf.by.x, rf.bz.x, 0,
rf.bx.y, rf.by.y, rf.bz.y, 0, rframematrix3(m, rf);
rf.bx.z, rf.by.z, rf.bz.z, 0, return xform3(p, m);
0, 0, 0, 1,
};
return _irfxform3(addpt3(xform3(p, m), rf.p));
} }