/*
* MathGraph32 Javascript : Software for animating online dynamic mathematics figures
* https://www.mathgraph32.org/
* @Author Yves Biton (yves.biton@sesamath.net)
* @License: GNU AGPLv3 https://www.gnu.org/licenses/agpl-3.0.html
*/
import NatObj from '../types/NatObj'
import NatCal from '../types/NatCal'
import Fonte from '../types/Fonte'
import CElementBase from './CElementBase'
import CListeObjets from './CListeObjets'
import CNoeudPointeurSurPoint from './CNoeudPointeurSurPoint'
import CSousListeObjets from './CSousListeObjets'
export default CImplementationProto
/**
* Classe reprétentant une implémentation de construction.
* this.listeSources contient des pointeurs sur les objets surces utilisés.
* @constructor
* @extends CElementBase
* @param {CListeObjets} listeProprietaire La liste propriétaire.
* @param {string} nomProto Le nom de la construction.
* @param {number} nbIntermediaires Le nombre d'objets intermédiares servant à construire
* les objets finaux.
* @param {number} nbFinaux Le nombre d'objets finaux de la construction.
* @param {CListeObjets} listeSourcesInit Liste contenant des pointeurs sur tous les objets sources.
* @returns {CImplementationProto}
*/
function CImplementationProto (listeProprietaire, nomProto, nbIntermediaires, nbFinaux, listeSourcesInit) {
if (arguments.length === 1) CElementBase.call(this, listeProprietaire)
else {
if (arguments.length === 2) { // Constructeur utilisé lors de l'implémentation
CElementBase.call(this, listeProprietaire, null, false)
this.listeSources = new CSousListeObjets()
const proto = arguments[1] // Le prototype associé
const nbSources = proto.nbObjSources
for (let i = 0; i < nbSources; i++) this.listeSources.add(proto.get(i).elementAssocie)
this.nomProto = proto.nom
this.nbFinaux = proto.nbObjFinaux
this.nbIntermediaires = proto.longueur() - nbSources - this.nbFinaux
} else {
CElementBase.call(this, listeProprietaire, null, false)
this.nomProto = nomProto
this.nbIntermediaires = nbIntermediaires
this.nbFinaux = nbFinaux
this.listeSources = listeSourcesInit
}
}
}
CImplementationProto.prototype = new CElementBase()
CImplementationProto.prototype.constructor = CImplementationProto
CImplementationProto.prototype.superClass = 'CElementBase'
CImplementationProto.prototype.className = 'CImplementationProto'
CImplementationProto.prototype.getClone = function (listeSource, listeCible) {
const listeSourcesClone = new CSousListeObjets()
listeSourcesClone.setImage(this.listeSources, listeSource, listeCible)
return new CImplementationProto(listeCible, this.nomProto, this.nbIntermediaires, this.nbFinaux, listeSourcesClone)
}
CImplementationProto.prototype.depDe = function (p) {
if (this.elementTestePourDependDe === p) return this.dependDeElementTeste
return this.memDep(CElementBase.prototype.depDe.call(this, p) || this.listeSources.depDe(p))
}
CImplementationProto.prototype.getNature = function () {
return NatObj.NImpProto
}
CImplementationProto.prototype.remplacePoint = function (ancienPoint, nouveauPoint) {
this.listeSources.remplacePoint(ancienPoint, nouveauPoint)
}
CImplementationProto.prototype.read = function (inps, list) {
CElementBase.prototype.read.call(this, inps, list)
this.nomProto = inps.readUTF()
this.nbIntermediaires = inps.readInt()
this.nbFinaux = inps.readInt()
this.listeSources = new CSousListeObjets()
this.listeSources.read(inps, list)
}
CImplementationProto.prototype.write = function (oups, list) {
CElementBase.prototype.write.call(this, oups, list)
oups.writeUTF(this.nomProto)
oups.writeInt(this.nbIntermediaires)
oups.writeInt(this.nbFinaux)
this.listeSources.write(oups, this.listeProprietaire)
}
/**
* Fonction implémentant le prototype proto dans la la liste propriétaire de this
* @param dimf Dimf : Contient les dimensions de la figure
* @param proto CPrototype
* un nouveau nom
*/
CImplementationProto.prototype.implemente = function (dimf, proto) {
let i; let ptgen; let elb; let ptp; let val; let gen; let ancienNom; let nouveauNom; let clone; let nomClone; let res
const listeProprietaire = this.listeProprietaire
const app = listeProprietaire.documentProprietaire.app
const listeEltsAjoutes = new CListeObjets()
const listePointsOuDroitesARenommer = new CListeObjets()
const listeCalculsARenommer = new CListeObjets()
const tabNewNames = [] // Contiendra les nouveaux noms à attribuer aux points et droites nommés
const tabNewNamesCal = [] // Contiendra les nouveaux noms à attribuer aux calculs ommés
proto.setListePourImplementation(listeProprietaire)
listeProprietaire.add(this)
proto.indicePremierObjetPourImplementation = listeProprietaire.longueur()
// en recherchant dans la liste des éléments sources les points et droites qui ont un nom
// dans le prototype et on leur génère un nom pour que les éventuelles mesures
// et calculs l'utilisant aient leur formules réactualisées
for (i = 0; i < proto.nbObjSources; i++) {
ptgen = proto.get(i) // Renvoie un CElementGenerique
elb = ptgen.elementAssocie
if (elb.estDeNature(NatObj.NObjNommable)) {
// CElementGraphique ptp = (CElementGraphique) elb;
const nomGen = ptgen.nom
// On regarde si l'élément correspondant était nommé.
// Si oui on rajoute cet élément à la liste des points devant être
// nommés
if ((nomGen !== '') && (elb.nom === '')) {
nouveauNom = listeProprietaire.genereNomPourPointOuDroite(nomGen.charAt(0), false)
elb.donneNom(nouveauNom)
if (!elb.nomMasque) elb.updateName(app.svgFigure, true) // Modifié version 6.2.0
}
}
}
// On implémente la liste d'objets sources gérée par l'implémentation de
// prototype
// Puis on ajoute des clones des objets intermédiaires et finaux du
// prototype
for (i = proto.nbObjSources; i < proto.longueur(); i++) {
elb = proto.get(i)
// Version 8.7.3 : On passe un troisième paramètre supplémentaire qui sera la nouvelle implémentation
// de prototype de façon à pouvoir optimiser CCommmentaire.prototype.determineDependances qui pourra
// utiliser cette implémentation de prototype dans la fonctionCCommentaire.prototype.pointeur
clone = elb.getClone(proto, this.listeProprietaire, this)
clone.estElementFinal = elb.estElementFinal
clone.impProto = this // Important
// Version 7.5.0 : On affecte au clone le tag de l'élément clone si celui-ci en a un
// et si ce tag existe déjà on lui rajoute l'id de l'élément
if (elb.tag && elb.tag !== '') {
if (listeProprietaire.hasElementOfTag(elb.tag, null)) {
clone.tag = elb.tag + '#' + elb.index
} else {
clone.tag = elb.tag
}
}
listeProprietaire.add(clone)
listeEltsAjoutes.add(clone)
if (clone.estDeNature(NatObj.NObjNommable)) {
// CPointAncetre ptp = (CPointAncetre) clone;
nomClone = clone.nom
if (nomClone !== '') {
if (listeProprietaire.existeMemeNomNonIntermediaire(clone, nomClone)) {
tabNewNames.push(nomClone)
clone.nom = ''
listePointsOuDroitesARenommer.add(clone)
}
}
// On donne la taille de nom par défaut aux points finaux
if (clone.estElementFinal) clone.tailleNom = app.getTaillePoliceNom()
} else {
if (clone.estDeNatureCalcul(NatCal.NTtCalcNomme)) {
// CValeurDynamique vald = (CValeurDynamique) clone;
nomClone = clone.getNom()
if (listeProprietaire.existeCalculOuVariableOuFonctionOuParametreMemeNom(clone, nomClone, false)) {
tabNewNamesCal.push(nomClone)
clone.nomCalcul = ''
listeCalculsARenommer.add(clone)
}
} else {
// Si la liste n'a pas de longueur unité et qu'on implémente une longueur ce sra cette longueur
// qui sera la longueur unité de la figure
if ((listeProprietaire.pointeurLongueurUnite === null) && clone.estDeNatureCalcul(NatCal.NMesureLongueur)) {
listeProprietaire.pointeurLongueurUnite = clone
}
}
}
}
listeEltsAjoutes.positionne(true, dimf)
// On renomme maintenant les points ou droites qui ont besoin d'être renommés
for (i = 0; i < listePointsOuDroitesARenommer.longueur(); i++) {
ptp = listePointsOuDroitesARenommer.get(i)
ancienNom = tabNewNames[i]
nouveauNom = listeProprietaire.genereNomPourPointOuDroite(ancienNom.charAt(0), false)
ptp.donneNom(nouveauNom)
// if (!ptp.estElementIntermediaire()) ptp.updateName(app.svgFigure, true);
}
// On renomme les calculs intermédiaires qui ont besoin d'être renommés
// en mettant à jour les affichages de commentaires dynamiques qui en
// dépendent
// Correction version 7.4.4 : Il ne faut pas choisir un nom qui a déjà été choisi pour un calcul implémenté
// On crée un tableau de nouveaux noms utilisés pour ne pas les utiliser à nouveau et la fonction
// genereNomPourCalcul prend un dernier paramètre qui est une liste de noms interdits
const nouveauxNoms = []
for (i = 0; i < listeCalculsARenommer.longueur(); i++) {
val = listeCalculsARenommer.get(i)
ancienNom = tabNewNamesCal[i]
// On génére un nouveau nom avec
// seulement les premiers caractères qui ne sont pas des chiffres
nouveauNom = listeProprietaire.genereNomPourCalcul(Fonte.premiersCaracteresNonChiffres(ancienNom), false, nouveauxNoms)
nouveauxNoms.push(nouveauNom)
listeEltsAjoutes.remplaceNomValeurDynamiqueDansCommentairesOuLatex(ancienNom, nouveauNom)
val.donneNom(nouveauNom)
}
// On reconstitue les formules des calculs ou fonctions intermédiaires et
// finaux dépendant des objets sources
for (i = 0; i < listeEltsAjoutes.longueur(); i++) {
elb = listeEltsAjoutes.get(i)
if (elb.estDeNatureCalcul(NatCal.NCalcOuFoncNonConstante)) {
// CCalcul calc = (CCalcul) elb;
elb.reconstruitChaineCalcul()
}
}
for (i = 0; i < proto.nbObjSources; i++) {
elb = proto.get(i).elementAssocie
if (elb.estDeNatureCalcul(NatCal.NTteValPourComDyn)) {
gen = proto.get(i)
// CValeurDynamique val = (CValeurDynamique) elb;
listeEltsAjoutes.remplaceNomValeurDynamiqueDansCommentairesOuLatex(gen.nom, elb.getNom())
}
}
// On met à jour les commentaires des éléments rajoutés au cas où ils
// contiendraient
// des affichages de valeurs dynamiques
listeEltsAjoutes.determineDependancesCommentaires()
listeEltsAjoutes.metAJourEtPositionne(true, dimf) // Modifié version 4.8. True car on permet les calculs aléatoires dans les objets
// On regarde si un des éléments finaux ajoutés au moins est une variable avec apnneau d'affichage avec boutons +, -, =
res = false
for (i = 0; i < listeEltsAjoutes.longueur(); i++) {
const el = listeEltsAjoutes.get(i)
if (el.estElementFinal && el.estDeNatureCalcul(NatCal.NVariable) && (el.dialogueAssocie)) {
res = true
break
}
}
if (res) {
listeProprietaire.removePaneVariables(app.svg.parentNode)
listeProprietaire.creePaneVariables()
}
}
/**
* Fonction implémentant la construction dans listePourImp pour une implémentation
* itérative. Appelé depuis CMacroConstructionIterative.
* @param {Dimf} dimf Dimensions du svg contenant la figure.
* @param {CListeObjets} listePourImp La liste dans laquelle se fait l'implémentation.
* @param {CPrototype} proto La construction à implémenter.
* @returns {void}
*/
CImplementationProto.prototype.implementePourIter = function (dimf, listePourImp, proto) {
let i, elb, clone
const listeEltsAjoutes = new CListeObjets()
// nomProto = proto.nom; Supprimé version 4.8. Inutile
proto.setListePourImplementation(this.listeProprietaire)
// On ajoute l'objet implementationPrototype à la liste
this.listeProprietaire.add(this)
// Important que que les indices soient corrects
proto.indicePremierObjetPourImplementation = this.listeProprietaire.longueur()
// On implémente la liste d'objets sources gérée par l'implémentation de
// prototype
// Puis on ajoute des clones des objets intermédiaires et finaux du
// prototype
for (i = proto.nbObjSources; i < proto.longueur(); i++) {
elb = proto.get(i)
clone = elb.getClone(proto, this.listeProprietaire)
clone.estElementFinal = elb.estElementFinal
clone.impProto = this // Important
listePourImp.add(clone)
listeEltsAjoutes.add(clone)
}
listeEltsAjoutes.positionne(true, dimf)
}
/**
* Implémente la construction proto de façon récursive dans listePourImp.
* Utilisé pour les implémentations récursives
* @param {Dimf} dimf Les dimensiosn du svg contenant la figure.
* @param {CListeObjets} listePourImp La lsite dans laquelle se fait l'implémentation.
* @param {CPrototype} proto La construction à implémenter.
* @param {number} nbFinaux Le nombre d'objets finaux de la construction (entier).
* @returns {void}
*/
CImplementationProto.prototype.implementePourRecur = function (dimf, listePourImp, proto, nbFinaux) {
let i, elb, ptgen, nomGen, clone
const listeEltsAjoutes = new CListeObjets()
// nomProto = proto.nom; Supprimé version 4.8. Inutile
proto.setListePourImplementation(this.listeProprietaire)
// On ajoute l'objet implementationPrototype à la liste
const imp = this.getClone(this.listeProprietaire, this.listeProprietaire)
imp.nomProto = proto.nom
this.listeProprietaire.add(imp) // A revoir version logiciel : utiliser un clone
// Important que que les indices soient corrects
proto.indicePremierObjetPourImplementation = this.listeProprietaire.longueur()
// On implémente la liste d'objets sources gérée par l'implémentation de
// prototype
// en recherchant dans la liste des éléments sources les points et droites qui ont un nom
// dans le prototype et on leur génère un nom pour que les éventuelles mesures
// et calculs l'utilisant aient leur formules réactualisées
for (i = 0; i < proto.nbObjSources; i++) {
elb = proto.get(i).elementAssocie
// listeSources.add(elb);
if (elb.estDeNature(NatObj.NObjNommable)) {
ptgen = proto.get(i)
nomGen = ptgen.nom
// On regarde si l'élément correspondant était nommé.
if ((nomGen !== '') && (elb.nom === '')) {
elb.donneNom(nomGen)
}
}
}
// Puis on ajoute des clones des objets intermédiaires et finaux du
// prototype à concurrence de nbFinaux
let nbFinauxTotal = 0
let nbIntermTotal = 0
for (i = proto.nbObjSources; (i < proto.longueur()) && (nbFinauxTotal < nbFinaux); i++) {
elb = proto.get(i)
clone = elb.getClone(proto, this.listeProprietaire)
const b = elb.estElementFinal
clone.estElementFinal = b
if (b) nbFinauxTotal++
else nbIntermTotal++
clone.impProto = imp // Important
listePourImp.add(clone)
listeEltsAjoutes.add(clone)
}
// Si le nombre d'objets finaux n'est pas le même que celui par défaut pour le prototype
// il faut le modifier dans l'implémentation de prototype et modifier aussi le nombre d'objets intermédiaires
// Cela n'arrive que pour les implémentations récursives avec création seulement des objets finaux
// de dernière génération (autres que ceux servant pour les implémentations suivantes)
imp.nbFinaux = nbFinaux
imp.nbIntermediaires = nbIntermTotal
listeEltsAjoutes.determineDependancesCommentaires() // Spécial version mtgApp
listeEltsAjoutes.positionne(true, dimf)
}
/**
* Fonction impémentant la construction de façon récursive dans listePourImp
* @param {Dimf} dimf
* @param {CListeObjets} listePourImp La liste dans laquelle se fait limplémentation.
* @param {CPrototype[]} tableProto Un tableau contenant la construction à utiliser suivant
* la profondeur de récursion.
* @param {CListeObjets} listeSourcesConst La liste des objets sources communs à toutes
* les implémentations.
* @param {CListeObjets} listeSourcesVar ; liste des objets sources qui seront reconstruits à chaque
* implémentation.
* @param {number} pasRec le pas de récursion qui est le nombre d'objets à sauter
* avant de réimplémenter la construction.
* @param {number} nbImp Le nombre de fois que la construction doit être itérée pour un même niveau.
* @param {boolean} creationNiveauMax Si true suels les objets finaux de dernière génération sont créés en dehors
* de ceux servant pour la genération suivante.
* @param {number} profond Le niveau de récursion (entier, 1 pour le niveau initial)
* @param {number} profondMax Le niveau maximum de récursion autorisé (entier)
* @param {CListeObjets} listePoints Lz liste des poiints finaux créés (our qu'on puisse si demandé les joindre)<.
* @returns {void}
*/
CImplementationProto.prototype.implementeRecursif = function (dimf, listePourImp, tableProto, listeSourcesConst,
listeSourcesVar, pasRec, nbImp, creationNiveauMax, profond, profondMax, listePoints) {
let i, j, k, elb, newlisteSourcesVar
// On commence par implémenter une première fois la construction
const proto = tableProto[profond]
const ls = listeSourcesConst.longueur()
const sizeInit = listePourImp.longueur()
for (i = 0; i < ls; i++) proto.get(i).elementAssocie = listeSourcesConst.get(i)
j = 0
for (i = ls; i < proto.nbObjSources; i++) {
proto.get(i).elementAssocie = listeSourcesVar.get(j)
j++
}
const nbObjFinaux = creationNiveauMax ? ((profond === profondMax) ? proto.nbObjFinaux : listeSourcesVar.longueur() + pasRec * (nbImp - 1)) : proto.nbObjFinaux
this.implementePourRecur(dimf, listePourImp, proto, nbObjFinaux)
if (profond === profondMax) {
if (listePoints !== null) {
// On rajoute à listePoints les points de génération ultime
for (i = sizeInit; i < listePourImp.longueur(); i++) {
elb = listePourImp.get(i)
if (elb.estElementFinal && elb.estDeNature(NatObj.NTtPoint)) {
listePoints.push(new CNoeudPointeurSurPoint(listePourImp, elb))
}
}
}
return
}
// On crée une liste formée de tous les éléments finaux créés par la première implémentation
const listef = new CListeObjets()
for (i = sizeInit; i < listePourImp.longueur(); i++) {
elb = listePourImp.get(i)
if (elb.estElementFinal && (elb.estDeNature(NatObj.NTtObj) ||
!elb.estDeNatureCalcul(NatCal.NAucunCalcul))) listef.add(elb)
}
// On applique la récursion
for (k = 0; k < nbImp; k++) {
j = 0
newlisteSourcesVar = new CListeObjets()
for (i = ls; i < proto.nbObjSources; i++) {
newlisteSourcesVar.add(listef.get(j + pasRec * k))
j++
}
this.implementeRecursif(dimf, listePourImp, tableProto, listeSourcesConst, newlisteSourcesVar,
pasRec, nbImp, creationNiveauMax, profond + 1, profondMax, listePoints)
}
}