From 9bbbc13ce25dab109f640f469d1a633ae5024932 Mon Sep 17 00:00:00 2001 From: rodri Date: Thu, 30 May 2024 21:11:33 +0000 Subject: [PATCH] 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. --- sys/include/geometry.h | 2 + sys/man/2/geometry | 201 ++++++++++++----------------------- sys/src/libgeometry/rframe.c | 103 +++++------------- 3 files changed, 99 insertions(+), 207 deletions(-) diff --git a/sys/include/geometry.h b/sys/include/geometry.h index 74fe938f2..cf0246d88 100644 --- a/sys/include/geometry.h +++ b/sys/include/geometry.h @@ -124,6 +124,8 @@ Quaternion slerp(Quaternion, Quaternion, double); Point3 qrotate(Point3, Point3, double); /* RFrame */ +void rframematrix(Matrix, RFrame); +void rframematrix3(Matrix3, RFrame3); Point2 rframexform(Point2, RFrame); Point3 rframexform3(Point3, RFrame3); Point2 invrframexform(Point2, RFrame); diff --git a/sys/man/2/geometry b/sys/man/2/geometry index 0afd33713..836495eaa 100644 --- a/sys/man/2/geometry +++ b/sys/man/2/geometry @@ -1,6 +1,6 @@ .TH GEOMETRY 2 .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 .de PB .PP @@ -135,6 +135,8 @@ Quaternion slerp(Quaternion q, Quaternion r, double t); Point3 qrotate(Point3 p, Point3 axis, double θ); .PB /* RFrame */ +void rframematrix(Matrix, RFrame); +void rframematrix3(Matrix3, RFrame3); Point2 rframexform(Point2 p, RFrame rf); Point3 rframexform3(Point3 p, RFrame3 rf); Point2 invrframexform(Point2 p, RFrame rf); @@ -615,7 +617,7 @@ by .I θ radians. .SS Frames of reference -A frame of reference in a +A frame of reference (or rframe) in a .IR n -dimensional space is made out of n+1 points, one being the origin .IR p , @@ -623,33 +625,33 @@ and the remaining being the basis vectors .I b1,⋯,bn 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 Name Description .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) Transforms the point .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 . It then returns the new 2D point. .TP .B rframexform3(\fIp\fP,\fIrf\fP) Transforms the point .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 . It then returns the new 3D point. .TP @@ -658,7 +660,7 @@ Transforms the point .IR p , relative to .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. .TP .B invrframexform3(\fIp\fP,\fIrf\fP) @@ -666,7 +668,7 @@ Transforms the point .IR p , relative to .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. .SS Triangles .TP @@ -689,7 +691,53 @@ relative to the triangle Returns the geometric center of .B Triangle3 .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 .B RFrame 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 grow downwards (see .IR graphics (2)). -.PP +.IP .EX -#include -#include -#include -#include - RFrame worldrf; /* from screen... */ @@ -733,112 +776,6 @@ main(void) worldrf.by = Vec2(0,-1); ⋯ .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 .B /sys/src/libgeometry .SH SEE ALSO @@ -857,6 +794,8 @@ Jonathan Blow, The Inner Product, April 2004. .br https://www.3dgep.com/understanding-quaternions/ +.br +https://motion.cs.illinois.edu/RoboticSystems/CoordinateTransformations.html .SH BUGS No care is taken to avoid numeric overflows. .SH HISTORY diff --git a/sys/src/libgeometry/rframe.c b/sys/src/libgeometry/rframe.c index aee3c03dc..ab77d6cba 100644 --- a/sys/src/libgeometry/rframe.c +++ b/sys/src/libgeometry/rframe.c @@ -2,106 +2,57 @@ #include #include -/* - * implicit identity origin rframes - * - * 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) +void +rframematrix(Matrix m, RFrame rf) { - p.w--; - return p; + m[0][0] = rf.bx.x; m[0][1] = rf.by.x; m[0][2] = rf.p.x; + 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 -_irfxform⁻¹(Point2 p) +void +rframematrix3(Matrix3 m, RFrame3 rf) { - p.w++; - return p; -} - -static Point3 -_irfxform3(Point3 p) -{ - p.w--; - return p; -} - -static Point3 -_irfxform3⁻¹(Point3 p) -{ - p.w++; - return p; + m[0][0] = rf.bx.x; m[0][1] = rf.by.x; m[0][2] = rf.bz.x; m[0][3] = rf.p.x; + 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; } Point2 rframexform(Point2 p, RFrame rf) { - Matrix m = { - rf.bx.x, rf.by.x, 0, - rf.bx.y, rf.by.y, 0, - 0, 0, 1, - }; + Matrix m; + + rframematrix(m, rf); invm(m); - return xform(subpt2(_irfxform⁻¹(p), rf.p), m); + return xform(p, m); } Point3 rframexform3(Point3 p, RFrame3 rf) { - Matrix3 m = { - rf.bx.x, rf.by.x, rf.bz.x, 0, - rf.bx.y, rf.by.y, rf.bz.y, 0, - rf.bx.z, rf.by.z, rf.bz.z, 0, - 0, 0, 0, 1, - }; + Matrix3 m; + + rframematrix3(m, rf); invm3(m); - return xform3(subpt3(_irfxform3⁻¹(p), rf.p), m); + return xform3(p, m); } Point2 invrframexform(Point2 p, RFrame rf) { - Matrix m = { - rf.bx.x, rf.by.x, 0, - rf.bx.y, rf.by.y, 0, - 0, 0, 1, - }; - return _irfxform(addpt2(xform(p, m), rf.p)); + Matrix m; + + rframematrix(m, rf); + return xform(p, m); } Point3 invrframexform3(Point3 p, RFrame3 rf) { - Matrix3 m = { - rf.bx.x, rf.by.x, rf.bz.x, 0, - rf.bx.y, rf.by.y, rf.bz.y, 0, - rf.bx.z, rf.by.z, rf.bz.z, 0, - 0, 0, 0, 1, - }; - return _irfxform3(addpt3(xform3(p, m), rf.p)); + Matrix3 m; + + rframematrix3(m, rf); + return xform3(p, m); }