免費論壇 繁體 | 簡體
Sclub交友聊天~加入聊天室當版主
分享
返回列表 发帖
本帖最后由 hbghlyj 于 2021-9-8 03:42 编辑

tikzjax弄出来SVG以后,字符显示不正确
https://githubmemory.com/repo/kisonecat/tikzjax/issues/7
https://stackoverflow.com/questi ... enerated-by-tikzjax
原因是它的字体编码很奇怪

把$π$复制出来却是¼

TOP

本帖最后由 hbghlyj 于 2021-9-14 07:07 编辑

@kuing
建议后台设置一下,为论坛编辑器增加BBcode:
[svg=長,高] svg 碼[/svg]
以支持 SVG 向量碼之顯示

TOP

本帖最后由 hbghlyj 于 2021-12-16 06:34 编辑

https://github.com/manuels/texlive.js/
Compiling LaTeX (TeX live) in your browser
<svg><style>br{display:none}</style></svg>

TOP

本帖最后由 hbghlyj 于 2021-12-16 06:34 编辑

<svg><style>br{display:none}</style></svg>

TOP

本帖最后由 hbghlyj 于 2021-10-1 20:37 编辑

回复 20# abababa
这个tikzjax将tikz转换成svg的过程其实挺慢的,比如见这里.这些图都是我用tikz画的,以肉眼可见的速度一个一个地显示出来.
远不如在编辑帖子时就转换成svg代码插入帖子出来得快.(心形线和旋轮线是用svg直接插的,就显示得很快)

TOP

回复 21# hbghlyj
比如这样
  1. jQuery(document).ready(function(){for(let i=0;i<jQuery("script[type='text/tikz']").length;i++){jQuery("script[type='text/tikz']")[i].innerHTML=jQuery("script[type='text/tikz']")[i].innerHTML.replaceAll("&gt;",">").replaceAll("&lt;","<")}});
复制代码

TOP

回复 29# abababa
同作者
https://tex.rossprogram.org/

TOP

<script src="//i.upmath.me/latex.js"></script>
https://i.upmath.me/g/

TOP

回复 35# abababa
Javascript内置encodeURI函数

TOP

本帖最后由 hbghlyj 于 2021-11-15 20:13 编辑

