2

In this old answer of mine, I used Asymptote to diagram numerous intersecting planes bound by a cube: https://tex.stackexchange.com/a/733960/319072.

I want to be able to do this in TikZ or MetaPost. There are solutions for two or three intersecting planes, but I haven't found one which handles more than three.

That is the goal of this question. How can you draw more than three intersecting planes which are bounded by a cube, in a 2D software?

(The real goal of this question is to share this achievement, and to inspire others in the community to take a stab at this, possibly with different techniques, hence my self-answer.)

MWE:

unitsize(1cm); import three; import graph3; // Function to sort points based on the angle with the centroid triple[] sort_points(triple[] points) { triple centroid = (0, 0, 0); for (int i = 0; i < points.length; ++i) { centroid += points[i]; } centroid /= points.length; real[] angles; for (int i = 0; i < points.length; ++i) { angles.push(atan2(points[i].y - centroid.y, points[i].x - centroid.x)); } for (int i = 0; i < points.length; ++i) { for (int j = i + 1; j < points.length; ++j) { if (angles[i] > angles[j]) { real temp_angle = angles[i]; angles[i] = angles[j]; angles[j] = temp_angle; triple temp_point = points[i]; points[i] = points[j]; points[j] = temp_point; } } } return points; } void draw_plane( triple normal_vector ,triple point_on_plane ,real xmin = -5 ,real xmax = 5 ,real ymin = -5 ,real ymax = 5 ,real zmin = -5 ,real zmax = 5 ,pen thecolor = white ,real theopacity = 0.3 ,bool drawthebox = false ) { if (drawthebox) { draw(box((xmin,ymin,zmin), (xmax,ymax,zmax))); } real a = normal_vector.x; real b = normal_vector.y; real c = normal_vector.z; real d = dot(normal_vector, point_on_plane); real x(real y, real z) { return (((d)-((b)*(y))-((c)*(z)))/(a)); } real y(real x, real z) { return (((d)-((a)*(x))-((c)*(z)))/(b)); } real z(real x, real y) { return (((d)-((a)*(x))-((b)*(y)))/(c)); } if (a==0 && b==0 && c==0) { draw( surface( (xmin,ymin,zmin) -- (xmax,ymin,zmin) -- (xmax,ymax,zmin) -- (xmin,ymax,zmin) -- cycle ) ,thecolor+opacity(theopacity) ); draw( surface( (xmin,ymin,zmax) -- (xmax,ymin,zmax) -- (xmax,ymax,zmax) -- (xmin,ymax,zmax) -- cycle ) ,thecolor+opacity(theopacity) ); draw( surface( (xmin,ymin,zmin) -- (xmax,ymin,zmin) -- (xmax,ymin,zmax) -- (xmin,ymin,zmax) -- cycle ) ,thecolor+opacity(theopacity) ); draw( surface( (xmin,ymax,zmin) -- (xmax,ymax,zmin) -- (xmax,ymax,zmax) -- (xmin,ymax,zmax) -- cycle ) ,thecolor+opacity(theopacity) ); draw( surface( (xmin,ymin,zmin) -- (xmin,ymax,zmin) -- (xmin,ymax,zmax) -- (xmin,ymin,zmax) -- cycle ) ,thecolor+opacity(theopacity) ); draw( surface( (xmax,ymin,zmin) -- (xmax,ymax,zmin) -- (xmax,ymax,zmax) -- (xmax,ymin,zmax) -- cycle ) ,thecolor+opacity(theopacity) ); } // yz planes if (a!=0 && b==0 &&c==0) { if (xmin<=d/a && d/a<=xmax) { draw( surface( (x(ymin,zmin),ymin,zmin) -- (x(ymax,zmin),ymax,zmin) -- (x(ymax,zmax),ymax,zmax) -- (x(ymin,zmax),ymin,zmax) -- cycle ) ,thecolor+opacity(theopacity) ); } } // xz planes if (a==0 & b!=0 & c==0) { if (ymin<=d/b && d/b<=ymax) { draw( surface( (xmin,y(xmin,zmin),zmin) -- (xmax,y(xmax,zmin),zmin) -- (xmax,y(xmax,zmax),zmax) -- (xmin,y(xmin,zmax),zmax) -- cycle ) ,thecolor+opacity(theopacity) ); } } // xy planes if (a==0 && b==0 & c!=0) { if (zmin<=d/c && d/c<=zmax) { draw( surface( (xmin,ymin,z(xmin,ymin)) -- (xmax,ymin,z(xmax,ymin)) -- (xmax,ymax,z(xmax,ymax)) -- (xmin,ymax,z(xmin,ymax)) -- cycle ) ,thecolor+opacity(theopacity) ); } } // y of x planes (invariant over z) if (a!=0 && b!=0 && c==0) { real xylower = xmin; if (ymax < y(xmin,0)) { xylower = x(ymax,0); } if (y(xmin,0) < ymin) { xylower = x(ymin,0); } real xyupper = xmax; if (ymax < y(xmax,0)) { xyupper = x(ymax,0); } if (y(xmax,0) < ymin) { xylower = x(ymin,0); } if (!(xylower<xmin || xmax<xylower) && !(xyupper<xmin || xmax<xyupper)) { draw( surface( (xylower,y(xylower,0),zmin) -- (xyupper,y(xyupper,0),zmin) -- (xyupper,y(xyupper,0),zmax) -- (xylower,y(xylower,0),zmax) -- cycle ) ,thecolor+opacity(theopacity) ); } } // z of x planes (invariant over y) if (a!=0 && b==0 && c!=0) { real xzlower = xmin; if (z(xmin,0)<zmin) { xzlower = x(0,zmin); } if (zmax<z(xmin,0)) { xzlower = x(0,zmax); } real xzupper = xmax; if (z(xmax,0)<zmin) { xzupper = x(0,zmin); } if (zmax<z(xmax,0)) { xzupper = x(0,zmax); } if (!(xzlower<xmin || xzlower>xmax) && !(xzupper<xmin || xzupper>xmax)) { draw( surface( (xzlower,ymin,z(xzlower,0)) -- (xzupper,ymin,z(xzupper,0)) -- (xzupper,ymax,z(xzupper,0)) -- (xzlower,ymax,z(xzlower,0)) -- cycle ) ,thecolor+opacity(theopacity) ); } } // z of y planes (invariant over x) if (a==0 && b!=0 && c!=0) { real yzlower = ymin; if (z(0,ymin)<zmin) { yzlower = y(0,zmin); } if (zmax<z(0,ymin)) { yzlower = y(0,zmax); } real yzupper = ymax; if (z(0,ymax)<zmin) { yzupper = y(0,zmin); } if (zmax<z(0,ymax)) { yzupper = y(0,zmax); } if (!(yzlower<ymin || yzlower>ymax) && !(yzupper<ymin || yzupper>ymax)) { draw( surface( (xmin,yzlower,z(0,yzlower)) -- (xmin,yzupper,z(0,yzupper)) -- (xmax,yzupper,z(0,yzupper)) -- (xmax,yzlower,z(0,yzlower)) -- cycle ) ,thecolor+opacity(theopacity) ); } } // Intersection points array triple[] intersections; // Calculate intersections with x, y, z bounds void add_intersection(real x, real y, real z) { if (xmin <= x && x <= xmax && ymin <= y && y <= ymax && zmin <= z && z <= zmax) { intersections.push((x, y, z)); } } // Add intersections with the six planes of the rectangular prism for (real y = ymin; y <= ymax; y += ymax - ymin) { for (real z = zmin; z <= zmax; z += zmax - zmin) { add_intersection(x(y, z), y, z); } } for (real x = xmin; x <= xmax; x += xmax - xmin) { for (real z = zmin; z <= zmax; z += zmax - zmin) { add_intersection(x, y(x, z), z); } } for (real x = xmin; x <= xmax; x += xmax - xmin) { for (real y = ymin; y <= ymax; y += ymax - ymin) { add_intersection(x, y, z(x, y)); } } // Sort intersections intersections = sort_points(intersections); // Initialize plane_outline with the first intersection point path3 plane_outline; if (intersections.length > 0) { plane_outline = (intersections[0]); } // Convert intersections to a path for (int i = 1; i < intersections.length; ++i) { plane_outline = plane_outline -- intersections[i]; } // Close the path if we have enough points if (intersections.length >= 3) { plane_outline = plane_outline -- cycle; } // Draw the outline of the clipped plane if (intersections.length >= 3) { draw(surface(plane_outline), thecolor + opacity(theopacity)); } } void draw_plane_test() { // (a==0 && b==0 && c==0) // draw_plane( // (0,0,0) // ,(0,0,0) // ,thecolor=blue // ,theopacity=0.1 // ,drawthebox=true // ); // (a!=0 && b==0 && c==0) // draw_plane( // (-2,0,0) // ,(-1,-1,-1) // ,thecolor=red // ); // draw_plane( // (3,0,0) // ,(3,2,1) // ,thecolor=red // ); // (a==0 && b!=0 && c==0) // draw_plane( // (0,2,0) // ,(2,-2,2) // ,thecolor=blue // ); // draw_plane( // (0,-3,0) // ,(1,5,2) // ,thecolor=blue // ); // (a==0 && b==0 && c!=0) // draw_plane( // (0,0,1) // ,(2,-2,2) // ,thecolor=green // ); // draw_plane( // (0,0,-2) // ,(1,5,1) // ,thecolor=green // ); // (a!=0 && b!=0 && c==0) // draw_plane( // (1,2,0) // ,(3,3,3) // ,thecolor=red // ,drawthebox=true // ); // draw_plane( // (-1,2,0) // ,(3,3,3) // ,thecolor=red // ); // draw_plane( // (20,-3,0) // ,(-3,-2,-4) // ,thecolor=red // ); // (a!=0 && b==0 && c!=0) // draw_plane( // (-2,0,3) // ,(0,0,0) // ,thecolor=green // ,drawthebox=true // ); // draw_plane( // (-4,0,-3) // ,(2,2,2) // ,thecolor=green // ,drawthebox=true // ); // draw_plane( // (4,0,30) // ,(-2,3,-2) // ,thecolor=green // ,drawthebox=true // ); // (a==0 && b!=0 && c!=0) // draw_plane( // (0,-2,3) // ,(0,0,0) // ,thecolor=blue // ,drawthebox=true // ); // draw_plane( // (0,-4,-3) // ,(2,2,2) // ,thecolor=blue // ,drawthebox=true // ); // draw_plane( // (0,4,30) // ,(3,-2,-2) // ,thecolor=blue // ,drawthebox=true // ); // (a!=0 && b!=0 && c!=0) draw_plane( (2,2,2) ,(-3,4,1) ,thecolor=red ,drawthebox=true ); draw_plane( (-2,-3,1) ,(-3,4,1) ,thecolor=green ); draw_plane( (-2,-3,1) ,(3,3,-1) ,thecolor=blue ); } draw_plane_test(); 

