问题

问题

H首先考虑以下示例以进行说明。目标是从点到平面绘制最短的线段BDE。棱柱ABCD.EFGHAB=AD=5\sqrt{2}AE=12。我认为作者选择这些数字很糟糕。

以下是我尝试使用pst-3dplot(过早的 3D 支持)和pst-eucl(仅为 2D 设计)绘制它。这个过程很繁琐,因为许多任务,例如

  • 根据两个现有的 3D 点,按照一定的缩放比例定义一个新的 3D 共线点,
  • 将现有的 3D 点投影到连接两个现有 3D 点的线上,
  • 用倾斜的垂直符号标记直角,

都是事先手动计算的。除此以外,\pstProjection和在 3D 中不起作用\pstRightAnglepst-eucl

这是我做的痛苦部分。看看神奇的精确数字。

\pstHomO[HomCoef=\pscalculate{50/194},PosAngle=-80]{E}{D}[P]
\pstHomO[HomCoef=\pscalculate{25/72},PosAngle=135]{E}{B}[Q]
\pstHomO[HomCoef=\pscalculate{9409/4225},PosAngle=0]{Q}{P}[H']

其他操作例如

  • 将现有的 3D 点投影到通过 3 个现有 3D 点的平面上,
  • 找到两条线之间的交点,每条线都经过两个不同的点,
  • ETC

未来的项目也需要这些。

问题

在这里我想知道哪些 LaTeX 软件包真的可以轻松支持上述 3D 绘图操作。需要重新绘制下面我所做的操作以证明您提出的软件包的有效性。我对 Asymptote、TikZ、Metapost 等不太了解。

我的痛苦尝试

在此处输入图片描述

\documentclass[pstricks,border=0cm,12pt]{standalone}
\usepackage{pst-3dplot,pst-eucl}

\psset{unit=5mm}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% OBJECTIVE
% Draw the shortest line segment 
% from the point H to 
% the plane BDE .
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



\def\pstSlantedRightAngle#1#2#3{%
  \pnodes([nodesep=6pt]{#1}#2){s}([nodesep=6pt]{#3}#2){t}
  \pstTranslation[PointName=none,PointSymbol=none]{#2}{s}{t}[u]
  \psline(s)(u)(t)}
        
\begin{document}
\begin{pspicture}[showgrid=false](-8,-1)(6,15)
    \psset{Alpha=-115,Beta=55}
    
    
    % prism ABCD.EFGH
    \def\A{(5 2 sqrt mul,0,0)}
    \def\B{(5 2 sqrt mul,5 2 sqrt mul,0)}
    \def\C{(0,5 2 sqrt mul,0)}
    \def\D{(0,0,0)}
    \def\E{(5 2 sqrt mul,0,12)}
    \def\F{(5 2 sqrt mul,5 2 sqrt mul,12)}
    \def\G{(0,5 2 sqrt mul,12)}
    \def\H{(0,0,12)}
    
    % hidden lines do not work!
    %\edef\coor{\D\A\C\H}
    %\expandafter\pstThreeDBox\coor
  
    
    \foreach \i in {A,B,...,H}{%
        \edef\coor{\csname\i\endcsname}
        \expandafter\pstThreeDDot\coor
        \expandafter\pstThreeDNode\coor{\i}
    } 
    
    \foreach \i/\j in {0/A,180/B,-135/C,-45/D,45/E,180/F,180/G,115/H}{\uput[\i](\j){$\j$}}
    \pspolygon(C)(D)(A)(E)(F)(G)
    \psline(H)(E)
    \psline(H)(G)
    \psline(H)(D)
    
    \psline[linestyle=dashed](B)(F)
    \psline[linestyle=dashed](B)(C)
    \psline[linestyle=dashed](B)(A)
    
    
    
    % plane EDB
    \pspolygon[fillstyle=solid,fillcolor=yellow,opacity=0.25,linestyle=none,linewidth=0](E)(B)(D)
    \psline[linestyle=dashed,linecolor=red](E)(B)(D)
    \psline[linecolor=red](E)(D)
    
    % the shortest distance from H to EDB
    \pstHomO[HomCoef=\pscalculate{50/194},PosAngle=-80]{E}{D}[P]
    \pstHomO[HomCoef=\pscalculate{25/72},PosAngle=135]{E}{B}[Q]
    \pstHomO[HomCoef=\pscalculate{9409/4225},PosAngle=0]{Q}{P}[H']
    
    \psline[linestyle=dashed,linecolor=green](H)(Q)(P)
    \pspolygon[linecolor=green](P)(H')(H)
    

  % right-angle mark
    \pstSlantedRightAngle{H}{P}{D}
    \pstSlantedRightAngle{E}{P}{Q}
    \pstSlantedRightAngle{H}{H'}{P}
    \pstSlantedRightAngle{H}{E}{Q}  
\end{pspicture}
\end{document}

幕后计算

在此处输入图片描述

在此处输入图片描述

在此处输入图片描述

在此处输入图片描述

我喜欢欧几里得几何!

在此处输入图片描述

在某些情况下,隐藏的线会被错误地渲染!

答案1

这不是一个完整的解决方案。我还没有让隐藏的虚线在您用线段旋转立方体时自动调整。请注意,从点到平面的HY距离为,其中,。非常感谢marmotHBDEsqrt{a^2h^2}{a^2+h^2}a = ABh=AE3dtools

    \documentclass[tikz,border=3mm]{standalone}
\usetikzlibrary{3dtools} % https://github.com/marmotghost/tikz-3dtools
\begin{document}
    \pgfdeclarelayer{background} 
    \pgfdeclarelayer{foreground}
        \pgfsetlayers{background,main,foreground} 
\foreach \Angle in {5,15,...,355} % {5,15,...,355}
    {\begin{tikzpicture}[same bounding box=A,line cap=round,line join=round,declare function={a=5*sqrt(2);h=12;} ]
    \begin{scope}[3d/install view={phi=\Angle,psi=0,theta=70}]
\path
  (a,0,0) coordinate (A)
 (a,a,0) coordinate (B)
 (0,a,0) coordinate (C)
 (0,0,0) coordinate (D)
 (a,0,h) coordinate (E)
 (a,a,h) coordinate (F)
 (0,a,h) coordinate (G)
 (0,0,h)  coordinate (H)
 ({a*h*h/(a*a+2*h*h)}, {-a*h*h/(a*a+2*h*h)}, {2*h*h*h/(a*a+2*h*h)}) coordinate (Y) %using Maple
[3d coordinate={(O)=0.5*(A)+0.5*(G)}];
\foreach \p in {A,B,C,D,E,F,G,H,Y,O}
{\draw[fill=black] (\p) circle (1.2 pt);}
\foreach \p/\g in {A/-90,B/-90,C/-90,D/90,E/90,F/90,G/90,H/90,Y/90,O/-90}
{\path (\p)+(\g:3mm) node{$\p$};}
\tikzset{3d/polyhedron/.cd,fore layer=foreground,back layer=background,
    face edges/.style={},%
    back/.style={3d/hidden,fill=none},
    fore/.style={3d/visible,solid,fill=none,3d/polyhedron/edges have complete dashes=false},
    complete dashes,
    O={(O)},
    draw face with corners={{(A)},{(B)},{(E)}},
    draw face with corners={{(B)},{(E)},{(F)}},
    draw face with corners={{(B)},{(C)},{(G)},{(F)}},
    draw face with corners={{(D)},{(C)},{(G)},{(H)}},
    draw face with corners={{(H)},{(E)},{(D)}},
    draw face with corners={{(A)},{(D)},{(E)}},
    draw face with corners={{(E)},{(F)},{(G)},{(H)}}}
\draw[3d/hidden] (B) --(D);
\draw[3d/visible] (H) -- (Y);
\end{scope}
\end{tikzpicture}}
\end{document}

您可以使用语法来查找平面上3dtools点的投影HBDE

\path[3d/plane through={(E) and (D) and (B) named pEDB}];
\path[3d/project={(H) on pEDB}] coordinate (X);

在此代码中,我将的长度减少AHAH=5

   \documentclass[tikz,border=3mm]{standalone}
\usetikzlibrary{3dtools} % https://github.com/marmotghost/tikz-3dtools
\begin{document}
    \pgfdeclarelayer{background} 
    \pgfdeclarelayer{foreground}
        \pgfsetlayers{background,main,foreground} 
\foreach \Angle in {70} % {5,15,...,355}
    {\begin{tikzpicture}[same bounding box=A,line cap=round,line join=round,declare function={a=5*sqrt(2);h=5;} ]
    \begin{scope}[3d/install view={phi=\Angle,psi=0,theta=70}]
\path
  (a,0,0) coordinate (A)
 (a,a,0) coordinate (B)
 (0,a,0) coordinate (C)
 (0,0,0) coordinate (D)
 (a,0,h) coordinate (E)
 (a,a,h) coordinate (F)
 (0,a,h) coordinate (G)
 (0,0,h)  coordinate (H)
 ({a*h*h/(a*a+2*h*h)}, {-a*h*h/(a*a+2*h*h)}, {2*h*h*h/(a*a+2*h*h)}) coordinate (Y) %using Maple
[3d coordinate={(O)=0.5*(A)+0.5*(G)}];
\path[3d/plane through={(E) and (D) and (B) named pEDB}];
\path[3d/project={(H) on pEDB}] coordinate (X);
\foreach \p in {A,B,C,D,E,F,G,H,Y,O,X}
{\draw[fill=black] (\p) circle (1.2 pt);}
\foreach \p/\g in {A/-90,B/-90,C/-90,D/90,E/90,F/90,G/90,H/90,Y/90,O/-90,X/0}
{\path (\p)+(\g:3mm) node{$\p$};}
\tikzset{3d/polyhedron/.cd,fore layer=foreground,back layer=background,
    face edges/.style={},%
    back/.style={3d/hidden,fill=none},
    fore/.style={3d/visible,solid,fill=none,3d/polyhedron/edges have complete dashes=false},
    complete dashes,
    O={(O)},
    draw face with corners={{(A)},{(B)},{(E)}},
    draw face with corners={{(B)},{(E)},{(F)}},
    draw face with corners={{(B)},{(C)},{(G)},{(F)}},
    draw face with corners={{(D)},{(C)},{(G)},{(H)}},
    draw face with corners={{(H)},{(E)},{(D)}},
    draw face with corners={{(A)},{(D)},{(E)}},
    draw face with corners={{(E)},{(F)},{(G)},{(H)}}}
\draw[3d/hidden] (B) --(D);
\draw[3d/visible] (H) -- (Y);
\end{scope}
\end{tikzpicture}}
\end{document}

这是一个略有不同的解决方案。来自土拨鼠

\documentclass[tikz,border=3mm]{standalone}
\usepackage{tikz-3dplot}
\usetikzlibrary{3dtools}
\begin{document}
    \pgfdeclarelayer{background} 
    \pgfdeclarelayer{foreground}
    \pgfsetlayers{background,main,foreground} 
    \foreach \Angle in {5,15,...,355}
    {\begin{tikzpicture}[same bounding box=A,line cap=round,line join=round,visible/.style={draw,solid}, hidden/.style={draw, dashed}, 3d/install view={phi=\Angle,psi=0,theta=70},declare function={a=5*sqrt(2);h=10;} ]
            \path
            (a/2,-a/2,0) coordinate (A)
            (a/2,a/2,0) coordinate (B)
            (-a/2,a/2,0) coordinate (C)
            (-a/2,-a/2,0) coordinate (D)
            (a/2,-a/2,h) coordinate (E)
            (a/2,a/2,h) coordinate (F)
            (-a/2,a/2,h) coordinate (G)
            (-a/2,-a/2,h)  coordinate (H)
            [3d coordinate={(O)=0.33*(B)+0.33*(D)+0.33*(E)+0.1*a*(nscreenx,nscreeny,nscreenz)}];
            [3d coordinate={(O)=0.5*(A)+0.5*(G)}];
            \path[3d/plane through={(E) and (D) and (B) named pEDB}];
            \path[3d/project={(H) on pEDB}] coordinate (X);
            \foreach \p in {A,B,C,D,E,F,G,H,O,X}
            {\draw[fill=black] (\p) circle (1.2 pt);}
            \foreach \p/\g in {A/-90,B/-90,C/-90,D/90,E/90,F/90,G/90,H/90,O/-90,X/0}
            {\path (\p)+(\g:3mm) node{$\p$};}
            \tikzset{3d/polyhedron/.cd,O={(O)},
                fore layer=foreground,back layer=background,
                back/.style={3d/polyhedron/complete dashes,fill=none},
                fore/.style={3d/visible,fill=none},%3d/polyhedron/edges have complete dashes=false
                draw face with corners={{(A)},{(B)},{(E)}},
                draw face with corners={{(B)},{(E)},{(F)}},
                draw face with corners={{(B)},{(C)},{(G)},{(F)}},
                draw face with corners={{(D)},{(C)},{(G)},{(H)}},
                draw face with corners={{(H)},{(E)},{(D)}},
                draw face with corners={{(A)},{(D)},{(E)}},
                draw face with corners={{(E)},{(F)},{(G)},{(H)}}
            }
            \draw[hidden] (B) -- (D);
            \draw[visible] (H) -- (X);
    \end{tikzpicture}}
\end{document}  

在此处输入图片描述

在此处输入图片描述

在此处输入图片描述

答案2

\documentclass[pstricks,border=0cm,12pt]{standalone}
\usepackage{pst-3dplot,pst-calculate}
\psset{unit=5mm}
\begin{document}

\def\X{5 2 sqrt mul}
\psset{Beta=40,Alpha=65}

\begin{pspicture}[showgrid](-5,-8)(8,10)
\pstThreeDCoor
\pstThreeDBox[hiddenLine](0,0,0)(\X,0,0)(0,\X,0)(0,0,12)
\pstThreeDTriangle[fillcolor=yellow,fillstyle=solid,opacity=0.5,linecolor=red,
    linestyle=dashed](\X,\X,0)(0,\X,12)(0,0,0)
\pstThreeDLine[linecolor=red](\X,\X,0)(0,\X,12)
\pstThreeDNode(0,\X,12){E}\uput[0](E){E}
\pstThreeDNode(\X,\X,12){H}\pstThreeDNode(\X,\X,0){D}\uput[0](D){D}
\psRelNode(E)(0,0){2425 36 div 194 div}{Q}\psdot(Q)
\psRelNode(D)(E){144 194 div}{P}\psdot(P)\uput[0](P){P}
\psline[linestyle=dashed,linecolor=green](H)(Q)(P)
\psline[linecolor=green](H)(P)
\psRelNode(Q)(P){2}{H'}\psdot(H')\psline[linecolor=green](P)(H')(H)
\end{pspicture}
\end{document}

在此处输入图片描述

答案3

虽然它不是 LaTeX 包而是 Three.js,但仅供我们参考。

在此处输入图片描述

console.clear();
import * as THREE from "https://threejs.org/build/three.module.js";
import { OrbitControls } from "https://threejs.org/examples/jsm/controls/OrbitControls.js";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 100);
camera.position.set(-10, 10, 10);
let renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x202020);
document.body.appendChild(renderer.domElement);

window.addEventListener( 'resize', onWindowResize );

let controls = new OrbitControls(camera, renderer.domElement);

let grid = new THREE.GridHelper(10, 10, 0x808080, 0x808080);
grid.position.y = -0.01;
//scene.add(grid);

let box = DashedHiddenEdgesBox(10, 6, 3, "yellow");
box.geometry.translate(0, 2.5, 0);
scene.add(box);

renderer.setAnimationLoop((_) => {
  box.rotation.x+=0.01;
  box.rotation.y+=0.01;
  renderer.render(scene, camera);
});

function DashedHiddenEdgesBox(w, h, d, color) {
  //box base points
  let basePts = [
    [0, 0, 0],[1, 0, 0],[1, 0, 1],[0, 0, 1],
    [0, 1, 0],[1, 1, 0],[1, 1, 1],[0, 1, 1]
  ].map(p => {return new THREE.Vector3(p[0], p[1], p[2])});
  // box sides normals
  let baseNor = [
    [0, 0, -1], [1, 0, 0], [0, 0, 1], [-1, 0, 0], [0, 1, 0], [0, -1, 0] 
  ].map(n => {return new THREE.Vector3(n[0], n[1], n[2])});
  
  let pts = [];
  let n1 = [];
  let n2 = [];
  
  //bottom
  for(let i = 0; i < 4; i++){
    // bottom
    pts.push(basePts[i].clone());
    pts.push(basePts[(i + 1) > 3 ? 0 : (i + 1)].clone());
    n1.push(baseNor[i].x, baseNor[i].y, baseNor[i].z,baseNor[i].x, baseNor[i].y, baseNor[i].z);
    n2.push(baseNor[5].x, baseNor[5].y, baseNor[5].z,baseNor[5].x, baseNor[5].y, baseNor[5].z);
    // top
    pts.push(basePts[4 + i].clone());
    pts.push(basePts[(4 + i + 1) > 7 ? 4 : (4 + i + 1)].clone());
    n1.push(baseNor[i].x, baseNor[i].y, baseNor[i].z,baseNor[i].x, baseNor[i].y, baseNor[i].z);
    n2.push(baseNor[4].x, baseNor[4].y, baseNor[4].z,baseNor[4].x, baseNor[4].y, baseNor[4].z);
    // middle
    pts.push(basePts[i].clone());
    pts.push(basePts[i + 4].clone());
    n1.push(baseNor[i].x, baseNor[i].y, baseNor[i].z,baseNor[i].x, baseNor[i].y, baseNor[i].z);
    let prev = (i - 1) < 0 ? 3 : (i - 1);
    n2.push(baseNor[prev].x, baseNor[prev].y, baseNor[prev].z,baseNor[prev].x, baseNor[prev].y, baseNor[prev].z);
  }
  //console.log(pts)
  
  let g = new THREE.BufferGeometry().setFromPoints(pts);
  g.setAttribute("n1", new THREE.Float32BufferAttribute(n1, 3));
  g.setAttribute("n2", new THREE.Float32BufferAttribute(n2, 3));
  g.translate(-0.5, -0.5, -0.5);
  g.scale(w, h, d);
  let m = new THREE.LineDashedMaterial({
    color: color, 
    dashSize: 0.3, 
    gapSize: 0.14,
    onBeforeCompile: shader => {
      shader.vertexShader = `
        attribute vec3 n1;
        attribute vec3 n2;
        varying float isDashed;
        ${shader.vertexShader}
      `.replace(
        `#include <fog_vertex>`,
        `#include <fog_vertex>
        
          vec3 nor1 = normalize(normalMatrix * n1);
          vec3 nor2 = normalize(normalMatrix * n2);
          vec3 vDir = normalize(mvPosition.xyz);
          //vDir = vec3(0, 0, -1);
          float v1 = step( 0., dot( vDir, nor1 ) );
          float v2 = step( 0., dot( vDir, nor2 ) );
          isDashed = min(v1, v2);
        `
      );
      console.log(shader.vertexShader);
      shader.fragmentShader = `
        varying float isDashed;
        ${shader.fragmentShader}
      `.replace(
        `if ( mod( vLineDistance, totalSize ) > dashSize ) {
        discard;
    }`,
        `
          if ( isDashed > 0.0 ) {
            if ( mod( vLineDistance, totalSize ) > dashSize ) {
              discard;
            }
          }`
      );
      console.log(shader.fragmentShader)
    }
  });
  let l = new THREE.LineSegments(g, m);
  l.computeLineDistances();
  return l;
}

function onWindowResize() {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(innerWidth, innerHeight);
}

相关内容