将discuz的BBcode正则表达式
  1. \[b\]([^\[]*)\[\/b\]
复制代码
改为
  1. \[b\](.*?)\[\/b\]
复制代码
即可在BBcode中使用中括号.

TOP

本帖最后由 hbghlyj 于 2021-11-15 20:12 编辑

例如
  1. [b]字[字[/b]
复制代码
对于第一个无法match
而对于第二个可以match

TOP

本帖最后由 hbghlyj 于 2021-12-20 14:49 编辑

这种.*?叫做lazy quantifier或者non-greedy quantifier
https://developer.mozilla.org/en ... essions/Quantifiers
it will stop as soon as it finds a match

TOP

回复 42# abababa
当然可以

TOP

本帖最后由 hbghlyj 于 2021-11-15 22:21 编辑

回复 43# kuing

static/js/bbcode.js
有一个bbcode2html函数
我们把它替换成discuz X的定义

TOP

可以到https://infinityfree.net/弄到免费的网络空间作测试

TOP

  1. document.body.innerHTML=document.body.innerHTML.replaceAll('[tikz]','<tikz>').replaceAll('[/tikz]','</tikz>');document.querySelectorAll('tikz').forEach(function(elem){elem.innerHTML='<img src="https://i.upmath.me/svg/'+encodeURI(elem.innerHTML.replaceAll('&gt;','>').replaceAll('&lt;','<'))+'">'})
复制代码

TOP

本帖最后由 hbghlyj 于 2021-12-16 06:35 编辑

我按照这个教程自己搭建了CORS proxy.好像成功了.示例如下:
  1. fetch("https://tranquil-ridge-80735.herokuapp.com/artofproblemsolving.com/m/texer/ajax.php:443", {
  2.   "headers": {
  3.    "content-type": "application/x-www-form-urlencoded; charset=UTF-8"
  4.   },
  5.   "referrer": "https://artofproblemsolving.com/",
  6.   "referrerPolicy": "origin",
  7.   "body": "action=aopscode&token=hlplshue&tex=JEEk&rerender=false","method": "POST"
  8. }).then(res => res.text()).then(res=>console.log(res))
复制代码
可以跨域请求.它的输出是aops的网页.
<svg><style>br{display:none}</style></svg>

TOP

回复 77# kuing
我先注册了Heroku帐号,下载了git和Heroku CLI,把cors-anywhere仓库克隆到本地,然后cd cors-anywhere,然后npm install,然后heroku login登录,然后heroku create新建应用,然后把本地的文件push到master上就可以用了
使用的时候就在那个herokuapp域名后面加上想要跨域请求的域名就可以了

TOP

本帖最后由 hbghlyj 于 2021-11-18 07:42 编辑

测试一下php脚本,从aops texer把tex转换成png再重定向.
用法:首先将需要的公式转成base64,比如
  1. window.btoa("$x^2+y^2$")
  2. 输出:JHheMit5XjIk
复制代码
所以$x^2+y^2$的base64是JHheMit5XjIk.然后把图片地址填上
  1. https://cjhb.site/aops.php?tex=JHheMit5XjIk
复制代码
即可.
重定向以后得到的图片是(每次生成新的链接)

我们可以封装一个简单的js函数
  1. function aops(str){window.open("http://cjhb.site/aops.php?tex="+window.btoa(str.replaceAll('\n','')))}
复制代码

示例1:将一段公式经过Base64加密得到的,可以在浏览器调试工具中运行,注意,反斜杠需要转义(当然也可以从html元素中读取,就不用手动转义了):
  1. aops('$$\\sqrt x=\\frac{\\sqrt y}{\\sqrt z}$$')
复制代码

重定向以后得到的图片是

示例2:将一段Asymptote代码去掉换行符以后经过Base64加密得到的,可以在浏览器调试工具中运行:
  1. aops(`[asy]import graph;
  2. import palette;
  3. import contour;

  4. size(12cm,IgnoreAspect);

  5. pair a=(pi/2,0);
  6. pair b=(3pi/2,2pi);

  7. real f(real x, real y) {return cos(x)*sin(y);}

  8. int N=100;
  9. int Divs=10;

  10. defaultpen(1bp);

  11. bounds range=bounds(-1,1);

  12. real[] Cvals=uniform(range.min,range.max,Divs);
  13. guide[][] g=contour(f,a,b,Cvals,N,operator --);

  14. pen[] Palette=quantize(Rainbow(),Divs);

  15. pen[][] interior=interior(g,extend(Palette,grey,black));
  16. fill(g,interior);
  17. draw(g);

  18. palette("$f(x,y)$",range,point(SE)+(0.5,0),point(NE)+(1,0),Right,Palette,
  19.         PaletteTicks("$%+#0.1f$",N=Divs));[/asy]`)
复制代码

其中的asy标签是aops论坛自定义的.重定向以后得到的图片是

用于转发数据的php代码如下:
  1. <?php
  2. $data = array(
  3.         'action'=>'aopscode',
  4.                 'token'=>'hlplshue',
  5.                 'tex'=>$_GET["tex"],
  6.                 'rerender'=>false
  7. );
  8. $ch = curl_init();
  9. curl_setopt_array($ch, array(
  10.         CURLOPT_URL => "https://artofproblemsolving.com/m/texer/ajax.php",
  11.         CURLOPT_RETURNTRANSFER => true, // receive server response
  12.         CURLOPT_POST => 1, // do post
  13.         CURLOPT_HTTPHEADER => array('Content-Type: application/x-www-form-urlencoded'),
  14.         CURLOPT_POSTFIELDS => http_build_query($data)
  15. ));
  16. $result = curl_exec($ch);
  17. curl_close($ch);
  18. $res=json_decode($result)->response->html;
  19. header('Location: '.explode('"', $res)[1]);
  20. die();
  21. ?>
复制代码
更多的Asymptote示例(均来自官网)
  1. aops(`[asy]import graph;

  2. size(140mm,70mm,IgnoreAspect);
  3. scale(false);
  4. real[] x={1,3,4,5,6};
  5. real[] y={1,5,2,0,4};

  6. marker mark=marker(scale(1mm)*cross(6,false,r=0.35),red,Fill);

  7. draw(graph(x,y,Hermite),"Hermite Spline",mark);
  8. xaxis("$x$",Bottom,LeftTicks(x));
  9. yaxis("$y$",Left,LeftTicks);
  10. attach(legend(),point(NW),40S+30E,UnFill);
  11. [/asy]`)
复制代码


  1. aops(`[asy]import graph;
  2. size(200,IgnoreAspect);

  3. real f(real x) {return 1/x;};

  4. bool3 branch(real x)
  5. {
  6.   static int lastsign=0;
  7.   if(x == 0) return false;
  8.   int sign=sgn(x);
  9.   bool b=lastsign == 0 || sign == lastsign;
  10.   lastsign=sign;
  11.   return b ? true : default;
  12. }

  13. draw(graph(f,-1,1,branch));
  14. axes("$x$","$y$",red);
  15. [/asy]`)
复制代码


  1. aops(`[asy]import graph;

  2. real Freq=60.0;
  3. real margin=5mm;

  4. pair exp(pair x) {
  5.   return exp(x.x)*(cos(x.y)+I*sin(x.y));
  6. }

  7. real Merr(real x, real w) {
  8.   real tau=x/(2*Freq);
  9.   return 20*log(abs((tau*w+tau/(exp(I*2*pi*Freq*tau)-1))*(I*2*pi*Freq)));
  10. }

  11. real Aerr(real x, real w) {
  12.   real tau=x/(2*Freq);
  13.   return degrees((tau*w+tau/(exp(I*2*pi*Freq*tau)-1))*(I*2*pi*Freq));
  14. }

  15. picture pic1;
  16. scale(pic1,Log,Linear);
  17. real Merr1(real x){return Merr(x,1);}
  18. draw(pic1,graph(pic1,Merr1,1e-4,1),black+1.2);

  19. ylimits(pic1,-60,20);
  20. yaxis(pic1,"magnitude (dB)",LeftRight,RightTicks(new
  21.                                                  real[] {-60,-40,-20,0,20}));
  22. xaxis(pic1,"$f/f_\\mathrm{Ny}$",BottomTop,LeftTicks(N=5));
  23. yequals(pic1,0,Dotted);
  24. yequals(pic1,-20,Dotted);
  25. yequals(pic1,-40,Dotted);
  26. xequals(pic1,1e-3,Dotted);
  27. xequals(pic1,1e-2,Dotted);
  28. xequals(pic1,1e-1,Dotted);

  29. size(pic1,100,100,point(pic1,SW),point(pic1,NE));

  30. label(pic1,"$\\theta=1$",point(pic1,N),2N);

  31. frame f1=pic1.fit();
  32. add(f1);

  33. picture pic1p;
  34. scale(pic1p,Log,Linear);
  35. real Aerr1(real x){return Aerr(x,1);}
  36. draw(pic1p,graph(pic1p,Aerr1,1e-4,1),black+1.2);

  37. ylimits(pic1p,-5,95);
  38. yaxis(pic1p,"phase (deg)",LeftRight,RightTicks(new real[] {0,45,90}));
  39. xaxis(pic1p,"$f/f_\\mathrm{Ny}$",BottomTop,LeftTicks(N=5));
  40. yequals(pic1p,0,Dotted);
  41. yequals(pic1p,45,Dotted);
  42. yequals(pic1p,90,Dotted);
  43. xequals(pic1p,1e-3,Dotted);
  44. xequals(pic1p,1e-2,Dotted);
  45. xequals(pic1p,1e-1,Dotted);

  46. size(pic1p,100,100,point(pic1p,SW),point(pic1p,NE));

  47. frame f1p=pic1p.fit();
  48. f1p=shift(0,min(f1).y-max(f1p).y-margin)*f1p;
  49. add(f1p);

  50. picture pic2;
  51. scale(pic2,Log,Linear);
  52. real Merr2(real x){return Merr(x,0.75);}
  53. draw(pic2,graph(pic2,Merr2,1e-4,1),black+1.2);

  54. ylimits(pic2,-60,20);
  55. yaxis(pic2,"magnitude (dB)",LeftRight,RightTicks(new
  56.                                                  real[] {-60,-40,-20,0,20}));
  57. xaxis(pic2,"$f/f_\\mathrm{Ny}$",BottomTop,LeftTicks(N=5));
  58. yequals(pic2,0,Dotted);
  59. yequals(pic2,-20,Dotted);
  60. yequals(pic2,-40,Dotted);
  61. xequals(pic2,1e-3,Dotted);
  62. xequals(pic2,1e-2,Dotted);
  63. xequals(pic2,1e-1,Dotted);

  64. size(pic2,100,100,point(pic2,SW),point(pic2,NE));

  65. label(pic2,"$\\theta=0.75$",point(pic2,N),2N);

  66. frame f2=pic2.fit();
  67. f2=shift(max(f1).x-min(f2).x+margin)*f2;
  68. add(f2);

  69. picture pic2p;
  70. scale(pic2p,Log,Linear);
  71. real Aerr2(real x){return Aerr(x,0.75);}
  72. draw(pic2p,graph(pic2p,Aerr2,1e-4,1),black+1.2);

  73. ylimits(pic2p,-5,95);
  74. yaxis(pic2p,"phase (deg)",LeftRight,RightTicks(new real[] {0,45.1,90}));
  75. xaxis(pic2p,"$f/f_\\mathrm{Ny}$",BottomTop,LeftTicks(N=5));
  76. yequals(pic2p,0,Dotted);
  77. yequals(pic2p,45,Dotted);
  78. yequals(pic2p,90,Dotted);
  79. xequals(pic2p,1e-3,Dotted);
  80. xequals(pic2p,1e-2,Dotted);
  81. xequals(pic2p,1e-1,Dotted);

  82. size(pic2p,100,100,point(pic2p,SW),point(pic2p,NE));

  83. frame f2p=pic2p.fit();
  84. f2p=shift(max(f1p).x-min(f2p).x+margin,min(f2).y-max(f2p).y-margin)*f2p;
  85. add(f2p);
  86. [/asy]`)
复制代码


  1. aops(`[asy]import graph;
  2. import palette;
  3. import contour;

  4. size(10cm,10cm,IgnoreAspect);

  5. pair a=(0,0);
  6. pair b=(2pi,2pi);

  7. real f(real x, real y) {return cos(x)*sin(y);}

  8. int N=200;
  9. int Divs=10;
  10. int divs=2;

  11. defaultpen(1bp);
  12. pen Tickpen=black;
  13. pen tickpen=gray+0.5*linewidth(currentpen);
  14. pen[] Palette=BWRainbow();

  15. bounds range=image(f,Automatic,a,b,N,Palette);

  16. // Major contours

  17. real[] Cvals=uniform(range.min,range.max,Divs);
  18. draw(contour(f,a,b,Cvals,N,operator --),Tickpen);

  19. // Minor contours
  20. real[] cvals;
  21. for(int i=0; i < Cvals.length-1; ++i)
  22.   cvals.append(uniform(Cvals[i],Cvals[i+1],divs)[1:divs]);
  23. draw(contour(f,a,b,cvals,N,operator --),tickpen);

  24. xaxis("$x$",BottomTop,LeftTicks,above=true);
  25. yaxis("$y$",LeftRight,RightTicks,above=true);

  26. palette("$f(x,y)$",range,point(NW)+(0,0.5),point(NE)+(0,1),Top,Palette,
  27.         PaletteTicks(N=Divs,n=divs,Tickpen,tickpen));
  28. [/asy]`)
复制代码


  1. aops(`[asy]import graph;
  2. import stats;

  3. size(400,200,IgnoreAspect);

  4. int n=10000;
  5. real[] a=new real[n];
  6. for(int i=0; i < n; ++i) a[i]=Gaussrand();

  7. draw(graph(Gaussian,min(a),max(a)),blue);

  8. // Optionally calculate "optimal" number of bins a la Shimazaki and Shinomoto.
  9. int N=bins(a);

  10. histogram(a,min(a),max(a),N,normalize=true,low=0,lightred,black,bars=false);

  11. xaxis("$x$",BottomTop,LeftTicks);
  12. yaxis("$dP/dx$",LeftRight,RightTicks(trailingzero));[/asy]`)
复制代码


  1. aops(`[asy]import graph;
  2. size(0,100);

  3. path g=ellipse((0,0),1,2);

  4. scale(true);

  5. axis(Label("C",align=10W),g,LeftTicks(endlabel=false,8,end=false),
  6.      ticklocate(0,360,new real(real v) {
  7.          path h=(0,0)--max(abs(max(g)),abs(min(g)))*dir(v);
  8.          return intersect(g,h)[0];}));[/asy]`)
复制代码


  1. aops(`[asy]import graph;
  2. import palette;

  3. int n=256;
  4. pen[] Palette=BWRainbow();

  5. real w(real w0, real z0, real z) {return w0*sqrt(1+(z/z0)^2);}

  6. real pot(real lambda, real w0, real r, real z)
  7. {
  8.   real z0=pi*w0^2/lambda, kappa=2pi/lambda;
  9.   return exp(-2*(r/w(w0,z0,z))^2)*cos(kappa*z)^2;
  10. }

  11. picture make_field(real lambda, real w0)
  12. {
  13.   real[][] v=new real[n][n];
  14.   for(int i=0; i < n; ++i)
  15.     for(int j=0; j < n; ++j)
  16.       v[i][j]=pot(lambda,w0,i-n/2,abs(j-n/2));

  17.   picture p=new picture;
  18.   size(p,250,250,IgnoreAspect);
  19.   real xm=-n/lambda, ym=-n/(2*w0), xx=n/lambda, yx=n/(2*w0);
  20.   image(p,v,(xm,ym),(xx,yx),Palette);
  21.   xlimits(p,xm,xx);
  22.   ylimits(p,ym,yx);
  23.   xaxis(p,"{\\Large $z/\\frac{\\lambda}{2}$}",BottomTop,LeftTicks);
  24.   yaxis(p,"{\\Large $r/w_0$}",LeftRight,RightTicks);
  25.   label(p,format("{\\LARGE $w_0/\\lambda=%.2f$}",w0/lambda),point(p,NW),5N);

  26.   return p;
  27. }

  28. picture p=make_field(160,80);
  29. picture q=make_field(80,80);
  30. picture r=make_field(16,80);
  31. picture s=make_field(2,80);

  32. real margin=1cm;
  33. add(p.fit(),(0,0),margin*NW);
  34. add(q.fit(),(0,0),margin*NE);
  35. add(r.fit(),(0,0),margin*SW);
  36. add(s.fit(),(0,0),margin*SE);[/asy]`)
复制代码


  1. aops(`[asy]import graph;
  2. import palette;

  3. size(10cm,10cm,IgnoreAspect);

  4. real f(real x, real y) {
  5.   return 0.9*pow10(2*sin(x/5+2*y^0.25)) + 0.1*(1+cos(10*log(y)));
  6. }

  7. scale(Linear,Log,Log);

  8. pen[] Palette=BWRainbow();

  9. bounds range=image(f,Automatic,(0,1),(100,100),nx=200,Palette);

  10. xaxis("$x$",BottomTop,LeftTicks,above=true);
  11. yaxis("$y$",LeftRight,RightTicks,above=true);

  12. palette("$f(x,y)$",range,(0,200),(100,250),Top,Palette,
  13.         PaletteTicks(ptick=linewidth(0.5*linewidth())));

  14. [/asy]`)
复制代码


  1. aops(`[asy]import graph;

  2. size(9cm,8cm,IgnoreAspect);
  3. string data="westnile.csv";

  4. file in=input(data).line().csv();

  5. string[] columnlabel=in;

  6. real[][] A=in;
  7. A=transpose(A);
  8. real[] number=A[0], survival=A[1];

  9. path g=graph(number,survival);
  10. draw(g);

  11. scale(true);

  12. xaxis("Initial no.\\ of mosquitoes per bird ($S_{M_0}/N_{B_0}$)",
  13.       Bottom,LeftTicks);
  14. xaxis(Top);
  15. yaxis("Susceptible bird survival",Left,RightTicks(trailingzero));
  16. yaxis(Right);

  17. real a=number[0];
  18. real b=number[number.length-1];

  19. real S1=0.475;
  20. path h1=(a,S1)--(b,S1);
  21. real M1=interp(a,b,intersect(h1,g)[0]);

  22. real S2=0.9;
  23. path h2=(a,S2)--(b,S2);
  24. real M2=interp(a,b,intersect(h2,g)[0]);

  25. labelx("$M_1$",M1);
  26. labelx("$M_2$",M2);

  27. draw((a,S2)--(M2,S2)--(M2,0),Dotted);
  28. draw((a,S1)--(M1,S1)--(M1,0),dashed);

  29. pen p=fontsize(10pt);

  30. real y3=0.043;
  31. path reduction=(M1,y3)--(M2,y3);
  32. draw(reduction,Arrow,TrueMargin(0,0.5*(linewidth(Dotted)+linewidth())));

  33. arrow(shift(-20,5)*Label(minipage("\\flushleft{\\begin{itemize}\\item[1.]
  34. Estimate proportion of birds surviving at end of season\\end{itemize}}",100),
  35.                          align=NNE),
  36.       (M1,S1),NNE,1cm,p,Arrow(NoFill));

  37. arrow(shift(-24,5)*Label(minipage("\\flushleft{\\begin{itemize}\\item[2.]
  38. Read off initial mosquito abundance\\end{itemize}}",80),align=NNE),
  39.       (M1,0),NE,2cm,p,Arrow(NoFill));

  40. arrow(shift(20,0)*Label(minipage("\\flushleft{\\begin{itemize}\\item[3.]
  41. Determine desired bird survival for next season\\end{itemize}}",90),align=SW),
  42.       (M2,S2),SW,arrowlength,p,Arrow(NoFill));

  43. arrow(shift(8,-15)*Label(minipage("\\flushleft{\\begin{itemize}\\item[4.]
  44. Calculate required proportional reduction in mosquitoes\\end{itemize}}",90),
  45.                          align=NW),
  46.       point(reduction,0.5),NW,1.5cm,p,Arrow(NoFill));[/asy]`)
复制代码


  1. aops(`[asy]import graph;
  2. size(100);

  3. pair a=(0,0);
  4. pair b=(2pi,2pi);

  5. path vector(pair z) {return (0,0)--(sin(z.x),cos(z.y));}

  6. add(vectorfield(vector,a,b));[/asy]`)
复制代码

TOP

本帖最后由 hbghlyj 于 2022-2-22 18:31 编辑

css的transform有一个matrix3d,这个应该是最为强大的了.下面的介绍来自https://drafts.csswg.org/css-tra ... atical-description.
• Mathematically, all transform functions can be represented as 4x4 transformation matrices of the following form:
\begin{bmatrix} m11 & m21 & m31 & m41 \\ m12 & m22 & m32 & m42 \\ m13 & m23 & m33 & m43 \\ m14 & m24 & m34 & m44 \end{bmatrix}
One translation unit on a matrix is equivalent to 1 pixel in the local coordinate system of the element.
• A 3D translation with the parameters tx, ty and tz is equivalent to the matrix:
\begin{bmatrix} 1 & 0 & 0 & tx \\ 0 & 1 & 0 & ty \\ 0 & 0 & 1 & tz \\ 0 & 0 & 0 & 1 \end{bmatrix}
• A 3D scaling with the parameters sx, sy and sz is equivalent to the matrix:
\begin{bmatrix} sx & 0 & 0 & 0 \\ 0 & sy & 0 & 0 \\ 0 & 0 & sz & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
• A 3D rotation with the vector [x,y,z] and the parameter alpha is equivalent to the matrix:
\begin{bmatrix} 1 - 2 \cdot (y^2 + z^2) \cdot sq & 2 \cdot (x \cdot y \cdot sq - z \cdot sc) & 2 \cdot (x \cdot z \cdot sq + y \cdot sc) & 0 \\ 2 \cdot (x \cdot y \cdot sq + z \cdot sc) & 1 - 2 \cdot (x^2 + z^2) \cdot sq & 2 \cdot (y \cdot z \cdot sq - x \cdot sc) & 0 \\ 2 \cdot (x \cdot z \cdot sq - y \cdot sc) & 2 \cdot (y \cdot z \cdot sq + x \cdot sc) & 1 - 2 \cdot (x^2 + y^2) \cdot sq & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}
where$$sc = \sin (\alpha/2) \cdot \cos (\alpha/2)$$$$sq = \sin^2 (\alpha/2)$$and where x, y, and z have been normalized (that is, where the x, y, and z values given have been divided by the square root of the sum of their squares).
在计算两个4×4矩阵的插值的时候使用了四元数,而不是欧拉角,以避免环架锁定(Gimbal Locks)的问题
13.1.1. Decomposing a 3D matrix
The pseudo code below is based upon the "unmatrix" method in "Graphics Gems II, edited by Jim Arvo", but modified to use Quaternions instead of Euler angles to avoid the problem of Gimbal Locks.

The following pseudocode works on a 4x4 homogeneous matrix:

Input:  matrix      ; a 4x4 matrix
Output: translation ; a 3 component vector
        scale       ; a 3 component vector
        skew        ; skew factors XY,XZ,YZ represented as a 3 component vector
        perspective ; a 4 component vector
        quaternion  ; a 4 component vector
Returns false if the matrix cannot be decomposed, true if it can


// Normalize the matrix.
if (matrix[3][3] == 0)
    return false

for (i = 0; i < 4; i++)
    for (j = 0; j < 4; j++)
        matrix[i​][j] /= matrix[3][3]

// perspectiveMatrix is used to solve for perspective, but it also provides
// an easy way to test for singularity of the upper 3x3 component.
perspectiveMatrix = matrix

for (i = 0; i < 3; i++)
    perspectiveMatrix[i​][3] = 0

perspectiveMatrix[3][3] = 1

if (determinant(perspectiveMatrix) == 0)
    return false

// First, isolate perspective.
if (matrix[0][3] != 0 || matrix[1][3] != 0 || matrix[2][3] != 0)
    // rightHandSide is the right hand side of the equation.
    rightHandSide[0] = matrix[0][3]
    rightHandSide[1] = matrix[1][3]
    rightHandSide[2] = matrix[2][3]
    rightHandSide[3] = matrix[3][3]

    // Solve the equation by inverting perspectiveMatrix and multiplying
    // rightHandSide by the inverse.
    inversePerspectiveMatrix = inverse(perspectiveMatrix)
    transposedInversePerspectiveMatrix = transposeMatrix4(inversePerspectiveMatrix)
    perspective = multVecMatrix(rightHandSide, transposedInversePerspectiveMatrix)
else
    // No perspective.
    perspective[0] = perspective[1] = perspective[2] = 0
    perspective[3] = 1

// Next take care of translation
for (i = 0; i < 3; i++)
    translate[i​] = matrix[3][i​]

// Now get scale and shear. 'row' is a 3 element array of 3 component vectors
for (i = 0; i < 3; i++)
    row[i​][0] = matrix[i​][0]
    row[i​][1] = matrix[i​][1]
    row[i​][2] = matrix[i​][2]

// Compute X scale factor and normalize first row.
scale[0] = length(row[0])
row[0] = normalize(row[0])

// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew[0] = dot(row[0], row[1])
row[1] = combine(row[1], row[0], 1.0, -skew[0])

// Now, compute Y scale and normalize 2nd row.
scale[1] = length(row[1])
row[1] = normalize(row[1])
skew[0] /= scale[1];

// Compute XZ and YZ shears, orthogonalize 3rd row
skew[1] = dot(row[0], row[2])
row[2] = combine(row[2], row[0], 1.0, -skew[1])
skew[2] = dot(row[1], row[2])
row[2] = combine(row[2], row[1], 1.0, -skew[2])

// Next, get Z scale and normalize 3rd row.
scale[2] = length(row[2])
row[2] = normalize(row[2])
skew[1] /= scale[2]
skew[2] /= scale[2]

// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip.  If the determinant
// is -1, then negate the matrix and the scaling factors.
pdum3 = cross(row[1], row[2])
if (dot(row[0], pdum3) < 0)
    for (i = 0; i < 3; i++)
        scale[i​] *= -1;
        row[i​][0] *= -1
        row[i​][1] *= -1
        row[i​][2] *= -1

// Now, get the rotations out
quaternion[0] = 0.5 * sqrt(max(1 + row[0][0] - row[1][1] - row[2][2], 0))
quaternion[1] = 0.5 * sqrt(max(1 - row[0][0] + row[1][1] - row[2][2], 0))
quaternion[2] = 0.5 * sqrt(max(1 - row[0][0] - row[1][1] + row[2][2], 0))
quaternion[3] = 0.5 * sqrt(max(1 + row[0][0] + row[1][1] + row[2][2], 0))

if (row[2][1] > row[1][2])
    quaternion[0] = -quaternion[0]
if (row[0][2] > row[2][0])
    quaternion[1] = -quaternion[1]
if (row[1][0] > row[0][1])
    quaternion[2] = -quaternion[2]

return true
13.1.2. Interpolation of decomposed 3D matrix values
Each component of the decomposed values translation, scale, skew and perspective of the source matrix get linearly interpolated with each corresponding component of the destination matrix.

Note: For instance, translate[0] of the source matrix and translate[0] of the destination matrix are interpolated numerically, and the result is used to set the translation of the animating element.

Quaternions of the decomposed source matrix are interpolated with quaternions of the decomposed destination matrix using the spherical linear interpolation (Slerp) as described by the pseudo code below:

Input:  quaternionA   ; a 4 component vector
        quaternionB   ; a 4 component vector
        t             ; interpolation parameter with 0 <= t <= 1
Output: quaternionDst ; a 4 component vector


product = dot(quaternionA, quaternionB)

// Clamp product to -1.0 <= product <= 1.0
product = min(product, 1.0)
product = max(product, -1.0)

if (abs(product) == 1.0)
   quaternionDst = quaternionA
   return

theta = acos(product)
w = sin(t * theta) / sqrt(1 - product * product)

for (i = 0; i < 4; i++)
  quaternionA[i​] *= cos(t * theta) - product * w
  quaternionB[i​] *= w
  quaternionDst[i​] = quaternionA[i​] + quaternionB[i​]

return
13.1.3. Recomposing to a 3D matrix
After interpolation, the resulting values are used to transform the elements user space. One way to use these values is to recompose them into a 4x4 matrix. This can be done following the pseudo code below:

Input:  translation ; a 3 component vector
        scale       ; a 3 component vector
        skew        ; skew factors XY,XZ,YZ represented as a 3 component vector
        perspective ; a 4 component vector
        quaternion  ; a 4 component vector
Output: matrix      ; a 4x4 matrix

Supporting functions (matrix is a 4x4 matrix):
  matrix  multiply(matrix a, matrix b)   returns the 4x4 matrix product of a * b

// apply perspective
for (i = 0; i < 4; i++)
  matrix[i​][3] = perspective[i​]

// apply translation
for (i = 0; i < 4; i++)
  for (j = 0; j < 3; j++)
    matrix[3][i​] += translation[j] * matrix[j][i​]

// apply rotation
x = quaternion[0]
y = quaternion[1]
z = quaternion[2]
w = quaternion[3]

// Construct a composite rotation matrix from the quaternion values
// rotationMatrix is a identity 4x4 matrix initially
rotationMatrix[0][0] = 1 - 2 * (y * y + z * z)
rotationMatrix[0][1] = 2 * (x * y - z * w)
rotationMatrix[0][2] = 2 * (x * z + y * w)
rotationMatrix[1][0] = 2 * (x * y + z * w)
rotationMatrix[1][1] = 1 - 2 * (x * x + z * z)
rotationMatrix[1][2] = 2 * (y * z - x * w)
rotationMatrix[2][0] = 2 * (x * z - y * w)
rotationMatrix[2][1] = 2 * (y * z + x * w)
rotationMatrix[2][2] = 1 - 2 * (x * x + y * y)

matrix = multiply(matrix, rotationMatrix)

// apply skew
// temp is a identity 4x4 matrix initially
if (skew[2])
    temp[2][1] = skew[2]
    matrix = multiply(matrix, temp)

if (skew[1])
    temp[2][1] = 0
    temp[2][0] = skew[1]
    matrix = multiply(matrix, temp)

if (skew[0])
    temp[2][0] = 0
    temp[1][0] = skew[0]
    matrix = multiply(matrix, temp)

// apply scale
for (i = 0; i < 3; i++)
  for (j = 0; j < 4; j++)
    matrix[i​][j] *= scale[i​]

return

TOP

返回列表 回复 发帖