找回密码
 快速注册
搜索
查看: 541|回复: 10

[python3]ggb导出为tikz

[复制链接]

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

hbghlyj 发表于 2021-12-31 09:42 |阅读模式
本帖最后由 hbghlyj 于 2024-2-29 23:46 编辑 tex.cjhb.site/
原理:将ggb文件解压(unzip)得到的geogebra.xml包含了几何对象的全部数据.
已支持的GeoGebra对象有:"点","圆","椭圆","双曲线","抛物线","直线","线段","向量","射线","折线","多边形","填充多边形","角","半圆","函数y=f(x)图像","正多边形","圆弧","扇形","三点圆弧","三点扇形".
支持对象标签.标签使用公式环境,并且将Unicode转换为latex,比如将β转换为\beta.
不处理隐藏的对象.不处理未定义的点(坐标为NaN).但是其他的未定义对象没有被排除(有待改进).
点的大小可以通过\pointsize调整,线的宽度也可以调,用于标注角的弧的大小可以通过\anglesize调整.
$type latex.py (10.51 KB, 下载次数: 68) $type ggb.py (22.76 KB, 下载次数: 69)

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2021-12-31 09:50
本帖最后由 hbghlyj 于 2022-1-2 14:27 编辑 geogebra.xml包含了GeoGebra对象的所有数据.比如一条圆锥曲线c,它的数据为:
  1. <element type="conic" label="c">
  2.         <show object="true" label="false"/>
  3.         <objColor r="0" g="0" b="0" alpha="0.0"/>
  4.         <layer val="0"/>
  5.         <labelMode val="0"/>
  6.         <lineStyle thickness="5" type="0" typeHidden="1" opacity="178"/>
  7.         <eigenvectors x0="0.7503199578268499" y0="-0.6610748527108818" z0="1.0" x1="0.6610748527108818" y1="0.7503199578268499" z1="1.0"/>
  8.         <matrix A0="32.24905166992741" A1="34.965131929527196" A2="-31.75959383487715" A3="10.695640886965936" A4="-31.491194071005353" A5="-5.387249697079366"/>
  9.         <eqnStyle style="implicit"/>
  10. </element>
