1 /*
  2     Copyright 2008-2011
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Heiko Vogel
  7         Alfred Wassermann,
  8 
  9     This file is part of JSXGraph.
 10 
 11     JSXGraph is free software: you can redistribute it and/or modify
 12     it under the terms of the GNU Lesser General Public License as published by
 13     the Free Software Foundation, either version 3 of the License, or
 14     (at your option) any later version.
 15 
 16     JSXGraph is distributed in the hope that it will be useful,
 17     but WITHOUT ANY WARRANTY; without even the implied warranty of
 18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 19     GNU Lesser General Public License for more details.
 20 
 21     You should have received a copy of the GNU Lesser General Public License
 22     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 23 */
 24 
 25 JXG.extend(JXG, {
 26 
 27     GENTYPE_ABC: 1, // unused
 28     GENTYPE_AXIS: 2,
 29     GENTYPE_MID: 3,
 30     GENTYPE_REFLECTION: 4,
 31     GENTYPE_MIRRORPOINT: 5,
 32     GENTYPE_TANGENT: 6,
 33     GENTYPE_PARALLEL: 7,
 34     GENTYPE_BISECTORLINES: 8,
 35     GENTYPE_PERPENDICULAR_BISECTOR: 9,
 36     GENTYPE_BISECTOR: 10,
 37     GENTYPE_NORMAL: 11,
 38     GENTYPE_POINT: 12,
 39     GENTYPE_GLIDER: 13,
 40     GENTYPE_INTERSECTION: 14,
 41     GENTYPE_CIRCLE: 15,
 42     GENTYPE_CIRCLE2POINTS: 16,
 43     GENTYPE_LINE: 17,
 44     GENTYPE_TRIANGLE: 18,
 45     GENTYPE_QUADRILATERAL: 19,
 46     GENTYPE_TEXT: 20,
 47     GENTYPE_POLYGON: 21,
 48     GENTYPE_REGULARPOLYGON: 22,
 49     GENTYPE_SECTOR: 23,
 50     GENTYPE_ANGLE: 24,
 51     GENTYPE_PLOT: 25,
 52     GENTYPE_SLIDER: 26,
 53     GENTYPE_XYZ: 27, // unused ...
 54     GENTYPE_JCODE: 28,
 55     GENTYPE_MOVEMENT: 29,
 56 
 57     // 30 ... 32 // unused ...
 58 
 59     GENTYPE_GRID: 33, // obsolete
 60 
 61     // 34 ... 39 // unused ...
 62 
 63     GENTYPE_DELETE: 41,
 64     GENTYPE_COPY: 42,
 65     GENTYPE_MIRROR: 43,
 66     GENTYPE_ROTATE: 44,
 67     GENTYPE_ABLATION: 45,
 68     GENTYPE_MIGRATE: 46,
 69     GENTYPE_TRANSFORM: 47,
 70 
 71     // 48 ... 50 // unused ...
 72 
 73     /*
 74      Important:
 75      ==========
 76 
 77      For being able to differentiate between the (GUI-specific) CTX and
 78      (CORE-specific) non-CTX steps, the non-CTX steps must not be changed
 79      to values > 50 !!!
 80      */
 81 
 82     GENTYPE_CTX_TYPE_G: 51,
 83     GENTYPE_CTX_TYPE_P: 52,
 84     GENTYPE_CTX_TRACE: 53,
 85     GENTYPE_CTX_VISIBILITY: 54,
 86     GENTYPE_CTX_CCVISIBILITY: 55,
 87     GENTYPE_CTX_MPVISIBILITY: 56,
 88     GENTYPE_CTX_WITHLABEL: 57,
 89     GENTYPE_CTX_SETLABEL: 58,
 90     GENTYPE_CTX_SETFIXED: 59,
 91     GENTYPE_CTX_STROKEWIDTH: 60,
 92     GENTYPE_CTX_LABELSIZE: 61,
 93     GENTYPE_CTX_SIZE: 62,
 94     GENTYPE_CTX_FACE: 63,
 95     GENTYPE_CTX_STRAIGHT: 64,
 96     GENTYPE_CTX_ARROW: 65,
 97     GENTYPE_CTX_COLOR: 66,
 98     GENTYPE_CTX_RADIUS: 67,
 99     GENTYPE_CTX_COORDS: 68,
100     GENTYPE_CTX_TEXT: 69,
101     GENTYPE_CTX_ANGLERADIUS: 70,
102     GENTYPE_CTX_DOTVISIBILITY: 71,
103     GENTYPE_CTX_FILLOPACITY: 72,
104 
105     SketchReader: {
106 
107         generator: {
108             toFixed: 0,
109             freeLine: false,
110             useGlider: false,
111             useSymbols: false
112         },
113         // configure the generator below
114 
115         generateJCodeMeta: function () {},
116 
117         id: function () {
118             return JXG.Util.genUUID();
119         },
120 
121         generateJCode: function (step, board, step_log) {
122 
123             // step has to be an objectliteral of the form: { type, args, src_ids, dest_sub_ids, dest_id }
124 
125             var options, assign, attrid, obj, type;
126 
127             var i, j, k, sub_id, str, str1, str2, objects, pid1, pid2, pid3, xstart, ystart, el, bo, arr,
128                 xy, sxy, sxyc, step2, copy_log = [];
129 
130             var set_str = '', reset_str = '', ctx_set_str = '', ctx_reset_str = '';
131 
132             options = JXG.SketchReader.generator;
133 
134             objects = board.objects;
135 
136             // print number -- helper to prepare numbers
137             // for printing, e.g. trim them with toFixed()
138 
139             var pn = function (v) {
140                 if (options.toFixed > 0)
141                     v = v.toFixed(options.toFixed);
142                 return v;
143             };
144 
145             var getObject = function (v) {
146                 var o;
147 
148                 if (options.useSymbols) {
149                     if (board.jc.sstack[0][v]) {
150                         o = board.jc.sstack[0][v];
151                     } else {
152                         o = objects[v];
153                     }
154                 } else {
155                     o = objects[v];
156                 }
157 
158                 return o;
159             };
160 
161             /* SKETCHBIN begin */
162 
163             assign = '';
164             attrid = 'id: \'' + step.dest_id + '\', ';
165 
166             if (JXG.exists(board) && options.useSymbols && step.type !== JXG.GENTYPE_ABLATION) {
167                 attrid = '';
168                 assign = step.dest_id + ' = ';
169 
170                 for (i = 0; i < step.src_ids.length; i++) {
171                     str = board.jc.findSymbol(getObject(step.src_ids[i]), 0); // Das Board wird hier immer benötigt!!!
172 
173                     if (str.length > 0) {
174                         step.src_ids[i] = str[0];
175                     }
176                 }
177             }
178 
179             /* SKETCHBIN end */
180 
181             if (step.type > 50)
182                 return JXG.SketchReader.generateJCodeMeta(step, board);
183 
184             switch (step.type) {
185 
186                 case JXG.GENTYPE_JCODE:
187                     set_str = step.args.code;
188                     break;
189 
190                 case JXG.GENTYPE_AXIS:
191                     set_str = step.args.name[0] + ' = point(' + pn(step.args.coords[0].usrCoords[1]) + ', ';
192                     set_str += pn(step.args.coords[0].usrCoords[2]) + ') <<id: \'' + step.dest_sub_ids[0] + '\', name: \'';
193                     set_str += step.args.name[0] + '\', fixed: true, priv: true, visible: false>>; ' + step.args.name[1];
194                     set_str += ' = point(' + pn(step.args.coords[1].usrCoords[1]) + ', ';
195                     set_str += pn(step.args.coords[1].usrCoords[2]) + ') <<id: \'' + step.dest_sub_ids[1] + '\', name: \'';
196                     set_str += step.args.name[1] + '\', fixed: true, priv: true, visible: false>>; ' + step.args.name[2];
197                     set_str += ' = point(' + pn(step.args.coords[2].usrCoords[1]) + ', ';
198                     set_str += pn(step.args.coords[2].usrCoords[2]) + ') <<id: \'' + step.dest_sub_ids[2] + '\', name: \'';
199                     set_str += step.args.name[2] + '\', fixed: true, priv: true, visible: false>>; ';
200 
201                     set_str += step.args.name[3] + ' = axis(' + step.args.name[0] + ', ' + step.args.name[1] + ') ';
202                     set_str += '<<id: \'' + step.dest_sub_ids[3] + '\', name: \'' + step.args.name[3] + '\', ticks: ';
203                     set_str += '<<minorHeight:0, majorHeight:10, ticksDistance: ' + JXG.Options.axisScaleX;
204                     set_str += ', drawLabels: true, drawZero: true>>>>; ';
205                     set_str += step.args.name[4] + ' = axis(' + step.args.name[0] + ', ' + step.args.name[2] + ') ';
206                     set_str += '<<id: \'' + step.dest_sub_ids[4] + '\', name: \'' + step.args.name[4] + '\', ticks: ';
207                     set_str += '<<minorHeight:0, majorHeight:10, ticksDistance: ' + JXG.Options.axisScaleY;
208                     set_str += ', drawLabels: true, drawZero: true>>>>; ';
209 
210                     set_str += step.dest_sub_ids[3] + '.visible = false; ';
211                     set_str += step.dest_sub_ids[4] + '.visible = false; ';
212 
213                     set_str += 'delete jxgBoard1_infobox; ';
214 
215                     reset_str = 'delete ' + step.dest_sub_ids[4] + '; delete ' + step.dest_sub_ids[3];
216                     reset_str += '; delete ' + step.dest_sub_ids[2] + '; ';
217                     reset_str += 'delete ' + step.dest_sub_ids[1] + '; delete ' + step.dest_sub_ids[0] + '; ';
218 
219                     break;
220 
221                 case JXG.GENTYPE_MID:
222                     set_str = assign + 'midpoint(' + step.src_ids[0] + ', ' + step.src_ids[1] + ') <<' + attrid;
223                     set_str += 'fillColor: \'' + step.args.fillColor + '\'>>; ';
224                     reset_str = 'delete ' + step.dest_id + '; ';
225                     break;
226 
227                 case JXG.GENTYPE_REFLECTION:
228                     set_str = assign + 'reflection(' + step.src_ids[0] + ', ' + step.src_ids[1] + ') <<' + attrid;
229                     set_str += 'fillColor: \'' + step.args.fillColor + '\'>>; ';
230                     reset_str = 'delete ' + step.dest_id + '; ';
231                     break;
232 
233                 case JXG.GENTYPE_MIRRORPOINT:
234                     set_str = assign + 'mirrorpoint(' + step.src_ids[1] + ', ' + step.src_ids[0] + ') <<' + attrid;
235                     set_str += 'fillColor: \'' + step.args.fillColor + '\'>>; ';
236                     reset_str = 'delete ' + step.dest_id + '; ';
237                     break;
238 
239                 case JXG.GENTYPE_TANGENT:
240                     if (step.args.create_point === true) {
241                         sub_id = step.dest_sub_ids[2];
242                         set_str = 'point(' + pn(step.args.usrCoords[1]) + ',' + pn(step.args.usrCoords[2]) + ') <<id: \'';
243                         set_str += sub_id + '\', fillColor: \'' + step.args.fillColor + '\'>>; ' + sub_id + '.glide(';
244                         set_str += step.src_ids[0] + '); ';
245                         reset_str = 'delete ' + sub_id + '; ';
246                     } else
247                         sub_id = step.src_ids[0];
248 
249                     set_str += assign + 'tangent(' + sub_id + ') <<' + attrid + 'point1: <<name: \'' + step.dest_sub_ids[0];
250                     set_str += '\', id: \'' + step.dest_sub_ids[0] + '\'>>, point2: <<name: \'' + step.dest_sub_ids[1];
251                     set_str += '\', id: \'' + step.dest_sub_ids[1] + '\'>> >>; ';
252                     reset_str = 'delete ' + step.dest_sub_ids[0] + '; ' + reset_str;
253                     reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[1] + '; ' + reset_str;
254                     break;
255 
256                 case JXG.GENTYPE_PARALLEL:
257                     if (step.args.create_point === true) {
258                         sub_id = step.dest_sub_ids[1];
259                         set_str = 'point(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]) + ') <<id: \'';
260                         set_str += sub_id + '\', name: \'\', visible: false, priv: true>>; ';
261                         reset_str = 'delete ' + sub_id + '; ';
262                     } else
263                         sub_id = step.src_ids[1];
264 
265                     set_str += assign + 'parallel(' + step.src_ids[0] + ', ' + sub_id + ') <<' + attrid + 'point: <<id: \'';
266                     set_str += step.dest_sub_ids[0] + '\', name: \'' + step.dest_sub_ids[0] + '\'>> >>; ';
267                     reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[0] + '; ' + reset_str;
268                     break;
269 
270                 case JXG.GENTYPE_BISECTORLINES:
271                     set_str = 'bisectorlines(' + step.src_ids[0] + ', ' + step.src_ids[1] + ') <<line1: <<id: \'';
272                     set_str = set_str + step.dest_sub_ids[2] + '\', point1: <<id: \'' + step.dest_sub_ids[1];
273                     set_str += '\', name: \'' + step.dest_sub_ids[1] + '\'>>, point2: <<id: \'' + step.dest_sub_ids[0];
274                     set_str += '\', name: \'' + step.dest_sub_ids[0] + '\'>>>>, line2: <<id: \'' + step.dest_sub_ids[5];
275                     set_str += '\', point1: <<id: \'' + step.dest_sub_ids[4] + '\', name: \'' + step.dest_sub_ids[4];
276                     set_str += '\'>>, point2: <<id: \'' + step.dest_sub_ids[3] + '\', name: \'' + step.dest_sub_ids[3];
277                     set_str += '\'>>>>>>; ';
278                     reset_str = 'delete ' + step.dest_sub_ids[5] + '; delete ' + step.dest_sub_ids[4] + '; delete ';
279                     reset_str += step.dest_sub_ids[3] + '; delete ' + step.dest_sub_ids[2] + '; delete ';
280                     reset_str += step.dest_sub_ids[1] + '; delete ' + step.dest_sub_ids[0] + '; ';
281                     break;
282 
283                 case JXG.GENTYPE_PERPENDICULAR_BISECTOR:
284                     if (step.args.create_line === true) {
285                         sub_id = step.dest_sub_ids[2];
286                         set_str = 'line(' + step.src_ids[0] + ', ' + step.src_ids[1] + ') <<id: \'' + sub_id;
287                         set_str += '\', visible: true>>; ';
288                         reset_str = 'delete ' + sub_id + '; ';
289                     } else
290                         sub_id = step.src_ids[2];
291 
292                     set_str += 'midpoint(' + step.src_ids[0] + ', ' + step.src_ids[1] + ') <<id: \'' + step.dest_sub_ids[0];
293                     set_str += '\', fillColor: \'' + step.args.fillColor + '\'>>; ';
294                     set_str += assign + 'normal(' + step.dest_sub_ids[0] + ', ' + sub_id + ') <<' + attrid;
295                     set_str += ' point: <<id: \'' + step.dest_sub_ids[1] + '\', name: \'' + step.dest_sub_ids[1];
296                     set_str += '\'>> >>; ';
297                     reset_str = 'delete ' + step.dest_sub_ids[0] + '; ' + reset_str;
298                     reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[1] + '; ' + reset_str;
299                     break;
300 
301                 case JXG.GENTYPE_BISECTOR:
302                     if (step.args.create_point === true) {
303                         // TODO: use "if (options.useGlider) {"
304                         
305                         // Projection to first line
306                         pid1 = step.dest_sub_ids[1];
307                         set_str = 'point(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]) + ') ';
308                         set_str += '<<id:\'' + pid1 + '\', ';
309                         set_str += 'name:\'\', priv:true, visible:false >>; ';
310                         set_str += pid1 + '.glide(' + step.src_ids[0] + ');\n';
311                         reset_str = 'delete ' + pid1 + '; ';
312 
313                         // Projection to second line
314                         pid2 = step.dest_sub_ids[2];
315                         set_str += 'point(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]) + ') ';
316                         set_str += '<<id:\'' + pid2 + '\', ';
317                         set_str += 'name:\'\', priv:true, visible:false >>; ';
318                         set_str += pid2 + '.glide(' + step.src_ids[1] + ');\n';
319                         reset_str += 'delete ' + pid2 + '; ';
320 
321                         if (step.args.create_intersection) {
322                             // intersection point
323                             pid3 = step.dest_sub_ids[3];
324                             set_str += 'intersection(' + step.src_ids[0] + ', ' + step.src_ids[1] + ', 0) ';
325                             set_str += '<<id:\'' + pid3 + '\', fillColor: \'' + step.args.fillColor + '\', ';
326                             set_str += 'name:\'\', priv:true, visible:false >>; \n';
327                             reset_str += 'delete ' + pid3 + '; ';
328                         } else {
329                             pid3 = step.src_ids[2];
330                         }
331                         set_str += assign + 'bisector(' + pid1 + ', ' + pid3 + ', ' + pid2 + ') ';
332                         set_str += '<<' + attrid + 'point: <<id: \'' + step.dest_sub_ids[0] + '\', priv: true, name: \'';
333                         set_str += step.dest_sub_ids[0] + '\'>> >>; ';
334                         reset_str += 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[0] + '; ';
335                     } else {
336                         set_str = assign + 'bisector(' + step.src_ids[1] + ', ' + step.src_ids[2] + ', ' + step.src_ids[0];
337                         set_str += ') <<' + attrid + 'point: <<id: \'' + step.dest_sub_ids[0] + '\', priv: true, name: \'';
338                         set_str += step.dest_sub_ids[0] + '\'>>>>; ';
339                         reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[0] + '; ';
340                     }
341                     break;
342 
343                 case JXG.GENTYPE_NORMAL:
344                     if (step.args.create_point === true) {
345                         sub_id = step.dest_sub_ids[1];
346                         set_str = 'point(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]);
347                         set_str += ') <<id: \'' + sub_id + '\', name: \'\', visible: false, priv: true>>; ';
348                         reset_str = 'delete ' + sub_id + '; ';
349                     } else
350                         sub_id = step.src_ids[1];
351 
352                     set_str += assign + 'normal(' + sub_id + ', ' + step.src_ids[0] + ') <<' + attrid;
353                     set_str += 'point: <<id: \'' + step.dest_sub_ids[0] + '\', name: \'' + step.dest_sub_ids[0];
354                     set_str += '\'>> >>; ';
355                     reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[0] + '; ' + reset_str;
356                     break;
357 
358                 case JXG.GENTYPE_POINT:
359                     set_str = assign + 'point(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]);
360                     set_str += ')' + ( options.useSymbols ? '' : ' <<id: \'' + step.dest_id + '\'>>') + ';';
361                     reset_str = 'delete ' + step.dest_id + '; ';
362                     break;
363 
364                 case JXG.GENTYPE_GLIDER:
365                     if (options.useGlider) {
366                         set_str = assign + 'glider(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]);
367                         set_str += ', ' + step.src_ids[0] + ')';
368                         set_str += ( options.useSymbols ? '' : '<<id: \'' + step.dest_id + '\'>>') + ';';
369                     } else {
370                         set_str = assign + 'point(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]);
371                         set_str += ') <<' + attrid + ' fillColor: \'' + step.args.fillColor + '\'>>; ' + step.dest_id;
372                         set_str += '.glide(' + step.src_ids[0] + '); ';
373                     }
374                     reset_str = 'delete ' + step.dest_id + '; ';
375                     break;
376 
377                 case JXG.GENTYPE_INTERSECTION:
378                     set_str = assign + 'intersection(' + step.src_ids[0] + ', ' + step.src_ids[1] + ', ' + step.args.choice;
379                     set_str += ') <<' + attrid + ' fillColor: \'' + step.args.fillColor + '\'>>; ';
380                     reset_str = 'delete ' + step.dest_id + '; ';
381                     break;
382 
383                 case JXG.GENTYPE_CIRCLE:
384                     reset_str = 'delete ' + step.dest_sub_ids[0] + '; ';
385 /*
386                     if (step.args.create_point === true || step.args.create_midpoint === true) {
387 
388                         if (step.args.create_point === true) {
389                             set_str = 'point(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]);
390                             set_str += ') <<id: \'' + step.dest_sub_ids[0] + '\', priv: false>>; ';
391                         } else {
392                             set_str = 'midpoint(' + step.src_ids[0] + ', ' + step.src_ids[1] + ') <<id: \'';
393                             set_str += step.dest_sub_ids[0] + '\', name: \'\', visible: true>>; ';
394                         }
395 
396                         set_str += assign + 'circle(' + step.dest_sub_ids[0] + ', ' + step.src_ids[0] + ') <<' + attrid;
397                         set_str += ' fillOpacity: ' + JXG.Options.opacityLevel + ' >>; ';
398                         reset_str = 'delete ' + step.dest_id + '; ' + reset_str;
399 */                        
400                     if (step.args.create_point === true) {
401                         set_str = 'point(' + pn(step.args.usrCoords[1]) + ', ' + pn(step.args.usrCoords[2]);
402                         set_str += ') <<id: \'' + step.dest_sub_ids[0] + '\', priv: false>>; ';
403 
404                         set_str += assign + 'circle(' + step.dest_sub_ids[0] + ', ' + step.src_ids[0] + ') <<' + attrid;
405                         set_str += ' fillOpacity: ' + JXG.Options.opacityLevel + ' >>; ';
406                         reset_str = 'delete ' + step.dest_id + '; ' + reset_str;
407                     } else if (step.args.create_by_radius === true) {
408                         set_str = 'point(' + pn(step.args.x) + ', ' + pn(step.args.y) + ') <<id: \'' + step.dest_sub_ids[0];
409                         set_str += '\', name: \'\', withLabel: true, visible: true, priv: false>>; ';
410                         set_str += step.dest_sub_ids[0] + '.visible = true; ';
411                         set_str += assign + 'circle(\'' + step.dest_sub_ids[0] + '\', ' + pn(step.args.r) + ') <<' + attrid;
412                         set_str += ' fillOpacity: ' + JXG.Options.opacityLevel + '>>; ';
413                         reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[0] + '; ';
414                     } else {
415                         set_str = assign + 'circle(' + step.src_ids[0] + ', ' + step.src_ids[1] + ', ' + step.src_ids[2];
416                         set_str += ') <<center: <<id: \'' + step.dest_sub_ids[0] + '\', name: \'' + step.dest_sub_ids[0];
417                         set_str += '\', visible: true>>, ' + attrid + ' fillOpacity: ' + JXG.Options.opacityLevel + '>>; ';
418                         reset_str = 'delete ' + step.dest_id + '; ' + reset_str;
419                     }
420 
421                     break;
422 
423                 case JXG.GENTYPE_CIRCLE2POINTS:
424                     if (step.args.create_two_points === true) {
425                         set_str = 'point(' + pn(step.args.x1) + ', ' + pn(step.args.y1) + ') <<id: \'' + step.dest_sub_ids[0];
426                         set_str += '\'>>; ';
427                         set_str += 'point(' + pn(step.args.x2) + ', ' + pn(step.args.y2) + ') <<id: \'';
428                         set_str += step.dest_sub_ids[1] + '\'>>; ';
429                         set_str += assign + 'circle(' + step.dest_sub_ids[0] + ', ' + step.dest_sub_ids[1] + ') <<' + attrid;
430                         set_str += ' fillOpacity: ' + JXG.Options.opacityLevel + '>>; ';
431                         reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[1] + '; delete ';
432                         reset_str += step.dest_sub_ids[0] + '; ';
433                     } else if (step.args.create_point === true) {
434                         set_str = 'point(' + pn(step.args.x) + ', ' + pn(step.args.y) + ') <<id: \'' + step.dest_sub_ids[0];
435                         set_str += '\'>>; ';
436                         set_str += assign + 'circle(' + step.dest_sub_ids[0] + ', ' + step.src_ids[0] + ') <<' + attrid;
437                         set_str += ' fillOpacity: ' + JXG.Options.opacityLevel + '>>; ';
438                         reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[0] + '; ';
439                     } else if (step.args.create_by_radius === true) {
440                         set_str = assign + 'circle(' + step.src_ids[0] + ', ' + step.args.r + ') <<' + attrid;
441                         set_str += ' fillOpacity: ' + JXG.Options.opacityLevel + '>>; ';
442                         reset_str = 'delete ' + step.dest_id + '; ';
443                     } else {
444                         set_str = assign + 'circle(' + step.src_ids[0] + ', ' + step.src_ids[1] + ') <<' + attrid;
445                         set_str += ' fillOpacity: ' + JXG.Options.opacityLevel + '>>; ';
446                         reset_str = 'delete ' + step.dest_id + '; ';
447                     }
448 
449                     break;
450 
451                 case JXG.GENTYPE_LINE:
452                     k = 0;
453                     j = 0;
454 
455                     if (step.args.create_point1) {
456                         pid1 = step.dest_sub_ids[k++];
457                         str1 = [];
458                         for (i = 0; i < step.args.p1.length; i++)
459                             str1[i] = pn(step.args.p1[i]);
460 
461                         set_str = 'point(' + str1.join(', ') + ') <<id: \'' + pid1 + '\', name: \'\', visible: false, ';
462                         set_str += 'priv: true>>; ';
463                         reset_str = 'delete ' + pid1 + '; ';
464                     } else
465                         pid1 = step.src_ids[j++];
466 
467                     if (step.args.create_point2) {
468                         pid2 = step.dest_sub_ids[k++];
469                         str1 = [];
470                         for (i = 0; i < step.args.p2.length; i++)
471                             str1[i] = pn(step.args.p2[i]);
472 
473                         set_str += 'point(' + str1.join(', ') + ') <<id: \'' + pid2 + '\', name: \'\', visible: false, ';
474                         set_str += 'priv: true>>; ';
475                         reset_str = 'delete ' + pid2 + '; ' + reset_str;
476                     } else
477                         pid2 = step.src_ids[j++];
478 
479                     str = 'line';
480                     str1 = '';
481 
482                     // the line's parents
483                     str2 = pid1 + ', ' + pid2;
484 
485                     // if we want a truly free line
486                     if (step.args.create_point1 && step.args.create_point2 && options.freeLine) {
487                         // forget the points
488                         set_str = '';
489                         reset_str = '';
490 
491                         // use the stdform instead
492                         if (step.args.p1.length === 2)
493                             step.args.p1.unshift(1);
494 
495                         if (step.args.p2.length === 2)
496                             step.args.p2.unshift(1);
497 
498                         str2 = JXG.Math.crossProduct(step.args.p1, step.args.p2);
499                         for (i = 0; i < str2.length; i++)
500                             str2[i] = pn(str2[i]);
501 
502                         str2 = str2.join(', ');
503                     }
504 
505                     if (!step.args.first && !step.args.last)
506                         str = 'segment';
507                     else {
508                         if (!step.args.first)
509                             str1 = 'straightFirst: ' + step.args.first;
510 
511                         if (!step.args.last)
512                             str1 = 'straightLast: ' + step.args.last;
513 
514                         if (str1.length > 0 && !options.useSymbols)
515                             str1 += ', ';
516                     }
517 
518                     // this is a corner case, we have to get rid of the ',' at the end
519                     // simple solution: rebuild attrid
520                     if (!options.useSymbols)
521                         attrid = ' id: \'' + step.dest_id + '\' ';
522 
523                     set_str += assign + str + '(' + str2 + ')';
524                     set_str += (str1.length + attrid.length > 0 ? ' <<' + str1 + attrid + '>>' : '') + ';';
525                     reset_str = 'delete ' + step.dest_id + '; ' + reset_str;
526 
527                     break;
528 
529                 case JXG.GENTYPE_TRIANGLE:
530                     for (i = 0; i < step.args.create_point.length; i++)
531                         if (step.args.create_point[i] === true) {
532                             set_str += 'point(' + pn(step.args.coords[i].usrCoords[1]) + ', ';
533                             set_str += pn(step.args.coords[i].usrCoords[2]) + ') <<id: \'' + step.dest_sub_ids[i];
534                             set_str += '\'>>; ';
535                         }
536 
537                     for (i = 0; i < step.dest_sub_ids.length; i++)
538                         if (step.dest_sub_ids[i] !== 0)
539                             reset_str = 'delete ' + step.dest_sub_ids[i] + '; ' + reset_str;
540 
541                     reset_str = 'delete ' + step.dest_id + '; ' + reset_str;
542 
543                     set_str += assign + 'polygon(';
544 
545                     for (i = 0; i < step.src_ids.length; i++) {
546                         set_str += step.src_ids[i];
547                         if (i < step.src_ids.length - 1)
548                             set_str += ', ';
549                     }
550 
551                     for (i = 0; i < 3; i++) {
552                         if (step.dest_sub_ids[i] !== 0) {
553                             if (step.src_ids.length > 0 || i > 0)
554                                 set_str += ', ';
555                             set_str += step.dest_sub_ids[i];
556                         }
557                     }
558 
559                     set_str += ') <<borders: <<ids: [ \'' + step.dest_sub_ids[3] + '\', \'' + step.dest_sub_ids[4];
560                     set_str += '\', \'' + step.dest_sub_ids[5] + '\' ]>>, ' + attrid + ' fillOpacity: ';
561                     set_str += JXG.Options.opacityLevel + ', hasInnerPoints:true, scalable:true>>; ';
562                     break;
563 
564                 case JXG.GENTYPE_QUADRILATERAL:
565                     for (i = 0; i < step.args.create_point.length; i++)
566                         if (step.args.create_point[i] === true) {
567                             set_str += 'point(' + pn(step.args.coords[i].usrCoords[1]) + ', ';
568                             set_str += pn(step.args.coords[i].usrCoords[2]) + ') <<id: \'' + step.dest_sub_ids[i];
569                             set_str += '\'>>; ';
570                         }
571 
572                     for (i = 0; i < step.dest_sub_ids.length; i++)
573                         if (step.dest_sub_ids[i] !== 0)
574                             reset_str = 'delete ' + step.dest_sub_ids[i] + '; ' + reset_str;
575 
576                     reset_str = 'delete ' + step.dest_id + '; ' + reset_str;
577 
578                     set_str += assign + 'polygon(';
579 
580                     for (i = 0; i < step.src_ids.length; i++) {
581                         set_str += step.src_ids[i];
582                         if (i < step.src_ids.length - 1)
583                             set_str += ', ';
584                     }
585 
586                     set_str += ') <<borders: <<ids: [ \'' + step.dest_sub_ids[4] + '\', \'' + step.dest_sub_ids[5];
587                     set_str += '\', \'';
588                     set_str += step.dest_sub_ids[6] + '\', \'' + step.dest_sub_ids[7] + '\' ]>>, ' + attrid;
589                     set_str += ' fillOpacity: ';
590                     set_str += JXG.Options.opacityLevel + ', hasInnerPoints:true, scalable:true>>; ';
591                     break;
592 
593                 case JXG.GENTYPE_TEXT:
594                     set_str = assign + 'text(' + pn(step.args.x) + ', ' + pn(step.args.y) + ', ' + step.args.str + ') <<';
595                     set_str += attrid + ' name: \'' + step.dest_id + '\'>>; ' + step.dest_id + '.setText(' + step.args.str;
596                     set_str += '); ';
597                     reset_str = 'delete ' + step.dest_id + '; ';
598                     break;
599 
600                 case JXG.GENTYPE_POLYGON:
601                     set_str = assign + 'polygon(';
602 
603                     for (i = 0; i < step.src_ids.length; i++) {
604                         set_str += step.src_ids[i];
605                         if (i != step.src_ids.length - 1)
606                             set_str += ', ';
607                     }
608 
609                     set_str += ') <<borders: <<ids: [ \'';
610 
611                     for (i = 0; i < step.dest_sub_ids.length; i++) {
612                         set_str += step.dest_sub_ids[i];
613                         if (i < step.dest_sub_ids.length - 1)
614                             set_str += '\', \'';
615                     }
616 
617                     set_str += '\' ]>>, ' + attrid + ' fillOpacity: ' + JXG.Options.opacityLevel + ' >>; ';
618                     reset_str = 'delete ' + step.dest_id + '; ';
619                     break;
620 
621                 case JXG.GENTYPE_REGULARPOLYGON:
622                     set_str = assign + 'regularpolygon(' + step.src_ids[0] + ', ' + step.src_ids[1] + ', ';
623                     set_str += step.args.corners + ') <<borders: <<ids: [ ';
624 
625                     for (i = 0; i < step.args.corners; i++) {
626                         set_str += '\'' + step.dest_sub_ids[i] + '\'';
627                         if (i != step.args.corners - 1)
628                             set_str += ', ';
629                         reset_str = 'delete ' + step.dest_sub_ids[i] + '; ' + reset_str;
630                     }
631 
632                     set_str += ' ]>>, vertices: <<ids: [ ';
633 
634                     for (i = 0; i < step.args.corners - 2; i++) {
635                         set_str += '\'' + step.dest_sub_ids[i + parseInt(step.args.corners)] + '\'';
636                         if (i != step.args.corners - 3)
637                             set_str += ', ';
638                         reset_str = 'delete ' + step.dest_sub_ids[i + parseInt(step.args.corners)] + '; ' + reset_str;
639                     }
640 
641                     set_str += ' ]>>, ' + attrid + ' fillOpacity: ' + JXG.Options.opacityLevel + ' >>; ';
642                     reset_str = 'delete ' + step.dest_id + '; ' + reset_str;
643                     break;
644 
645                 case JXG.GENTYPE_SECTOR:
646                     set_str = assign + 'sector(' + step.src_ids[0] + ', ' + step.src_ids[1] + ', ' + step.src_ids[2];
647                     set_str += ') <<';
648                     set_str += attrid + ' name: \'' + step.dest_id + '\', fillOpacity: ' + JXG.Options.opacityLevel;
649                     set_str += '>>; ';
650                     reset_str = 'delete ' + step.dest_id + '; ';
651                     break;
652 
653                 case JXG.GENTYPE_ANGLE:
654                     set_str = assign + 'angle(' + step.src_ids[0] + ', ' + step.src_ids[1] + ', ' + step.src_ids[2] + ') ';
655                     set_str += '<<radiuspoint: << priv:true, id: \'' + step.dest_sub_ids[0] + '\', name: \'' + step.dest_sub_ids[0];
656                     set_str += '\'>>, pointsquare: << priv:true, id: \'' + step.dest_sub_ids[1] + '\', name: \'' + step.dest_sub_ids[1];
657                     set_str += '\'>>, dot: << priv:true, id: \'' + step.dest_sub_ids[2] + '\', name: \'' + step.dest_sub_ids[2];
658                     set_str += '\'>>, ';
659                     set_str += attrid + ' fillOpacity: ' + JXG.Options.opacityLevel + '>>; ';
660                     reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[2] + '; delete ';
661                     reset_str += step.dest_sub_ids[1];
662                     reset_str += '; delete ' + step.dest_sub_ids[0] + '; ';
663                     break;
664 
665                 case JXG.GENTYPE_PLOT:
666                     set_str = assign + step.args.plot_type + '(' + step.args.func + ') <<';
667 
668                     if (step.args.isPolar)
669                         set_str += 'curveType: \'polar\', ';
670 
671                     set_str += attrid + ' name:\'' + step.dest_id + '\'>>; ';
672                     reset_str = 'delete ' + step.dest_id + '; ';
673                     break;
674 
675                 case JXG.GENTYPE_SLIDER:
676                     set_str = assign + 'slider([' + pn(step.args.x1) + ', ' + pn(step.args.y1) + '], [' + pn(step.args.x2);
677                     set_str += ', ' + pn(step.args.y2) + '], [' + pn(step.args.start) + ', ' + pn(step.args.ini) + ', ';
678                     set_str += pn(step.args.end) + ']) <<' + attrid + ' name: \'' + step.dest_id + '\', baseline: <<id: \'';
679                     set_str += step.dest_sub_ids[0] + '\', name: \'' + step.dest_sub_ids[0] + '\'>>, highline: <<id: \'';
680                     set_str += step.dest_sub_ids[1] + '\', name: \'' + step.dest_sub_ids[1] + '\'>>, point1: <<id: \'';
681                     set_str += step.dest_sub_ids[2] + '\', name: \'' + step.dest_sub_ids[2] + '\'>>, point2: <<id: \'';
682                     set_str += step.dest_sub_ids[3] + '\', name: \'' + step.dest_sub_ids[3] + '\'>>, label: <<id: \'';
683                     set_str += step.dest_sub_ids[4] + '\', name: \'' + step.dest_sub_ids[4] + '\'>>>>; ';
684 
685                     reset_str = 'delete ' + step.dest_id + '; delete ' + step.dest_sub_ids[4] + '; delete ';
686                     reset_str += step.dest_sub_ids[3] + '; delete ' + step.dest_sub_ids[2] + '; delete ';
687                     reset_str += step.dest_sub_ids[1] + '; delete ';
688                     reset_str += step.dest_sub_ids[0] + '; ';
689                     break;
690 
691                 case JXG.GENTYPE_DELETE:
692 
693                     arr = [];
694                     ctx_set_str = [];
695                     ctx_reset_str = [];
696 
697                     for (i = 0; i < step.args.steps.length; i++) {
698                         if (step_log[step.args.steps[i]].type > 50) {
699                             arr = this.generateJCodeMeta(step_log[step.args.steps[i]], board);
700                         } else {
701                             arr = this.generateJCode(step_log[step.args.steps[i]], board, step_log);
702                         }
703 
704                         if (arr[2].trim() !== '') {
705                             set_str = arr[2] + set_str;
706                         }
707                         if (JXG.isFunction(arr[3])) {
708                             ctx_set_str.unshift(arr[3]);
709                         }
710                         if (arr[0].trim() !== '') {
711                             reset_str += arr[0];
712                         }
713                         if (JXG.isFunction(arr[1])) {
714                             ctx_reset_str.push(arr[1]);
715                         }
716                     }
717 
718                     break;
719 
720                 case JXG.GENTYPE_COPY:
721                     copy_log = [];
722 
723                     // Adapt the steps to the new IDs
724 
725                     for (el in step.args.steps) {
726 
727                         if (step.args.steps.hasOwnProperty(el)) {
728                             step2 = JXG.deepCopy(step_log[step.args.steps[el]]);
729 
730                             if (step2.type == JXG.GENTYPE_COPY) {
731 
732                                 for (i = 0; i < step2.args.map.length; i++)
733                                     for (j = 0; j < step.args.map.length; j++)
734                                         if (step2.args.map[i].copy == step.args.map[j].orig)
735                                             step2.args.map[i].copy = step.args.map[j].copy;
736 
737                                 step2 = JXG.SketchReader.replaceStepDestIds(step2, step2.args.map);
738                             } else
739                                 step2 = JXG.SketchReader.replaceStepDestIds(step2, step.args.map);
740 
741                             copy_log.push(step2);
742                         }
743                     }
744 
745                     for (i = 0; i < copy_log.length; i++) {
746 
747                         if (copy_log[i].type > 50)
748                             arr = this.generateJCodeMeta(copy_log[i], board);
749                         else
750                             arr = this.generateJCode(copy_log[i], board, step_log);
751 
752                         if (arr[0].trim() !== '')
753                             set_str += arr[0];
754                         if (JXG.isFunction(arr[1]))
755                             ctx_set_str.push(arr[1]);
756                         if (arr[2].trim() !== '')
757                             reset_str = arr[2] + reset_str;
758                         if (JXG.isFunction(arr[3]))
759                             ctx_reset_str.unshift(arr[3]);
760                     }
761 
762                     // Apply the offset-translation to the free points of the copy
763 
764                     if (step.args.dep_copy) {
765 
766                         for (i = 0; i < step.args.map.length; i++) {
767                             if (getObject(step.args.map[i].orig).elementClass == JXG.OBJECT_CLASS_POINT) {
768                                 set_str += step.args.map[i].copy;
769                                 set_str += '.X = function() { return (' + step.args.map[i].orig + '.X() - ';
770                                 set_str += pn(step.args.x) + '); }; ';
771                                 set_str += step.args.map[i].copy;
772                                 set_str += '.Y = function() { return (' + step.args.map[i].orig + '.Y() - ';
773                                 set_str += pn(step.args.y) + '); }; ';
774                             }
775                         }
776 
777                     } else {
778 
779                         for (i = 0; i < step.args.free_points.length; i++) {
780                             xstart = getObject(step.args.free_points[i].orig).coords.usrCoords[1];
781                             ystart = getObject(step.args.free_points[i].orig).coords.usrCoords[2];
782 
783                             set_str += step.args.free_points[i].copy + '.X = function() { return ';
784                             set_str += pn(xstart - step.args.x) + '; }; ';
785                             set_str += step.args.free_points[i].copy + '.Y = function() { return ';
786                             set_str += pn(ystart - step.args.y) + '; }; ';
787                             set_str += step.args.free_points[i].copy + '.free(); ';
788                         }
789                     }
790 
791                     for (j = 0; j < step.args.map.length; j++) {
792                         el = getObject(step.args.map[j].orig);
793 
794                         // Check if a radius-defined circle should be copied
795                         if (el.type == JXG.OBJECT_TYPE_CIRCLE && el.point2 == null) {
796                             // Make the radius of the circle copy depend on the original circle's radius
797                             set_str += step.args.map[j].copy + '.setRadius(function () { return ';
798                             set_str += step.args.map[j].orig + '.radius(); }); ';
799                         }
800                     }
801 
802                     break;
803 
804                 case JXG.GENTYPE_ABLATION:
805 
806                     xstart = getObject(step.src_ids[0]).coords.usrCoords[1];
807                     ystart = getObject(step.src_ids[0]).coords.usrCoords[2];
808 
809                     set_str = 'point(' + pn(xstart - step.args.x) + ', ' + pn(ystart - step.args.y) + ') <<id: \'';
810                     set_str += step.dest_sub_ids[0] + '\'>>; ';
811                     set_str += 'circle(' + step.dest_sub_ids[0] + ', 1) <<id: \'' + step.dest_sub_ids[1];
812                     set_str += '\', fillOpacity: ' + JXG.Options.opacityLevel + ', strokeColor: \'#888888\', visible: true>>; ';
813 
814                     if (step.args.fids.length == 1)
815                         step.args.func = step.args.fids[0] + '.radius()';
816                     else
817                         step.args.func = 'dist(' + step.args.fids[0] + ', ' + step.args.fids[1] + ')';
818 
819                     set_str += step.dest_sub_ids[1] + '.setRadius(function() { return ' + step.args.func + '; }); ';
820 
821                     if (step.args.migrate != 0 && step.args.migrate != -1)
822                         set_str += '$board.migratePoint(' + step.dest_sub_ids[0] + ', ' + step.args.migrate + '); ';
823                     else
824                         reset_str += 'delete ' + step.dest_sub_ids[0] + '; '; // a de-migration function is missing ...
825 
826                     reset_str = 'delete ' + step.dest_sub_ids[1] + '; ' + reset_str;
827 
828                     break;
829 
830                 case JXG.GENTYPE_MIGRATE:
831 
832                     set_str += '$board.migratePoint(' + step.src_ids[0] + ', ' + step.dest_id + '); ';
833                     reset_str += 'delete ' + step.dest_id + '; '; // as above: missing de-migration ...
834 
835                     break;
836 
837                 case JXG.GENTYPE_TRANSFORM:
838 
839                     set_str = step.dest_sub_ids[0] + ' = transform(' + step.args.tmat + ') <<type: \'generic\'>>; ';
840                     set_str += 'point(' + step.src_ids[0] + ', ' + step.dest_sub_ids[0] + ') <<id: \'' + step.dest_id;
841                     set_str += '\', visible: true>>; ';
842 
843                     reset_str = 'delete ' + step.dest_id + '; ';
844                     reset_str += 'delete ' + step.dest_sub_ids[0] + '; ';
845 
846                     break;
847 
848                 case JXG.GENTYPE_MOVEMENT:
849                     if (step.args.obj_type == JXG.OBJECT_TYPE_LINE) {
850                         set_str = step.src_ids[0] + '.move([' + pn(step.args.coords[0].usrCoords[0]) + ', ';
851                         set_str += pn(step.args.coords[0].usrCoords[1]) + ', ' + pn(step.args.coords[0].usrCoords[2]) + ']); ';
852                         reset_str = step.src_ids[0] + '.move([' + step.args.zstart[0] + ', ' + step.args.xstart[0] + ', ';
853                         reset_str += step.args.ystart[0] + ']); ';
854 
855                         set_str += step.src_ids[1] + '.move([' + pn(step.args.coords[1].usrCoords[0]) + ', ';
856                         set_str += pn(step.args.coords[1].usrCoords[1]) + ', ' + pn(step.args.coords[1].usrCoords[2]) + ']); ';
857                         reset_str += step.src_ids[1] + '.move([' + step.args.zstart[1] + ', ' + step.args.xstart[1] + ', ';
858                         reset_str += step.args.ystart[1] + ']); ';
859 
860                     } else if (step.args.obj_type == JXG.OBJECT_TYPE_CIRCLE) {
861                         set_str = step.src_ids[0] + '.move([' + pn(step.args.coords[0].usrCoords[1]) + ', ';
862                         set_str += pn(step.args.coords[0].usrCoords[2]) + ']); ';
863                         reset_str = step.src_ids[0] + '.move([' + step.args.xstart + ', ' + step.args.ystart + ']); ';
864 
865                         if (step.args.has_point2) {
866                             set_str += step.src_ids[1] + '.move([' + pn(step.args.coords[1].usrCoords[1]) + ', ';
867                             set_str += pn(step.args.coords[1].usrCoords[2]) + ']); ';
868                             reset_str += step.src_ids[1] + '.move([' + step.args.old_p2x + ', ' + step.args.old_p2y;
869                             reset_str += ']); ';
870                         }
871 
872                     } else if (step.args.obj_type == JXG.OBJECT_TYPE_GLIDER) {
873                         set_str = step.src_ids[0] + '.setPosition(' + pn(step.args.position) + '); ';
874                         reset_str = step.src_ids[0] + '.setPosition(' + step.args.xstart + '); ';
875 
876                     } else if (step.args.obj_type == JXG.OBJECT_TYPE_POLYGON) {
877                         set_str = reset_str = "";
878 
879                         for (i = 0; i < step.src_ids.length; i++) {
880                             set_str += step.src_ids[i] + '.move([' + pn(step.args.coords[i].usrCoords[1]) + ', ';
881                             set_str += pn(step.args.coords[i].usrCoords[2]) + ']); ';
882                             reset_str += step.src_ids[i] + '.move([' + step.args.xstart[i] + ', ' + step.args.ystart[i];
883                             reset_str += ']); ';
884                         }
885                     } else {
886                         set_str = step.src_ids[0] + '.move([' + pn(step.args.coords[0].usrCoords[1]) + ', ';
887                         set_str += pn(step.args.coords[0].usrCoords[2]) + ']); ';
888                         reset_str = step.src_ids[0] + '.move([' + step.args.xstart + ', ' + step.args.ystart + ']); ';
889                     }
890 
891                     break;
892 
893                 default:
894                     return;
895             }
896 
897             return [ set_str, ctx_set_str, reset_str, ctx_reset_str ];
898         },
899 
900         replaceStepDestIds: function (step, id_map) {
901             var i, j, copy_ids = [];
902 
903             for (i = 0; i < id_map.length; i++) {
904                 copy_ids.push(id_map[i].copy);
905 
906                 if (step.dest_id == id_map[i].orig)
907                     step.dest_id = id_map[i].copy;
908 
909                 for (j = 0; j < step.dest_sub_ids.length; j++) {
910                     if (step.dest_sub_ids[j] == id_map[i].orig) {
911                         step.dest_sub_ids[j] = id_map[i].copy;
912                     }
913                 }
914 
915                 for (j = 0; j < step.src_ids.length; j++) {
916                     if (step.src_ids[j] == id_map[i].orig) {
917                         step.src_ids[j] = id_map[i].copy;
918                     }
919                 }
920             }
921 
922             for (j = 0; j < step.dest_sub_ids.length; j++) {
923                 if (!JXG.isInArray(copy_ids, step.dest_sub_ids[j])) {
924                     step.dest_sub_ids[j] = this.id();
925                 }
926             }
927 
928             step.src_ids = JXG.uniqueArray(step.src_ids);
929             step.dest_sub_ids = JXG.uniqueArray(step.dest_sub_ids);
930 
931             return step;
932         },
933 
934         readSketch: function (str, board) {
935             var i, j, arr, json_obj, unzipped, meta, constr;
936 
937             unzipped = new JXG.Util.Unzip(JXG.Util.Base64.decodeAsArray(str)).unzip();
938 
939             if (!JXG.exists(unzipped[0])) {
940                 return '';
941             }
942 
943             unzipped = JXG.Util.utf8Decode(unzipped[0][0]);
944             constr = JSON.parse(unzipped);
945 
946             for (i = 0; i < constr.length - 1; i++) {
947 
948                 if (constr[i].type == 0)
949                     continue;
950 
951                 // fix for files created with the beta version
952                 if (constr[i].type == JXG.GENTYPE_CTX_VISIBILITY && constr[i].args.isGrid) {
953                     //constr[i] = { type: 0, src_ids: [], dest_sub_ids: [], dest_id: 0 };
954                     continue;
955                 }
956 
957                 if (constr[i].type == JXG.GENTYPE_GRID) {
958                     //constr[i] = { type: 0, src_ids: [], dest_sub_ids: [], dest_id: 0 };
959                     continue;
960                 }
961                 // end of fix
962 /*
963                 if (constr[i].type == 100) // Obsolete fix
964                     constr[i].type = JXG.GENTYPE_MOVEMENT;
965 
966                 if (constr[i].type == JXG.GENTYPE_MOVEMENT) {
967 
968                     for (j=i+1; j<constr.length-1; j++) {
969                         if (constr[j].type == JXG.GENTYPE_MOVEMENT && JXG.Draw.areEqual(constr[i].src_ids,
970                             constr[j].src_ids)) {
971                             constr[i] = { type: 0, src_ids: [], dest_sub_ids: [], dest_id: 0 };
972                             break;
973                         }
974                     }
975 
976                     if (j < constr.length-1)
977                         continue;
978                 }
979 */
980                 if (constr[i].type == 27) // Obsolete fix
981                     constr[i].type = JXG.GENTYPE_DELETE;
982 
983                 if (constr[i].type == 31) // Obsolete fix
984                     constr[i].type = JXG.GENTYPE_ABLATION;
985 
986                 if (constr[i] > 50)
987                     arr = this.generateJCodeMeta(constr[i], board);
988                 else
989                     arr = this.generateJCode(constr[i], board, constr);
990 
991                 board.jc.parse(arr[0], true);
992             }
993 
994             meta = constr.pop();
995 
996             // not yet :(
997             //if (meta.axisVisible)
998             //if (meta.gridVisible)
999 
1000             arr = meta.boundingBox; // bounding box
1001             board.setBoundingBox(arr);
1002 
1003             // these might be important in the future
1004             //GUI.transformation = meta.transformation; // transformation matrices (rotations, ...)
1005             //GUI.restore_state = meta.restoreState; // restore states
1006 
1007             board.options.grid.snapToGrid = !meta.snapToGrid;
1008             board.options.point.snapToGrid = !meta.snapToGrid;
1009             board.options.point.snapToPoints = !meta.snapToPoints;
1010 
1011             return '';
1012         }
1013     }
1014 });
1015