H
首先考虑以下示例以进行说明。目标是从点到平面绘制最短的线段BDE
。棱柱ABCD.EFGH
有AB=AD=5\sqrt{2}
和AE=12
。我认为作者选择这些数字很糟糕。
以下是我尝试使用pst-3dplot
(过早的 3D 支持)和pst-eucl
(仅为 2D 设计)绘制它。这个过程很繁琐,因为许多任务,例如
- 根据两个现有的 3D 点,按照一定的缩放比例定义一个新的 3D 共线点,
- 将现有的 3D 点投影到连接两个现有 3D 点的线上,
- 用倾斜的垂直符号标记直角,
都是事先手动计算的。除此以外,\pstProjection
和在 3D 中不起作用\pstRightAngle
。pst-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
距离为,其中,。非常感谢marmotH
BDE
sqrt{a^2h^2}{a^2+h^2}
a = AB
h=AE
3dtools
\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
点的投影H
BDE
\path[3d/plane through={(E) and (D) and (B) named pEDB}];
\path[3d/project={(H) on pEDB}] coordinate (X);
在此代码中,我将的长度减少AH
为AH=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);
}