复制代码
这里的show标签的两个属性object和label分别记录对象c是否显示,和c的标签是否显示.
这里的eigenvectors和matrix是几何属性.
matrix是指圆锥曲线c的矩阵表示$\mathbf x^{\sf T}A\mathbf x=\mathbf 0$,其中$\mathbf x=(x\ y\ 1)^{\sf T}$,matrix的属性$A0,A1,A2,A3,A4,A5$就是矩阵的元素:$$A=\begin{pmatrix}A0&A3&A4\\A3&A1&A5\\A4&A5&A2\end{pmatrix}$$eigenvectors已经构成一个行列式为1的2阶正交矩阵$P=\begin{pmatrix}x0&x1&0\\y0&y1&0\\0&0&1\end{pmatrix}$,用它就可以将(1,0),(0,1)旋转到两个主轴的端点了.旋转角$θ=\href{https://numpy.org/doc/stable/reference/generated/numpy.arctan2.html}{\rm arctan2}(y0,x0)$.
那么$\mathbf x^{\sf T}P^{\sf T}AP\mathbf x=\mathbf0$就是将圆锥曲线c旋转得到的圆锥曲线c'的方程,它不含$xy$项,即$$P^{\sf T}AP=\begin{pmatrix}m&0&p\\0&n&q\\p&q&r\end{pmatrix}$$如果$m=0$,c'是水平的抛物线,用\draw[rotate=θ]plot[smooth]({-(n*(\x)^2+2*q*\x+r)/(2*p)},\x);可以画出c.
如果$n=0$,c'是竖直的抛物线,用\draw[rotate=θ]plot[smooth](\x,{-(m*(\x)^2+2*p*\x+r)/(2*q)});可以画出c.
如果$mn>0$,c'是椭圆,用\draw[rotate=θ](α,β)ellipse(a and b);可以画出c.其中
$a=\sqrt{-\frac{\alpha  p+\beta  q+r}{m}},b=\sqrt{-\frac{\alpha  p+\beta  q+r}{n}},α=-\frac{p}{m},β=-\frac{q}{n}$.
如果$mn<0$,分两种情况:
如果c'是水平的双曲线,用\draw[rotate=θ]plot[smooth,variable=\t,domain=-80:80]({a*sec(\t)+α},{b*tan(\t)+β})plot[rotate=θ,variable=\t,domain=100:260]({a*sec(\t)+α},{b*tan(\t)+β});可以画出c.其中
$a=\sqrt{-\frac{\alpha  p+\beta  q+r}{m}},b=\sqrt{\frac{\alpha  p+\beta  q+r}{n}},α=-\frac{p}{m},β=-\frac{q}{n}$.
如果c'是竖直的双曲线,用\draw[rotate=θ]plot[smooth,variable=\t,domain=-80:80]({a*tan(\t)+α},{b*sec(\t)+β})plot[rotate=θ,variable=\t,domain=100:260]({a*tan(\t)+α},{b*sec(\t)+β});可以画出c.其中
$a=\sqrt{\frac{\alpha  p+\beta  q+r}{m}},b=\sqrt{-\frac{\alpha  p+\beta  q+r}{n}},α=-\frac{p}{m},β=-\frac{q}{n}$.
注1:这里的α,β是圆锥曲线c'的中心的坐标.
注2:因为直线/双曲线/抛物线是无界的,不好确定参数范围(domain),需要手动调整.
注3:在数值计算中,对于抛物线,m精确地等于0是不可能的,需改为abs(m)<0.0001*abs(n),否则会输出一个非常巨大的椭圆,然后tikz就会报错:超出页边距.
注4:如果在upmath.me画不出来的话可能是运行时间过长.可换用texlive.net或在本地编译.
注5:把"点"放在最后一个执行是为了当其他对象与点重叠时,点能够被画在最上层.
"多边形"必须放在"线段"前执行是因为"正多边形"会产生新的线段,需要作为新元素加到xml里面,GeoGebra中由"多边形"生成的线段默认是隐藏的,只显示多边形的那个浅色的边界.
"圆弧","扇形","三点圆弧","三点扇形"必须放在conic前执行,因为它们会删除已经处理过的conicpart元素,这样就不会在"圆"中重复处理了.

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-1-2 13:56
本帖最后由 hbghlyj 于 2022-8-30 20:51 编辑

计划任务:
添加到按钮.
直角符号
正确解析label offset
用tikzplotlib添加对于导出GeoGebra中的隐函数图像的支持.
根据视窗设置"直线","抛物线","双曲线","函数y=f(x)图像"的参数范围.

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-1-2 16:09
本帖最后由 hbghlyj 于 2022-1-2 17:31 编辑 对于GeoGebra生成的圆锥曲线的特征向量组成的正交矩阵,它的行列式是±1,对于它的行列式的符号,发现一个规律:
对于椭圆和双曲线,行列式为+1,即有x0*y1-y0*x1=+1.
<command name="Ellipse">
<input a0="G" a1="H" a2="I"/>
<output a0="e"/>
</command>
<element type="conic" label="e">
<show object="true" label="false"/>
<objColor r="0" g="0" b="0" alpha="0.0"/>
<layer val="0"/>
<labelMode val="0"/>
<lineStyle thickness="5" type="0" typeHidden="1" opacity="178"/>
<eigenvectors x0="0.005494422557933674" y0="-0.9999849055464562" z0="1.0" x1="0.9999849055464562" y1="0.005494422557933674" z1="1.0"/>
<matrix A0="638.3313137673149" A1="3.103935853879297" A2="9093.325647958063" A3="3.4903656909170424" A4="-2424.2570063973167" A5="-18.68226392383849"/>
<eqnStyle style="implicit"/>
</element>
对于抛物线,行列式为-1,即有x0*y1-y0*x1=-1.
<command name="Parabola">
<input a0="C" a1="f"/>
<output a0="c"/>
</command>
<element type="conic" label="c">
<show object="true" label="false"/>
<objColor r="0" g="0" b="0" alpha="0.0"/>
<layer val="0"/>
<labelMode val="0"/>
<lineStyle thickness="5" type="0" typeHidden="1" opacity="178"/>
<eigenvectors x0="-0.0028489912866555226" y0="0.999995941616089" z0="1.0" x1="0.999995941616089" y1="0.0028489912866555226" z1="1.0"/>
<matrix A0="1.16114001617187" A1="9.424761293917002E-6" A2="64.0" A3="0.00330809121416487" A4="-4.95459158868804" A5="-9.857259160368457"/>
<eqnStyle style="implicit"/>
</element>
应该是GeoGebra内部的逻辑,我也不懂

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-1-2 19:21
本帖最后由 hbghlyj 于 2022-1-3 14:44 编辑 现在还没搞懂geogebra.xml中的视窗数据 如果有了视窗数据,就可以让python计算直线与矩形视窗的交点,从而确定参数范围了.
GeoGebra数据类型
GeoGebra xml资料
视窗的x最小值,x最大值,y最小值,y最大值在哪里啊?
view的参数代表什么意思啊

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-1-6 17:15
原来GeoGebra使用的是齐次坐标
二维点的坐标计算方法是$\left(\frac xz,\frac yz\right)$
自由点的z坐标是1.0,导出的点的z坐标是一个实数,z坐标为0时是无穷远点
help.geogebra.org/topic/looking-for-more-docs-on-xml-contents
例如

<command name="Intersect">
        <input a0="f" a1="h"/>
        <output a0="A"/>
</command>
<element type="point" label="A">
        <show object="true" label="true"/>
        <objColor r="68" g="68" b="68" alpha="0.0"/>
        <layer val="0"/>
        <labelOffset x="10" y="6"/>
        <labelMode val="0"/>
        <coords x="2.0468137349164426" y="4.702439735377867" z="2.871920930232597"/>
        <pointSize val="4"/>
        <pointStyle val="0"/>
</element>

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-1-6 18:29
本帖最后由 hbghlyj 于 2022-1-7 18:08 编辑 GeoGebra xml的label offset(标签偏移量)的单位好像比坐标单位小¯\_(ツ)_/¯
它好像是,程序先确定一个标签位置,如果用户修改就加上xshift,yshift数据,这些数据是"相对偏移量",不是以被标记点为原点,而是以"程序预设的标签位置"为原点,但是"程序预设标签"是什么机制,还没弄懂跟窗口有一定关系,(在一个限度范围内)GeoGebra会把点的标签推移使得它在窗口内.

730

主题

1万

回帖

9万

积分

积分
93623
QQ

显示全部楼层

kuing 发表于 2022-1-7 00:43
可不可以让小数点后只保留三位?现在都十几位根本没必要啊,浪费资源……

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-1-7 04:01
回复 8# kuing
可以啊.您修改一下脚本,把每个坐标套上一个round(x,3)就行了

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-1-7 04:29
本帖最后由 hbghlyj 于 2022-1-7 04:35 编辑 试图破译视窗的大小window="100,100,600,400"应该是屏幕的单位,怎么转换到坐标呢
<views>
<view id="4097" visible="false" inframe="false" stylebar="true" location="1,1,1,1" size="400" window="100,100,700,550"/>
<view id="512" toolbar="0 | 1 501 5 19 , 67 | 2 15 45 18 , 7 37 | 514 3 9 , 13 44 , 47 | 16 51 | 551 550 11 , 20 22 21 23 , 55 56 57 , 12 | 69 | 510 511 , 512 513 | 533 531 , 534 532 , 522 523 , 537 536 , 535 , 538 | 521 520 | 36 , 38 49 560 | 571 30 29 570 31 33 | 17 | 540 40 41 42 , 27 28 35 , 6 , 502" visible="false" inframe="false" stylebar="false" location="1,1,1" size="500" window="100,100,600,400"/>
<view id="4" toolbar="0 || 2020 , 2021 , 2022 , 66 || 2001 , 2003 , 2002 , 2004 , 2005 || 2040 , 2041 , 2042 , 2044 , 2043" visible="false" inframe="false" stylebar="false" location="1,1" size="300" window="100,100,600,400"/>
<view id="8" toolbar="1001 | 1002 | 1003 || 1005 | 1004 || 1006 | 1007 | 1010 || 1008 1009 || 66 68 || 6" visible="false" inframe="false" stylebar="false" location="1,3" size="300" window="100,100,600,400"/>
<view id="1" visible="true" inframe="false" stylebar="true" location="1" size="339" window="100,100,600,400"/>
<view id="2" visible="false" inframe="false" stylebar="false" location="3" size="200" tab="ALGEBRA" window="100,100,250,400"/>
<view id="16" visible="false" inframe="false" stylebar="false" location="1" size="150" window="50,50,500,500"/>
<view id="32" visible="false" inframe="false" stylebar="true" location="1" size="150" window="50,50,500,500"/>
<view id="64" toolbar="0" visible="false" inframe="false" stylebar="false" location="1" size="150" window="50,50,500,500"/>
<view id="70" toolbar="0 || 2020 || 2021 || 2022" visible="false" inframe="false" stylebar="true" location="1" size="150" window="50,50,500,500"/>
</views>




scale="130.30303030302707" yscale="130.3030303030271"这个应该是换算坐标的比值?
<euclidianView>
<viewNumber viewNo="1"/>
<size width="339" height="203"/>
<coordSystem xZero="155.3030303030326" yZero="380.026315789466" scale="130.30303030302707" yscale="130.3030303030271"/>
<evSettings axes="false" grid="false" gridIsBold="false" pointCapturing="3" rightAngleStyle="1" checkboxSize="26" gridType="3" lockedAxesRatio="1.0"/>
<bgColor r="255" g="255" b="255"/>
<axesColor r="0" g="0" b="0"/>
<gridColor r="192" g="192" b="192"/>
<lineStyle axes="1" grid="0"/>
<axis id="0" show="false" label="" unitLabel="" tickStyle="1" showNumbers="false"/>
<axis id="1" show="false" label="" unitLabel="" tickStyle="1" showNumbers="false"/>
</euclidianView>

3149

主题

8387

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65396
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2022-1-7 05:02
本帖最后由 hbghlyj 于 2022-1-7 05:09 编辑 angle元素的arcsize属性好像也是用屏幕单位困难是怎么把屏幕单位换算成坐标
<command name="Angle">
<input a0="F" a1="E" a2="B"/>
<output a0="α"/>
</command>
<element type="angle" label="α">
<angleStyle val="0"/>
<value val="1.5707963267948968"/>
<show object="true" label="false"/>
<objColor r="0" g="100" b="0" alpha="0.10000000149011612"/>
<layer val="0"/>
<labelMode val="1"/>
<lineStyle thickness="5" type="0" typeHidden="2" opacity="178"/>
<arcSize val="30"/>
</element>
现在我的做法是用\anglesize这个手动调整用于标注角的弧的大小

手机版|悠闲数学娱乐论坛(第3版)

GMT+8, 2025-3-4 16:05

Powered by Discuz!

× 快速回复 返回顶部 返回列表