output

1 Answer 1

4

No software is without its flaws, but I have crafted one which can do this for TikZ in many cases. I wanted to share it to inspire others to take a stab at this themselves. I think it is a fascinating topic.

It is currently available only on GitHub, but when I am prepared, I should upload it to CTAN.

\documentclass[tikz,border=1cm]{standalone} \usepackage{lua-tikz3dtools} % https://github.com/Pseudonym321/TikZ-Animations/tree/master1/TikZ/lua-tikz3dtools \begin{document} \foreach \i[parse = true] in {1} { \pgfmathsetmacro{\i}{pi/3} \begin{tikzpicture} \useasboundingbox[scale=2] (-1,-1) rectangle (1,1); \setobject[ name = {T} ,object = { matrix_multiply( matrix_multiply( euler(pi/2,pi/3,3.5*pi/6) ,translate(0,0,-5) ) ,matrix_multiply( { {1,0,0,0} ,{0,1,0,0} ,{0,0,1,0} ,{0,0,0,1} } ,matrix_multiply(xscale(1),yscale(1)) ) ) } ] \setobject[ name = {I} ,object = { matrix_inverse(T) } ] \foreach \j in {2,3,1,0,4}{ \appendsurface[ ustart = {-2} ,ustop = {2} ,usamples = {2} ,vstart = {-2} ,vstop = {2} ,vsamples = {2} ,transformation = {matrix_multiply(euler(\j+\i,2*\j,\j),T)} ,x = {u} ,y = {v} ,z = { \j/10 } ,fill options = { preaction = { \ifnum\j=0 fill = red \fi \ifnum\j=2 fill = green \fi \ifnum\j=1 fill = yellow \fi \ifnum\j=3 fill = blue \fi \ifnum\j=4 fill = orange \fi ,fill opacity = 0.6 } ,postaction = { draw = none ,ultra thin ,line join = round ,line cap = round } } ,filter = { abs(matrix_multiply(A,I)[1][3])<1.01 and abs(matrix_multiply(B,I)[1][3])<1.01 and abs(matrix_multiply(C,I)[1][3])<1.01 and abs(matrix_multiply(A,I)[1][2])<1.01 and abs(matrix_multiply(B,I)[1][2])<1.01 and abs(matrix_multiply(C,I)[1][2])<1.01 and abs(matrix_multiply(A,I)[1][1])<1.01 and abs(matrix_multiply(B,I)[1][1])<1.01 and abs(matrix_multiply(C,I)[1][1])<1.01 } ] } \appendsolid[ ustart = {-1} ,ustop = {1} ,usamples = {2} ,vstart = {-1} ,vstop = {1} ,vsamples = {2} ,wstart = {-1} ,wstop = {1} ,wsamples = {2} ,transformation = {T} ,x = {u} ,y = {v} ,z = {w} ,fill options = { preaction = { fill = gray ,fill opacity = 0.5 } ,postaction = { draw = none ,ultra thin ,line join = round ,line cap = round } } ] \appendcurve[ ustart = {-1} ,ustop = {1.2} ,usamples = {2} ,x = {-1} ,y = {-1} ,z = {u} ,transformation = {T} ,arrow tip = {true} ,draw options = { draw ,ultra thin ,line cap = round } ] \appendlabel[ x = {-1} ,y = {-1} ,z = {1.5} ,transformation = {T} ,name = {\(z\)} ] \appendcurve[ ustart = {-1} ,ustop = {1.2} ,usamples = {2} ,x = {-1} ,y = {u} ,z = {-1} ,transformation = {T} ,arrow tip = {true} ,draw options = { draw ,ultra thin ,line cap = round } ] \appendlabel[ x = {-1} ,y = {1.5} ,z = {-1} ,transformation = {T} ,name = {\(y\)} ] \appendcurve[ ustart = {-1} ,ustop = {1.2} ,usamples = {2} ,x = {u} ,y = {-1} ,z = {-1} ,transformation = {T} ,arrow tip = {true} ,draw options = { draw ,ultra thin ,line cap = round } ] \appendlabel[ x = {1.5} ,y = {-1} ,z = {-1} ,transformation = {T} ,name = {\(x\)} ] \displaysegments \end{tikzpicture} } \end{document} 

output

1
  • 1
    Congratulations on your efforts! Commented 15 hours ago

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.