(ご注意)
本記事で紹介する情報は、web版GeoGebra(クラシック6)にのみ有効です。ローカル版GeoGebraではうまく機能しません。また、記事公開後のGeoGebraのアップデートにより、正常に機能しなくなる可能性があります。あらかじめご了承ください。
課題
スライダーaを、最小値-1、最大値1、増分0.001で作成する。
リストlist1を、以下の定義で作成する。
list1 = Sort(Join({{1 / 2, sqrt(2) / 2, sqrt(3) / 2, -1 / 2, (-sqrt(2)) / 2, (-sqrt(3)) / 2}, Unique(Flatten(Zip(Sequence(α π, α, -4, 4, β), β, {1, 1 / 4, 1 / 6})))}))
上記list1は、πや√、分数を用いた数値が混在したリストである。
スライダーaを動かして、list1の値付近に近づいたとき、その値にaを吸い付かせたい。
そして、ルート、π、分数を含む値に吸い付かせたとき、aのラベルを、(小数表示ではなく)ルート、π、分数を用いた表記(数式表示)にしたい。
以上の挙動を実現したい。
以前、下記記事で紹介した方法は、π表記に対応していなかった。
http://usidesu.hatenablog.com/entry/2020/10/28/034229
今回、CASに数式を渡して、LaTeX表記を取得する方法をとったことで、πにも対応した。
サンプル
スライダー吸い付きCAS版 – GeoGebra
つくりかた
※aとlist1は作成済みであることが前提です。
step1
下記のJavaScriptを、グローバルJavaScript欄にコピペする。
function setJavaScript(objName, handler, script) {
var isexist = ggbApplet.exists(objName);
if (isexist) {
var objType = ggbApplet.getObjectType(objName);
var onwhat = (handler == 'click') ? 'val' : 'onUpdate';
var xmlText = '<element type=\"' + objType + '\" label=\"' + objName + '\"><javascript ' + onwhat + '=\"' + script + '\"/></element>';
ggbApplet.evalXML(xmlText);
}
}
function getSliderInfo(sliderName) {
var xmlStr = ggbApplet.getXML(sliderName);
var parser = new DOMParser();
var dom = parser.parseFromString(xmlStr, "text/xml");
var sld = dom.getElementsByTagName('slider');
if (sld.length == 0) {
return undefined;
}
var output = new Object();
for (var k = 0; k < sld[0].attributes.length; k++) {
var nameText = sld[0].attributes[k].name;
var val = sld[0].attributes[k].value;
if(val=='true'){output[nameText] = true;}
else if(val=='false'){output[nameText] = false;}
else{output[nameText] = ggbApplet.getValue(val);}
}
var startX = output.x;
var startY = output.y;
if (output.absoluteScreenLocation) {
startX = ggbApplet.getValue('x(Corner[1])+(x(Corner[3])-x(Corner[1]))*((' + startX + '+1)/(x(Corner[5])+2))');
startY = ggbApplet.getValue('y(Corner[3])-(y(Corner[3])-y(Corner[1]))*((' + startY + '+1)/(y(Corner[5])+2))');
}
var endX = output.x + (output.horizontal ? output.width : 0);
var endY = output.y + (output.horizontal ? 0 : output.width);
if (output.absoluteScreenLocation) {
endX = ggbApplet.getValue(startX + '+(x(Corner[3])-x(Corner[1]))*((' + (output.horizontal ? output.width : 0) + ')/(x(Corner[5])+2))');
endY = ggbApplet.getValue(startY + '+(y(Corner[3])-y(Corner[1]))*((' + (output.horizontal ? 0 : output.width) + ')/(y(Corner[5])+2))');
}
output['startX'] = startX;
output['startY'] = startY;
output['endX'] = endX;
output['endY'] = endY;
return output;
}
function makeSliderPoint(sliderName, listName) {
var info = getSliderInfo(sliderName);
if (!info) {
return undefined;
}
ggbApplet.evalCommand('StartOf' + sliderName + '=(0,0)');
ggbApplet.evalCommand('GoalOf' + sliderName + '=(5,0)');
var xmlTextMin = '<expression label=\"minOf' + sliderName + '\" exp=\"{0}\"/><element type=\"list\" label=\"minOf' + sliderName + '\"></element>';
ggbApplet.evalXML(xmlTextMin);
var xmlTextMax = '<expression label=\"maxOf' + sliderName + '\" exp=\"{0}\"/><element type=\"list\" label=\"maxOf' + sliderName + '\"></element>';
ggbApplet.evalXML(xmlTextMax);
ggbApplet.evalCommand('PointOf' + sliderName + ' = Dilate(GoalOf' + sliderName + ', (' + sliderName + ' - minOf' + sliderName + '(1)) / (maxOf' + sliderName + '(1) - minOf' + sliderName + '(1)), StartOf' + sliderName + ')');
}
function evalSliderInfo(sliderName, listName) {
var info = getSliderInfo(sliderName);
if(!info){return;}
ggbApplet.evalCommand('SetValue[StartOf' + sliderName + ',(' + info.startX + ',' + info.startY + ')]');
ggbApplet.evalCommand('SetValue[GoalOf' + sliderName + ',(' + info.endX + ',' + info.endY + ')]');
ggbApplet.evalCommand('SetValue[minOf' + sliderName + ',{' + info.min + '}]');
ggbApplet.evalCommand('SetValue[maxOf' + sliderName + ',{' + info.max + '}]');
}
function renamePartsOfSlider(oldName,newName){
var headNameArr = ['indexOf','commonTextOf','formulaTextOf','StartOf','GoalOf','minOf','maxOf','PointOf','combinedTextOf'];
for(var k=0; k<headNameArr.length; k++){
ggbApplet.renameObject(headNameArr[k]+oldName, headNameArr[k]+newName);
}
}
function sliderUpdateScript(listName,diff){
var oldName = ggbApplet.getValueString('nameOfSlider');
ggbApplet.evalCommand('UpdateConstruction[]');
var newName = ggbApplet.getValueString('nameOfSlider');
if(oldName!=newName){
renamePartsOfSlider(oldName,newName);
return;
}
ggbApplet.evalCommand('If[abs('+newName+'-x(ClosestPoint(Zip((α, 0), α, '+listName+'), ('+newName+', 0))))<'+diff+',SetValue['+newName+',x(ClosestPoint(Zip((α, 0), α, '+listName+'), ('+newName+', 0)))]]');
evalSliderInfo(newName, listName);
}
function makeLabelText(sliderName,listName){
var equationText = ggbApplet.getDefinitionString(listName);
var casNum = ggbApplet.getCASObjectNumber()+1;
ggbApplet.evalCommand('$'+casNum+'='+equationText);
ggbApplet.evalCommand('ftOf'+listName+'=FormulaText[$'+casNum+']');
ggbApplet.evalCommand('cfOf'+listName+'=CopyFreeObject[ftOf'+listName+']');
var rawText = ggbApplet.getValueString('ftOf'+listName);
var lengthOfRawText = rawText.length;
var centerText = rawText.slice(7,lengthOfRawText-9);
var wrappedText = '\"'+centerText+'\"';
var replacedText = wrappedText.replace(/,/g, '\",\"');
ggbApplet.evalCommand('latexTextListOf'+listName+'={'+replacedText+'}');
ggbApplet.evalCommand('indexOf'+sliderName+'=If('+sliderName+' ≟ x(ClosestPoint(Zip((α, 0), α, '+listName+'), ('+sliderName+', 0))), IndexOf('+sliderName+', '+listName+'), 1)');
ggbApplet.evalCommand('formulaTextOf'+sliderName+'=\"\"+Element[latexTextListOf'+listName+',indexOf'+sliderName+']');
var xmlformulaTextLatex = '<element type=\"text\" label=\"formulaTextOf' + sliderName + '\"><isLaTeX val=\"true\"/></element>';
ggbApplet.evalXML(xmlformulaTextLatex);
ggbApplet.evalCommand('commonTextOf' + sliderName + '=\"\"+'+sliderName);
var xmlCommonTextLatex = '<element type=\"text\" label=\"commonTextOf' + sliderName + '\"><isLaTeX val=\"true\"/></element>';
ggbApplet.evalXML(xmlCommonTextLatex);
ggbApplet.evalCommand('combinedTextOf' + sliderName + ' = If(' + sliderName + ' ≟ x(ClosestPoint(Zip((α, 0), α, ' + listName + '), (' + sliderName + ', 0))), formulaTextOf' + sliderName + ', commonTextOf' + sliderName + ')');
var xmlCombinedTextLatex = '<element type=\"text\" label=\"combinedTextOf' + sliderName + '\"><isLaTeX val=\"true\"/></element>';
ggbApplet.evalXML(xmlCombinedTextLatex);
}
function setTextStartPoint(objName, expText) {
var isexist = ggbApplet.exists(objName);
if (isexist) {
var objType = ggbApplet.getObjectType(objName);
var xmlText = '<element type=\"' + objType + '\" label=\"' + objName + '\"><startPoint exp=\"' + expText + '\"/></element>';
ggbApplet.evalXML(xmlText);
}
}
function snapSliderToListAndMakeFormulaTextLabel(sliderName, listName, diff) {
makeSliderPoint(sliderName, listName);
evalSliderInfo(sliderName, listName);
ggbApplet.evalCommand('nameOfSlider=Name('+sliderName+')');
setJavaScript(sliderName, 'update', 'sliderUpdateScript("'+listName+'",'+diff+');');
makeLabelText(sliderName, listName);
setTextStartPoint('formulaTextOf' + sliderName, 'PointOf' + sliderName);
setTextStartPoint('commonTextOf' + sliderName, 'PointOf' + sliderName);
setTextStartPoint('combinedTextOf' + sliderName, 'PointOf' + sliderName);
ggbApplet.evalCommand('SetValue[' + sliderName + ',' + listName + '(1)]');
ggbApplet.setVisible('StartOf' + sliderName, false);
ggbApplet.setVisible('GoalOf' + sliderName, false);
ggbApplet.setVisible('minOf' + sliderName, false);
ggbApplet.setVisible('maxOf' + sliderName, false);
ggbApplet.setVisible('PointOf' + sliderName, false);
ggbApplet.setVisible('formulaTextOf' + sliderName, false);
ggbApplet.setVisible('commonTextOf' + sliderName, false);
ggbApplet.setVisible('indexOf' + sliderName, false);
ggbApplet.setVisible('ftOf' + listName, false);
ggbApplet.setVisible('cfOf' + listName, false);
ggbApplet.setVisible('latexTextListOf' + listName, false);
ggbApplet.setVisible('nameOfSlider', false);
ggbApplet.setAuxiliary('StartOf' + sliderName, true);
ggbApplet.setAuxiliary('GoalOf' + sliderName, true);
ggbApplet.setAuxiliary('minOf' + sliderName, true);
ggbApplet.setAuxiliary('maxOf' + sliderName, true);
ggbApplet.setAuxiliary('PointOf' + sliderName, true);
ggbApplet.setAuxiliary('formulaTextOf' + sliderName, true);
ggbApplet.setAuxiliary('commonTextOf' + sliderName, true);
ggbApplet.setAuxiliary('indexOf' + sliderName, true);
ggbApplet.setAuxiliary('ftOf' + listName, true);
ggbApplet.setAuxiliary('cfOf' + listName, true);
ggbApplet.setAuxiliary('latexTextListOf' + listName, true);
ggbApplet.setAuxiliary('nameOfSlider', true);
ggbApplet.setLabelVisible('' + sliderName, false);
alert('実行完了しました。');
}
step2
ボタンを作成し、そのOn Click ハンドラに、以下のスクリプトを記述する。
snapSliderToListAndMakeFormulaTextLabel('a','list1', 0.1);
なお、ハンドラ下部の「GeoGebra Script」を「JavaScript」に変更するのを忘れないよう、注意されたい。
step3
アプレットを保存し、再び開く。これによって、step1で作ったグローバルJavaScriptが読み込まれる。
step4
ボタンをクリックする。これによって、目的の挙動が実現される。作成されたラベルは、テキストオブジェクトであり、適宜スタイルの調整が可能である。
list1の要素や、スライダーの名前は、自由に変更可能である。
なお、複数のスライダーには対応していない。
吸い付き感度の調整
スライダーのOn Updateスクリプト内の数字は、吸い付きの感度を表す。ここを変更することで、吸い付き感度を調整できる。