1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
|
divert(-1)
lib3D.m4 Macros for rotation, projection, and other
operations on argument triples representing
3D vectors or colors.
* Circuit_macros Version 10.1.4, copyright (c) 2023 J. D. Aplevich under *
* the LaTeX Project Public Licence in file Licence.txt. The files of *
* this distribution may be redistributed or modified provided that this *
* copyright notice is included and provided that modifications are clearly *
* marked to distinguish them from this distribution. There is no warranty *
* whatsoever for these files. *
define(`lib3D_')
ifdef(`libgen_',,`include(libgen.m4)divert(-1)')
=============================================================================
`setview(azimuth, elevation, rotation)
Set view angles (degrees) for projection onto
a plane. The view vector is obtained by looking
in along the x axis, then rotating about -x,
-y, and z in that order. 3D vectors are
projected onto the resulting yz plane
using the project() macro. If rotation = 0,
the projection matrix P is
P =( -sin(az), cos(az), 0 )
(-sin(el)cos(az),-sin(az)sin(el),cos(el))'
define(`setview',`
`# setview($@)'
define(`m4azim',ifelse(`$1',,0,prod_($1,dtor_)))dnl
define(`m4elev',ifelse(`$2',,0,prod_($2,dtor_)))dnl
define(`m4rot', ifelse(`$3',,0,prod_($3,dtor_)))dnl
define(`m4azimr',`ifelse(`$1',,0,`prod_(`$1',dtor_)')')dnl
define(`m4elevr',`ifelse(`$2',,0,`prod_(`$2',dtor_)')')dnl
define(`m4rotx',`ifelse(`$3',,0,`prod_(`$3',dtor_)')')dnl
define(`m4caz',
`ifelse(`$1',,1,`$1',0,1,`$1',90,0,`$1',-90, 0,m4cos(m4azim))')dnl
define(`m4saz',
`ifelse(`$1',,0,`$1',0,0,`$1',90,1,`$1',-90,-1,m4sin(m4azim))')dnl
define(`m4cel',
`ifelse(`$2',,1,`$2',0,1,`$2',90,0,`$2',-90, 0,m4cos(m4elev))')dnl
define(`m4sel',
`ifelse(`$2',,0,`$2',0,0,`$2',90,1,`$2',-90,-1,m4sin(m4elev))')dnl
define(`m4cro',`ifelse(`$3',,1,`$3',0,1,`$3',90,0,`$3',-90, 0,m4cos(m4rot))')dnl
define(`m4sro',`ifelse(`$3',,0,`$3',0,0,`$3',90,1,`$3',-90,-1,m4sin(m4rot))')dnl
view3D1=m4caz*m4cel;dnl
view3D2=m4saz*m4cel;dnl
view3D3=m4sel
ifdef(`setlight_',,`light3D1=view3D1; light3D2=view3D2; light3D3=view3D3')
dnl Unit vectors projected on the view plane
UPx_: project(1,0,0)
UPy_: project(0,1,0)
UPz_: project(0,0,1)
`# setview end'
')
define(`m4cos',`ifelse(`$1',0,1,cos(`$1'))')
define(`m4sin',`ifelse(`$1',0,0,sin(`$1'))')
The resulting view vector
#efine(`View3D',`PtoBase3D(1,0,0)')
define(`View3D',`view3D1,view3D2,view3D3')
`Extract the x-y, x-z, or y-z coordinate pair
from a triple'
define(`Pr_xy',`$1,$2')
define(`Pr_xz',`$1,$3')
define(`Pr_yz',`$2,$3')
Projection coords back to orig 3D coords
define(`PtoBase3D',
`rot3Dz(m4azim,rot3Dy(-m4elev,rot3Dx(-m4rot,`$1',`$2',`$3')))')
This does the 3D to 2D axonometric projection
i.e. project(x,y,z) produces coordinate pair
u,v on the 2D plane defined by the view angles
and Project(x,y,z) produces position (u,v)
ifdpic(
`define(`project',
`Pr_yz(rot3Dx(m4rot,rot3Dy(m4elev,rot3Dz(-m4azim,`$1',`$2',`$3'))))')
define(`Project',`(ifelse(`$1',0,
`ifelse(`$2',0,`ifelse(`$3',0,`(0,0)',UPz_*(`$3'))',
`UPy_*(`$2')`'ifelse(`$3',0,,+UPz_*(`$3'))')',
`UPx_*(`$1')`'ifelse(`$2',0,,+UPy_*(`$2'))`'ifelse(`$3',0,,+UPz_*(`$3'))'))')',
`define(`project',
`Pr_yz(rot3Dx(m4rot,rot3Dy(m4elev,rot3Dz(-m4azim,`$1',`$2',`$3'))))')
define(`Project',`(project($@))')')
`Rotation about x axis rot3Dx(angle,x1,x2,x3)'
define(`rot3Dx',``$2',diff_(prod_(m4cos(`$1'),`$3'),prod_(m4sin(`$1'),`$4')),dnl
sum_(prod_(m4sin(`$1'),`$3'),prod_(m4cos(`$1'),`$4'))')
`Rotation about y axis rot3Dy(angle,x1,x2,x3)'
define(`rot3Dy',`sum_(prod_(m4cos(`$1'),`$2'),prod_(m4sin(`$1'),`$4')),`$3',dnl
diff_(prod_(m4cos(`$1'),`$4'),prod_(m4sin(`$1'),`$2'))')
`Rotation about z axis rot3Dz(angle,x1,x2,x3)'
define(`rot3Dz',`diff_(prod_(m4cos(`$1'),`$2'),prod_(m4sin(`$1'),`$3')),dnl
sum_(prod_(m4sin(`$1'),`$2'),prod_(m4cos(`$1'),`$3')),`$4'')
`Cross product cross3D(x1,y1,z1,x2,y2,z2)'
define(`cross3D',`diff_(prod_(`$2',`$6'),prod_(`$3',`$5')),dnl
diff_(prod_(`$3',`$4'),prod_(`$1',`$6')),dnl
diff_(prod_(`$1',`$5'),prod_(`$2',`$4'))')
`Dot product dot3D(x1,y1,z1,x2,y2,z2)'
define(`dot3D',`(sum_(
sum_(prod_(`$1',`$4'),prod_(`$2',`$5')),prod_(`$3',`$6')))')
Vector addition, subtraction, scalar product
define(`sum3D',`sum_(`$1',`$4'),sum_(`$2',`$5'),sum_(`$3',`$6')')
define(`diff3D',`diff_(`$1',`$4'),diff_(`$2',`$5'),diff_(`$3',`$6')')
define(`sprod3D',`prod_(`$1',`$2'),prod_(`$1',`$3'),prod_(`$1',`$4')')
Extract direction cosine
`eg v = dcosine3D(1,x,y,z) assigns x to v'
define(`dcosine3D',`(ifelse(`$1',1,`$2',`$1',2,`$3',`$4'))')
Euclidian length
define(`length3D',`sqrt((`$1')^2+(`$2')^2+(`$3')^2)')
Unit vector
define(`unit3D',`sprod3D(1/length3D(`$1',`$2',`$3'),`$1',`$2',`$3')')
Proportion: between3D(x,Vec1,Vec2), i.e.
Vec1 * (1-x) + Vec2 * x
define(`between3D',`sum3D(sprod3D((1-(`$1')),`$2',`$3',`$4'),
sprod3D( `$1',`$5',`$6',`$7'))')
`assign3D([u],[v],[w],x,y,z); eg
assign3D(u,v,w,cross3D(x1,y1,z1,x2,y2,z2))
assigns u = 4th arg, v = 5th arg, w = 6th arg,
for nonblank u, v, or w'
define(`assign3D',`assign3($@)')
`vassign3D(name,x,y,z); eg
vassign3D(u,x,y,z)
assigns u[1] = x, u[2] = y, u[3] = z'
define(`vassign3D',`for i_vassign3D = 1 to 3 do {
exec sprintf("`$1'[i_vassign3D] = $%g",i_vassign3D+3) }')
Write out the 3 arguments for debug
define(`print3D',`print sprintf("`$1'(%g,%g,%g)",`$2',`$3',`$4')')
`setlight (azimuth, elevation, rotation)
Set angles (degrees) for 3D highlighting.
Defaults are the previous values for
setview(). The Light3D vector is defined
as for View3D.'
define(`setlight',`define(`setlight_')
define(`m4hzim',`ifelse(`$1',,m4azimr,`prod_(`$1',dtor_)')')dnl
define(`m4hlev',`ifelse(`$2',,m4elevr,`prod_(`$2',dtor_)')')dnl
define(`m4hot',`ifelse(`$3',,m4rotx,`prod_(`$3',dtor_)')')dnl
define(`m4chz',`ifelse(`$1',0,1,`$1',90,0,`$1',-90, 0,cos(m4hzim))')dnl
define(`m4shz',`ifelse(`$1',0,0,`$1',90,1,`$1',-90,-1,sin(m4hzim))')dnl
define(`m4chl',`ifelse(`$2',0,1,`$2',90,0,`$2',-90, 0,cos(m4hlev))')dnl
define(`m4shl',`ifelse(`$2',0,0,`$2',90,1,`$2',-90,-1,sin(m4hlev))')dnl
define(`m4cho',`ifelse(`$3',0,1,`$3',90,0,`$3',-90, 0,cos(m4hot))')dnl
define(`m4sho',`ifelse(`$3',0,0,`$3',90,1,`$3',-90,-1,sin(m4hot))')dnl
light3D1=m4chz*m4chl
light3D2=m4shz*m4chl
light3D3=m4shl
')
The resulting vector
#efine(`Light3D',`PtoBase3D(1,0,0)')
define(`Light3D',
`ifdef(`setlight_',`light3D1,light3D2,light3D3',`view3D1,view3D2,view3D3')')
`Fector(x,y,z,nx,ny,nz) with .Origin at pos
Arrow with flat 3D head. The second vector,
(i.e. args nx,ny,nz) is the normal to the
head flat surface'
define(`Fector',
`[ Origin: 0,0
define(`M4F_V',``$1',`$2',`$3'')dnl the whole vector V
lV = length3D(M4F_V)
define(`M4F_T',``$4',`$5',`$6'')dnl normal to the top surface
lT = length3D(M4F_T)
define(`M4F_Vn',`sprod3D(1/lV,M4F_V)')dnl unit vector Vn
aln = 0.15*scale ;dnl arrowhead length
awd = 0.09*scale ;dnl " width
adp = 0.0375*scale ;dnl " depth (thickness)
define(`M4F_Vt',`sprod3D((lV-aln),M4F_Vn)')dnl head base vector
Start: Origin
End: project(M4F_V)
rpoint_(from Origin to End)
lTdp = adp/2/lT
vtx = dcosine3D(1,M4F_Vt); vty = dcosine3D(2,M4F_Vt) # Vt coords
vtz = dcosine3D(3,M4F_Vt)
dnl half-thickness vector in direction of T
tx = prod_(lTdp,`$4'); ty = prod_(lTdp,`$5')
tz = prod_(lTdp,`$6')
dnl half-width vector right
rf = awd/2/lT/lV
rx = rf*dcosine3D(1,cross3D(M4F_V,M4F_T))
ry = rf*dcosine3D(2,cross3D(M4F_V,M4F_T))
rz = rf*dcosine3D(3,cross3D(M4F_V,M4F_T))
dnl top and bottom points of V
TV: project(sum3D(M4F_V, tx,ty,tz))
BV: project(diff3D(M4F_V, tx,ty,tz))
dnl top, bottom right, left of base
TR: project(sum3D(vtx,vty,vtz, sum3D(tx,ty,tz,rx,ry,rz)))
BR: project(sum3D(vtx,vty,vtz, diff3D(rx,ry,rz,tx,ty,tz)))
BL: project(diff3D(vtx,vty,vtz, sum3D(rx,ry,rz,tx,ty,tz)))
TL: project(diff3D(vtx,vty,vtz, diff3D(rx,ry,rz,tx,ty,tz)))
lthickness = linethick
dnl base
if dot3D(M4F_V,View3D) < 0 then {
thinlines_
ifgpic(`gshade(0.5,BR,BL,TL,TR,BR,BL)',
`line thick 0 fill_(0.5) from BR to BL then to TL then to TR then to BR')
line from BR to BL ; line to TL ; line to TR ; line to BR
linethick_(lthickness)
}
dnl shaft
linethick_(1.2)
psset_(arrows=c-c)
line from Origin to project(vtx,vty,vtz)
psset_(arrows=-)
thinlines_
dnl top or bottom
if dot3D(M4F_T,View3D) > 0 then {
ifgpic(`gshade(1,TR,TL,TV,TR,TL)',
`line thick 0 fill_(1) from TV to TR then to TL then to TV')
line from TV to TR ; line to TL ; line to TV
} else {
ifgpic(`gshade(0,BR,BL,BV,BR,BL)',
`line thick 0 fill_(0) from BV to BR then to BL then to BV')
line from BV to BR ; line to BL ; line to BV
}
dnl starboard normal; draw right face
define(`M4F_S',
`cross3D(diff3D(sprod3D(aln,M4F_Vn),rx,ry,rz),M4F_T)')dnl
if dot3D(M4F_S,View3D) > 0 then {
ifgpic(`gshade(1,TV,BV,BR,TR,TV,BV)',
`line thick 0 fill_(1) from TV to BV then to BR then to TR then to TV')
line from TV to BV ; line to BR ; line to TR ; line to TV
}
dnl port normal; draw left face
define(`M4F_P',
`cross3D(M4F_T,sum3D(sprod3D(aln,M4F_Vn),rx,ry,rz))')dnl
if dot3D(M4F_P,View3D) > 0 then {
ifgpic(`gshade(1,TV,BV,BL,TL,TV,BV)',
`line thick 0 fill_(1) from TV to BV then to BL then to TL then to TV')
line from TV to BV ; line to BL ; line to TL ; line to TV
}
linethick_(lthickness)
`$7'] ') # End Fector
`shadedball( rad,
highlight rad, highlight degrees,
initial shade, final shade )
The highlight is by default at
radius rad*3/5 and angle 110 deg
(or arg2 deg); if setlight has been
invoked then the highlight is as
given by its azimuth and elevation arguments'
define(`shadedball',`[ C: (0,0); r = ifelse(`$1',,circlerad,`$1')
ifdef(`setlight_',
`H: (project(sprod3D(r,light3D1,light3D2,light3D3)))
hr = distance(C,H)',
`hr = ifelse(`$2',,r*3/5,`$2'); ha = ifelse(`$3',,110,`$3')
H: (cosd(ha)*hr,sind(ha)*hr)')
u0 = ifelse(`$4',,1,`$4'); uf = ifelse(`$5',,0.25,`$5')
rm = r+hr; n = int(rm/(linethick bp__))
for i=1 to n-1 do { x = i/n*rm; u = u0+(i/n)^2*(uf-u0)
if x <= (r-hr) then {
circle rad x thick linethick*1.6 outlined rgbstring(u,u,u) at H } \
else { arc cw thick linethick*ifpgf(2,1.6) outlined rgbstring(u,u,u) \
from Cintersect(H,x,C,r) to Cintersect(H,x,C,r,R) with .c at H } }
circle rad r ifpgf(+linethick bp__/2) at C
`$6']')
divert(0)dnl
|