@@ -53,83 +53,129 @@ const PGplots = {
5353 board . containerObj . after ( descriptionSpan ) ;
5454 board . containerObj . setAttribute ( 'aria-describedby' , descriptionSpan . id ) ;
5555
56- // Convert a decimal number into a fraction or mixed number. This is basically the JXG.toFraction method
57- // except that the "mixed" parameter is added, and it returns an improper fraction if mixed is false.
58- const toFraction = ( x , useTeX , mixed , order ) => {
59- const arr = JXG . Math . decToFraction ( x , order ) ;
60-
61- if ( arr [ 1 ] === 0 && arr [ 2 ] === 0 ) {
62- return '0' ;
63- } else {
64- let str = '' ;
65- // Sign
66- if ( arr [ 0 ] < 0 ) str += '-' ;
67- if ( arr [ 2 ] === 0 ) {
68- // Integer
69- str += arr [ 1 ] ;
70- } else if ( ! ( arr [ 2 ] === 1 && arr [ 3 ] === 1 ) ) {
71- // Proper fraction
72- if ( mixed ) {
73- if ( arr [ 1 ] !== 0 ) str += arr [ 1 ] + ' ' ;
74- if ( useTeX ) str += `\\frac{${ arr [ 2 ] } }{${ arr [ 3 ] } }` ;
75- else str += `${ arr [ 2 ] } /${ arr [ 3 ] } ` ;
56+ // This object is passed to the plotContents method as its second argument and exposes these methods (and
57+ // potentially other things in the future) to the code that is called in that method. So the JavaScript code
58+ // generated in JSXGraph.pm can use these methods.
59+ const plot = {
60+ // Convert a decimal number into a fraction or mixed number. This is basically the JXG.toFraction method
61+ // except that the "mixed" parameter is added, and it returns an improper fraction if mixed is false.
62+ toFraction ( x , useTeX , mixed , order ) {
63+ const arr = JXG . Math . decToFraction ( x , order ) ;
64+
65+ if ( arr [ 1 ] === 0 && arr [ 2 ] === 0 ) {
66+ return '0' ;
67+ } else {
68+ let str = '' ;
69+ // Sign
70+ if ( arr [ 0 ] < 0 ) str += '-' ;
71+ if ( arr [ 2 ] === 0 ) {
72+ // Integer
73+ str += arr [ 1 ] ;
74+ } else if ( ! ( arr [ 2 ] === 1 && arr [ 3 ] === 1 ) ) {
75+ // Proper fraction
76+ if ( mixed ) {
77+ if ( arr [ 1 ] !== 0 ) str += arr [ 1 ] + ' ' ;
78+ if ( useTeX ) str += `\\frac{${ arr [ 2 ] } }{${ arr [ 3 ] } }` ;
79+ else str += `${ arr [ 2 ] } /${ arr [ 3 ] } ` ;
80+ } else {
81+ if ( useTeX ) str += `\\frac{${ arr [ 3 ] * arr [ 1 ] + arr [ 2 ] } }{${ arr [ 3 ] } }` ;
82+ else str += `${ arr [ 3 ] * arr [ 1 ] + arr [ 2 ] } /${ arr [ 3 ] } ` ;
83+ }
84+ }
85+ return str ;
86+ }
87+ } ,
88+
89+ // Override the default axis generateLabelText method so that 0 is displayed
90+ // using MathJax if the axis is configured to show tick labels using MathJax.
91+ generateLabelText ( tick , zero , value ) {
92+ if ( JXG . exists ( value ) ) return this . formatLabelText ( value ) ;
93+ const distance = this . getDistanceFromZero ( zero , tick ) ;
94+ return this . formatLabelText ( Math . abs ( distance ) < JXG . Math . eps ? 0 : distance / this . visProp . scale ) ;
95+ } ,
96+
97+ trimTrailingZeros ( value ) {
98+ if ( value . indexOf ( '.' ) > - 1 && value . endsWith ( '0' ) ) {
99+ value = value . replace ( / 0 + $ / , '' ) ;
100+ // Remove the decimal if it is now at the end.
101+ value = value . replace ( / \. $ / , '' ) ;
102+ }
103+ return value ;
104+ } ,
105+
106+ // Override the formatLabelText method for the axes ticks so that
107+ // better number formats can be used for tick labels.
108+ formatLabelText ( value ) {
109+ let labelText ;
110+
111+ if ( JXG . isNumber ( value ) ) {
112+ if ( this . visProp . label . format === 'fraction' || this . visProp . label . format === 'mixed' ) {
113+ labelText = plot . toFraction (
114+ value ,
115+ this . visProp . label . usemathjax ,
116+ this . visProp . label . format === 'mixed'
117+ ) ;
118+ } else if ( this . visProp . label . format === 'scinot' ) {
119+ const [ mantissa , exponent ] = value . toExponential ( this . visProp . digits ) . toString ( ) . split ( 'e' ) ;
120+ labelText = this . visProp . label . usemathjax
121+ ? `${ plot . trimTrailingZeros ( mantissa ) } \\cdot 10^{${ exponent } }`
122+ : `${ plot . trimTrailingZeros ( mantissa ) } x 10^${ exponent } ` ;
76123 } else {
77- if ( useTeX ) str += `\\frac{${ arr [ 3 ] * arr [ 1 ] + arr [ 2 ] } }{${ arr [ 3 ] } }` ;
78- else str += `${ arr [ 3 ] * arr [ 1 ] + arr [ 2 ] } /${ arr [ 3 ] } ` ;
124+ labelText = plot . trimTrailingZeros ( value . toFixed ( this . visProp . digits ) . toString ( ) ) ;
79125 }
126+ } else {
127+ labelText = value . toString ( ) ;
80128 }
81- return str ;
82- }
83- } ;
84129
85- // Override the default axis generateLabelText method so that 0 is displayed
86- // using MathJax if the axis is configured to show tick labels using MathJax.
87- const generateLabelText = function ( tick , zero , value ) {
88- if ( JXG . exists ( value ) ) return this . formatLabelText ( value ) ;
89- const distance = this . getDistanceFromZero ( zero , tick ) ;
90- return this . formatLabelText ( Math . abs ( distance ) < JXG . Math . eps ? 0 : distance / this . visProp . scale ) ;
91- } ;
130+ if ( this . visProp . scalesymbol . length > 0 ) {
131+ if ( labelText === '1' ) labelText = this . visProp . scalesymbol ;
132+ else if ( labelText === '-1' ) labelText = `-${ this . visProp . scalesymbol } ` ;
133+ else if ( labelText !== '0' ) labelText = labelText + this . visProp . scalesymbol ;
134+ }
92135
93- const trimTrailingZeros = ( value ) => {
94- if ( value . indexOf ( '.' ) > - 1 && value . endsWith ( '0' ) ) {
95- value = value . replace ( / 0 + $ / , '' ) ;
96- // Remove the decimal if it is now at the end.
97- value = value . replace ( / \. $ / , '' ) ;
98- }
99- return value ;
100- } ;
136+ return this . visProp . label . usemathjax ? `\\( ${ labelText } \\)` : labelText ;
137+ } ,
138+
139+ createLabel ( x , y , text , options ) {
140+ const anchor = options . angleAnchor ;
141+ delete options . angleAnchor ;
142+ const rotate = options . rotate ;
143+ delete options . rotate ;
101144
102- // Override the formatLabelText method for the axes ticks so that
103- // better number formats can be used for tick labels.
104- const formatLabelText = function ( value ) {
105- let labelText ;
106-
107- if ( JXG . isNumber ( value ) ) {
108- if ( this . visProp . label . format === 'fraction' || this . visProp . label . format === 'mixed' ) {
109- labelText = toFraction (
110- value ,
111- this . visProp . label . usemathjax ,
112- this . visProp . label . format === 'mixed'
145+ const textElement = board . create ( 'text' , [ x , y , text ] , options ) ;
146+
147+ if ( typeof anchor !== 'undefined' ) {
148+ const cosA = Math . cos ( ( anchor * Math . PI ) / 180 ) ;
149+ const sinA = Math . sin ( ( anchor * Math . PI ) / 180 ) ;
150+
151+ const transform = board . create (
152+ 'transform' ,
153+ [
154+ ( ) => {
155+ const [ w , h ] = textElement . getSize ( ) ;
156+ return (
157+ ( w * Math . abs ( sinA ) > h * Math . abs ( cosA )
158+ ? ( - h / 2 / Math . abs ( sinA ) ) * cosA
159+ : ( ( cosA < 0 ? 1 : - 1 ) * w ) / 2 ) / board . unitX
160+ ) ;
161+ } ,
162+ ( ) => {
163+ const [ w , h ] = textElement . getSize ( ) ;
164+ return (
165+ ( w * Math . abs ( sinA ) > h * Math . abs ( cosA )
166+ ? ( ( sinA < 0 ? 1 : - 1 ) * h ) / 2
167+ : ( - w / 2 / Math . abs ( cosA ) ) * sinA ) / board . unitY
168+ ) ;
169+ }
170+ ] ,
171+ { type : 'translate' }
113172 ) ;
114- } else if ( this . visProp . label . format === 'scinot' ) {
115- const [ mantissa , exponent ] = value . toExponential ( this . visProp . digits ) . toString ( ) . split ( 'e' ) ;
116- labelText = this . visProp . label . usemathjax
117- ? `${ trimTrailingZeros ( mantissa ) } \\cdot 10^{${ exponent } }`
118- : `${ trimTrailingZeros ( mantissa ) } x 10^${ exponent } ` ;
119- } else {
120- labelText = trimTrailingZeros ( value . toFixed ( this . visProp . digits ) . toString ( ) ) ;
173+ transform . bindTo ( textElement ) ;
121174 }
122- } else {
123- labelText = value . toString ( ) ;
124- }
175+ if ( rotate ) textElement . addRotation ( rotate ) ;
125176
126- if ( this . visProp . scalesymbol . length > 0 ) {
127- if ( labelText === '1' ) labelText = this . visProp . scalesymbol ;
128- else if ( labelText === '-1' ) labelText = `-${ this . visProp . scalesymbol } ` ;
129- else if ( labelText !== '0' ) labelText = labelText + this . visProp . scalesymbol ;
177+ return textElement ;
130178 }
131-
132- return this . visProp . label . usemathjax ? `\\(${ labelText } \\)` : labelText ;
133179 } ;
134180
135181 board . suspendUpdate ( ) ;
@@ -329,8 +375,8 @@ const PGplots = {
329375 options . xAxis . overrideOptions ?? { }
330376 )
331377 ) ;
332- xAxis . defaultTicks . generateLabelText = generateLabelText ;
333- xAxis . defaultTicks . formatLabelText = formatLabelText ;
378+ xAxis . defaultTicks . generateLabelText = plot . generateLabelText ;
379+ xAxis . defaultTicks . formatLabelText = plot . formatLabelText ;
334380
335381 if ( options . xAxis . location !== 'middle' ) {
336382 board . create (
@@ -424,8 +470,8 @@ const PGplots = {
424470 options . yAxis . overrideOptions ?? { }
425471 )
426472 ) ;
427- yAxis . defaultTicks . generateLabelText = generateLabelText ;
428- yAxis . defaultTicks . formatLabelText = formatLabelText ;
473+ yAxis . defaultTicks . generateLabelText = plot . generateLabelText ;
474+ yAxis . defaultTicks . formatLabelText = plot . formatLabelText ;
429475
430476 if ( options . yAxis . location !== 'center' ) {
431477 board . create (
@@ -448,7 +494,7 @@ const PGplots = {
448494 }
449495 }
450496
451- plotContents ( board ) ;
497+ plotContents ( board , plot ) ;
452498
453499 board . unsuspendUpdate ( ) ;
454500
0 commit comments