import { j3pAddElt, j3pArrondi, j3pC, j3pDistance, j3pElement, j3pEmpty, j3pGetNewId, j3pImageRotation, j3pIntersection, j3pIsHtmlElement, j3pNotify, j3pPointDansSegment } from 'src/legacy/core/functions'
import { j3pEquation } from 'src/legacy/outils/geometrie/functions'
import { j3pCreeAngle, j3pCreeArc, j3pCreeCercle, j3pCreeRectangle, j3pCreeSecteur, j3pCreeSegment, j3pCreeSVG, j3pCreeTexte, j3pGradueSegment, j3pPolygone } from 'src/legacy/core/functionsSvg'
import { j3pTableauValeurs } from 'src/legacy/core/functionsTarbre'
/**
* Petit moteur de géométrie dynamique
* @fileOverview
* @author JP Vanroyen
* @since Novembre 2012
*
* Exemple de syntaxe d’appel
```
var repere = new Repere({
idConteneur: this.zones.MG,
idDivRepere: 'unrepere',
aimantage: false,
visible: true,
larg: 500,
haut: 500,
pasdunegraduationX: 1,
pixelspargraduationX: 30,
pasdunegraduationY: 1,
pixelspargraduationY: 30,
xO: 250,
yO: 250,
debuty: 0,
negatifs: true,
objets: [{
type: 'point',
nom: 'A',
par1: 6,
par2: 4,
fixe: false,
visible: true,
etiquette: true,
style: {couleur: '#00F', epaisseur: 2, taille: 18}
}, {
type: 'point',
nom: 'B',
par1: 4,
par2: -3,
fixe: false,
visible: true,
etiquette: true,
style: {couleur: '#00F', epaisseur: 2, taille: 18}
}, {
type: 'point',
nom: 'C',
par1: -3,
par2: -6,
fixe: false,
visible: true,
etiquette: true,
style: {couleur: '#0F0', epaisseur: 2, taille: 18}
},
{type: 'segment', nom: 's', par1: 'A', par2: 'B', style: {couleur: '#456', epaisseur: 2}},
{type: 'cercle', nom: 'ce', par1: 'A', par2: 'B', style: {couleur: '#456', epaisseur: 2}},
{type: 'droite', nom: 'd1', par1: 'A', par2: 'C', style: {couleur: '#382', epaisseur: 2}},
{type: 'droiteparallele', nom: 'd2', par1: 'B', par2: 'd1', style: {couleur: '#538', epaisseur: 2}},
{type: 'droiteperpendiculaire', nom: 'd3', par1: 'B', par2: 'd1', style: {couleur: '#F00', epaisseur: 2}},
{
type: 'point',
nom: 'D',
par1: -1,
par2: 5,
fixe: false,
visible: true,
etiquette: true,
style: {couleur: '#000', epaisseur: 2, taille: 18}
}, {
type: 'point',
nom: 'E',
par1: -2,
par2: 3,
fixe: false,
visible: true,
etiquette: true,
style: {couleur: '#000', epaisseur: 2, taille: 18}
},
{type: 'droite', nom: 'd4', par1: 'D', par2: 'E', style: {couleur: '#FA5', epaisseur: 2}},
{
type: 'pointintersection',
nom: 'T',
par1: 'd4',
par2: 'd3',
visible: true,
etiquette: true,
style: {couleur: '#0F0', epaisseur: 2, taille: 18}
}, {
type: 'pointintersection',
nom: 'S',
par1: 'd4',
par2: 'd2',
visible: true,
etiquette: true,
style: {couleur: '#0F0', epaisseur: 2, taille: 18}
},
{type: 'droite', nom: 'd5', par1: 'T', par2: 'A', style: {couleur: '#FA5', epaisseur: 2}},
{type: 'courbe', nom: 'c1', par1: '0.3*x^2-2', par2: -5, par3: 5, par4: 30, style: {couleur: '#FA5', epaisseur: 2}},
{
type: 'courbe_tabvaleur',
nom: 'c1',
tab_val: [[0, 1], [0.1, 1.2], [0.2, 1.5]],
style: {couleur: '#FA5', epaisseur: 2}
}]
})
repere.construit()
```
*/
/**
* Position ou coordonnées (l’unité peut être le pixel ou l’unité du repère suivant le contexte)
* @typedef XY
* @type Object
* @property {number} x
* @property {number} y
*/
/**
* Retourne la chaine nettoyée de ses caractères non alphanumériques (pour pouvoir être utilisé comme id)
* @param {string} chaine
* @return {string}
*/
export function cleanForId (chaine) {
// \W revient au même que [^\w] soit [^A-Za-z0-9_]
if (typeof chaine === 'string') return chaine.replace(/\W/g, '')
console.error(Error('Il faut passer une string'), typeof chaine)
return ''
}
const identity = arg => arg
/**
* Retourne le coef directeur de la droite
* @private
* @param {Repere} repere
* @param {string} par1 Nom du point 1 dans repere.objets
* @param {string} par2 Nom du point 2 dans repere.objets
* @return {number} Coef directeur (Number.POSITIVE_INFINITY si verticale)
*/
function coefdir (repere, par1, par2) {
const o1 = repere.getObjet(par1)
const o2 = repere.getObjet(par2)
const px1 = o1.par1
const py1 = o1.par2
const px2 = o2.par1
const py2 = o2.par2
if (px1 === px2) return Number.POSITIVE_INFINITY
return (py2 - py1) / (px2 - px1)
}
/**
* Helper de construit pour ajouter les objets dans le repère
* @private
* @param {Repere} repere
*/
function construitObjets (repere) {
// au cas ou des points seraient déclarés après des objets basés dessus, on leur affecte leurs props x & y d’abord
for (const objet of repere.objets) {
if (objet.type === 'point') {
objet.x = objet.par1
objet.y = objet.par2
}
}
for (const [k, objet] of repere.objets.entries()) {
if (!repere.objetsByNom.has(objet.nom)) {
console.warn(`À la reconstruction on a un objet ${objet.nom} dans objets qui n’est pas dans objetsByNom`)
repere.add(objet)
}
switch (objet.type) {
case 'point':
// FIXME pourquoi tester visible ? la méthode point le gère…
if (objet.visible) repere.point(objet)
break
case 'segment' :
repere.segment(objet)
break
case 'droite' :
repere.droite(objet)
break
case 'demidroite' :
repere.demidroite(objet)
break
case 'droiteparallele' :
repere.droiteparallele(objet)
break
case 'droiteperpendiculaire' :
repere.droiteperpendiculaire(k, objet.nom, objet.par1, objet.par2, objet.style)
break
case 'pointintersection' :
repere.pointintersection(objet.nom, objet.par1, objet.par2, true, objet.etiquette, objet.style)
break
// {type:'cercle',nom:'ce',par1:'A',par2:'B',style:{couleur:'#FA5',couleurRemplissage:'#FA5',epaisseur:2,opaciteRemplissage:0.3}},
case 'cercle' :
repere.cercle(objet.nom, objet.par1, objet.par2, objet.style)
break
case 'angle' :
repere.angle(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.style)
break
case 'cercledia' :
repere.cercledia(objet.nom, objet.par1, objet.par2, objet.style)
break
// {type:'courbe',nom:'c1',par1:'0.3*x^2-2',par2:-5,par3:5,par4:30,style:{couleur:'#FA5',epaisseur:2}}
case 'courbe':
repere.courbe(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.style)
break
// {type:'courbe_tabvaleur',nom:'c1',tab_val:[[0,1], [0.1,1.2], [0.2,1.5], ...] ,style:{couleur:'#FA5',epaisseur:2}}
case 'courbe_tabvaleur':
repere.courbe_tabvaleur(objet.nom, objet.tab_val, objet.style)
break
// {type:'prisme',nom:'pr1',par1:'A',par2:2,par3:4,par4:45,style:{couleur:'#FA5',couleurRemplissage:'#FA5',epaisseur:2,opaciteRemplissage:0.3}}
case 'prisme':
repere.prisme(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.par5, objet.style)
break
case 'polygone':
repere.polygone(objet.nom, objet.par1, objet.style)
break
case 'pointsur':
repere.pointsur(objet)
break
case 'secteur':
repere.secteur(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.style)
break
case 'ellipse':
repere.ellipse(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.style)
break
case 'ellipse2':
repere.ellipse2(objet.nom, objet.par1, objet.par2, objet.par3, objet.par4, objet.par5, objet.par6, objet.par7, objet.par8, objet.par9, objet.style)
break
case 'ellipse3' :
repere.ellipse3(objet.nom, objet.cx, objet.cy, objet.rx, objet.ry, objet.angledeb, objet.anglefin, objet.rotation, objet.style)
break
case 'face':
repere.face(objet.nom, objet.par1, objet.style)
break
case 'arc': {
const centre = repere.getObjet(objet.centre || objet.par1)
if (!centre) return console.error('pas trouvé le centre de nom ' + objet.par1)
const posCentre = repere.RenvoiePixels(centre.x, centre.y)
const rayon = Number(objet.rayon || objet.par2) * repere.pixelspargraduationX
const svgOptions = objet.style
const arcOptions = {
centreX: posCentre[0],
centreY: posCentre[1],
rayon,
angleDebut: Number(objet.angleDebut || objet.par3),
angle: Number(objet.angle || objet.par4)
}
if (objet.nom) arcOptions.id = objet.nom
// et on garde dans l’objet une ref sur l’élément svg créé
objet.svg = j3pCreeArc(repere.svg, svgOptions, arcOptions)
break
}
default:
// les noms préfixés par $ n’ont apparemment pas de type et c’est normal
if (!objet.nom || objet.nom[0] !== '$') console.error(Error('type inconnu pour cet objet'), objet)
}
}
} // construitObjets
/**
* Crée un path avec objet.d
* @private
* @param svg
* @param objet
* @return {SVGPathElement}
*/
function creeFace (svg, objet) {
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
path.setAttribute('id', objet.id)
path.setAttribute('d', objet.d)
path.setAttribute('style', 'stroke:' + objet.couleur + ';fill:' + objet.couleurRemplissage + ';stroke-width:0;fill-opacity:' + objet.opaciteRemplissage + ';stroke-dasharray:' + objet.pointilles)
if (typeof svg === 'string') svg = j3pElement(svg)
svg.appendChild(path)
return path
}
/**
* @private
* @param {string|SVGElement} svg
* @param objet
* @return {*}
*/
function creeEllipse2 (svg, objet) {
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
path.setAttribute('id', objet.id)
const X0 = objet.centreX
const Y0 = 300 - objet.centreY
const X1 = objet.ArriveX
const Y1 = objet.ArriveY
const Rx = objet.rx
const Ry = objet.ry
const c8 = objet.p8
const c9 = objet.p9
let ch = 'M ' + X0 + ',' + Y0 + ' '
ch += ' a ' + Rx + ' ' + Ry + ' ' + 0 + ' ' + c8 + ' ' + c9 + ' ' + X1 + ',' + Y1
path.setAttribute('d', ch)
path.setAttribute('style', 'stroke:' + objet.couleur + ';fill:' + objet.couleurIn + ';stroke-width:' + objet.epaisseur + ';fill-opacity:' + objet.opacite + ';stroke-dasharray:' + objet.pointilles)
if (typeof (svg) === 'string') svg = j3pElement(svg)
svg.appendChild(path)
return path
}
/**
* @private
* @param {string|SVGElement} svg
* @param objet
*/
function creeEllipse3 (svg, objet) {
function ladist (p1, p2) {
return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))
}
let lim1 = 9999999999
let lim2 = 9999999999
if (objet.style.pointilles) {
lim1 = parseFloat(objet.style.pointilles.substring(0, objet.style.pointilles.indexOf(',')))
lim2 = parseFloat(objet.style.pointilles.substring(objet.style.pointilles.indexOf(',') + 1))
}
let tour = true
let pointref = {
x: objet.centreX + objet.rx * Math.cos(objet.angledeb),
y: objet.centreY - objet.ry * Math.sin(objet.angledeb)
}
for (let i = objet.angledeb; i < objet.anglefin; i++) {
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
path.setAttribute('id', objet.id + i)
const angle1 = (i / 180) * Math.PI
const angle2 = (i + 1) / 180 * Math.PI
const X0 = objet.centreX + objet.rx * Math.cos(angle1)
const Y0 = objet.centreY - objet.ry * Math.sin(angle1)
const X1 = objet.centreX + objet.rx * Math.cos(angle2)
const Y1 = objet.centreY - objet.ry * Math.sin(angle2)
const pointar = { x: X1, y: Y1 }
const unedsit = ladist(pointref, pointar)
if (tour) {
if (unedsit > lim1) {
tour = false
pointref = pointar
}
} else {
if (unedsit > lim2) {
tour = true
pointref = pointar
}
}
if (tour) {
let ch = 'M ' + X0 + ' ' + Y0 + ' '
ch += ' A ' + objet.rx + ' ' + objet.ry + ' ' + ' 0 0 ,0 ' + X1 + ' ' + Y1// "M "+X0+" "+Y0+
path.setAttribute('d', ch)
path.setAttribute('style', 'stroke:' + objet.style.couleur + ';fill:none;stroke-width:2;fill-opacity:' + objet.style.opacite)
if (typeof svg === 'string') svg = j3pElement(svg)
svg.appendChild(path)
}
}
}
/**
* Retourne un nombre de 12 à 35 suivant la longueur de ch
* pour decalage à gauche par rapport à (0y) ???
* @param {string} ch
* @return {number}
* @private
*/
function decalage (ch) {
switch (ch.length) {
case 1:
return 12
case 2:
return 17
case 3:
return 22
case 4:
return 25
case 5:
return 30
default :
return 35
}
}
/**
* Retourne la position de la souris relative au svg (en pixels, même si la souris est hors du svg)
* @private
* @param {Repere} repere
* @param {MouseEvent} event
* @return {XY}
*/
function getPosRelSouris (repere, event) {
// les deux sont relatifs au viewport
const { clientX, clientY } = event
const { x, y } = repere.svg.getBoundingClientRect()
return {
x: clientX - x,
y: clientY - y
}
}
/**
* Retourne un vecteur de même direction et de norme 1
* @param {number[]} vect
* @return {number[]}
* @private
*/
function normalise (vect) {
const x = vect[0]
const y = vect[1]
const norme = Math.sqrt(x * x + y * y)
return [x / norme, y / norme]
}
/**
* Appelé sur le mouseMove (avec un bind !), une catastrophe car on détruit et reconstruit tout dès qu’un point est sélectionné !
* @param event
* @this Repere
* @private
*/
function onMove (event) {
const repere = this
if (!this.hasMouseDown) return
const { x, y } = getPosRelSouris(repere, event)
if (!repere.pointselectionne) {
// si y’a pas de point sélectionné, que le clic est enfoncé et que le repère est mobile faut le déplacer
if (!repere.fixe) {
repere.xO += x - repere._lastClickPos.x
repere.yO += y - repere._lastClickPos.y
repere._lastClickPos = { x, y }
repere.construit()
}
// rien à faire d’autre
return
}
// y’a un point sélectionné => on va détruire et recréer le svg à chaque fois que la souris bouge d’un pixel
// pourquoi tant de haine ?
// on passe en revue les points imposés dans tout repère
if (repere.pointselectionne === 'O') {
if (!repere.fixe) {
repere.xO = x
repere.yO = y
repere.construit()
}
return
}
if (repere.pointselectionne === '$I') {
repere.pixelspargraduationX = x - repere.xO
if (repere.pixelspargraduationX > 10) repere.construit()
return
}
if (repere.pointselectionne === '$J') {
repere.pixelspargraduationY = -y + repere.yO
if (repere.pixelspargraduationY > 10) repere.construit()
return
}
if (repere.pointselectionne === '$K') {
repere.pixelspargraduationX = Math.floor((event.pageX - j3pC().x - repere.xO) / 10)
if (repere.pixelspargraduationX > 10) repere.construit()
return
}
if (repere.pointselectionne === '$L') {
repere.pixelspargraduationY = Math.floor((-event.pageY + j3pC().y + repere.yO) / 10)
if (repere.pixelspargraduationY > 10) repere.construit()
return
}
// sinon c’est un autre point sélectionné
const objSelect = repere.getObjet(repere.pointselectionne)
if (objSelect.fixe) return
const cs = pos2coord(repere, { x, y })
if (objSelect.type === 'pointsur') {
// il faut changer le coefficient
// on recupere les extremites du segment (ou droite)
const indiceExt1 = objSelect.par1
const ext1 = repere.getObjet(indiceExt1)
const indiceExt2 = objSelect.par2 // extremité du segment
const ext2 = repere.getObjet(indiceExt2)
const eq = j3pEquation(ext1.x, ext1.y, ext2.x, ext2.y)
const proj = repere.projeteOrthogonal(eq, [cs.x, cs.y])
if (ext2.x === ext1.x) {
objSelect.par2 = (proj[1] - ext1.y) / ((ext2.y - ext1.y))
} else {
objSelect.par2 = (proj[0] - ext1.x) / ((ext2.x - ext1.x))
}
if (objSelect.type === 'segment') {
if (objSelect.par2 < 0) objSelect.par2 = 0.01
if (objSelect.par2 > 1) objSelect.par2 = 0.99
}
} else if (repere.aimantage) {
if ((Math.abs(objSelect.par1 - cs.x) > 0.8) || (Math.abs(objSelect.par2 - cs.y) > 0.8)) {
objSelect.par1 = Math.round(cs.x)
objSelect.par2 = Math.round(cs.y)
} else {
// ça a bougé un peu mais pas assez pour changer le point de place => rien à faire
return
}
} else {
objSelect.par1 = cs.x
objSelect.par2 = cs.y
}
repere.construit()
} // onMove
/**
* Transforme une position en pixel dans le repère en coordonnées
* @param {Repere} repere
* @param {XY} pos en pixels
* @return {XY} les coordonnées (3 décimales)
* @private
*/
function pos2coord (repere, pos) {
return {
x: j3pArrondi(((pos.x - repere.xO) / repere.pixelspargraduationX) * repere.pasdunegraduationX, 3),
y: j3pArrondi(((-pos.y + repere.yO) / repere.pixelspargraduationY) * repere.pasdunegraduationY, 3)
}
}
/**
* Constructeur d’un repère en svg
* @param {Object} props Les valeurs du repère
* @param {string|HTMLElement} props.idConteneur
* @param {string} [props.idDivRepere]
* @param {string} [props.indice=''] suffixe qui sera utilisé sur tous les id générés
* @param {string} props.pointselectionne
* @param {number} props.larg
* @param {number} props.haut
* @param {number} props.pasdunegraduationX
* @param {number} props.pixelspargraduationX
* @param {number} props.pasdunegraduationY
* @param {number} props.pixelspargraduationY
* @param {number} props.xO
* @param {number} props.y0
* @param {number} props.debuty
* @param {boolean} props.trame
* @param {boolean} props.negatifs
* @param {boolean} props.aimantage
* @param {boolean} [props.visible=false] passer true pour afficher les axes de coordonnées
* @param {boolean} props.fixe
* @param {boolean} [props.keepWeaksIds] passer true pour revenir à l’ancien comportement, qui mettait des ids complètement invalides (mais récupérables avec du getElementById)
* @param {Object[]} props.objets
* @constructor
*/
function Repere (props) {
// @todo virer ça dès que les sections arrêteront d’utiliser keepWeaksIds
// il faudra rechercher / remplacer dans ce fichier (en regex)
// this._idCleaner => cleanForId
// this._getId\((.+)\) => j3pGetNewId($1, true)
// et vérifier que _weakMode et keepWeaksIds n’existent plus dans le projet
this._weakMode = Boolean(props.keepWeaksIds)
if (this._weakMode) {
this._idCleaner = identity
this._getId = id => id + this._suffix
} else {
this._idCleaner = cleanForId
this._getId = id => j3pGetNewId(id + this._suffix, true)
}
const conteneur = typeof props.idConteneur === 'string' ? j3pElement(props.idConteneur) : props.idConteneur
if (!j3pIsHtmlElement(conteneur)) throw Error('conteneur invalide')
/**
* Conteneur du repere
* @type {HTMLElement}
*/
this.conteneur = conteneur
this._suffix = typeof props.indice === 'string'
? props.indice
: typeof props.indice === 'number'
? String(props.indice)
: ''
/**
* id du div construit autour du svg
* @type {string}
*/
this.idDivRepere = this._getId(props.idDivRepere || 'divRepere')
this.idRepere = this._getId('idRepere' + this._suffix)
this.larg = Number(props.larg) || 300
this.haut = Number(props.haut) || 300
// nécessaire si conteneur est lui-même dans un DIV avec propriété css overflow=true
this.pasdunegraduationX = props.pasdunegraduationX
this.pixelspargraduationX = props.pixelspargraduationX
this.pasdunegraduationY = props.pasdunegraduationY
this.pixelspargraduationY = props.pixelspargraduationY
this.pointselectionne = ''
this.debuty = props.debuty
this.xO = typeof props.xO === 'number' ? props.xO : this.larg / 2
this.yO = typeof props.yO === 'number' ? props.yO : this.haut / 2
this.couleur = {
ptf: '#00F',
ptm: '#F00'
}
this.trame = Boolean(props.trame)
this.negatifs = props.negatifs
this.aimantage = props.aimantage
this.hasMouseDown = false
/**
* Position de la souris au dernier clic (en pixels)
* Ne sert que pour la translation du repère (dans onMove)
* @private
* @type {XY}
*/
this._lastClickPos = {
x: 0,
y: 0
}
this.visible = Boolean(props.visible)
this.fixe = Boolean(props.fixe)
/**
* La liste des objets du repère, ne pas la modifier directement, passer par `add(objet)` pour ajouter ou `getObjet(nom)` pour récupérer
* @type {RepereObject[]}
*/
this.objets = []
/**
* Idem objets mais indexés par leur nom
* @type {Map<string, RepereObject>}
*/
this.objetsByNom = new Map()
// on init avec qq points
// origine
this.add({ type: 'point', nom: '_O', par1: 0, par2: 0 })
// unité X
this.add({
type: 'point',
nom: '_I',
par1: 1,
par2: 0,
fixe: false,
visible: false,
etiquette: false,
style: { couleur: '#456', epaisseur: 2, taille: 18, taillepoint: 5 }
})
// unité Y, pourquoi des params ≠ de x ?
this.add({ type: 'point', nom: '_J', par1: 0, par2: 1 })
// puis ceux-là (à quoi ils servent ?)
this.add({ nom: '$I', par1: this.pasdunegraduationX, par2: 0 })
this.add({ nom: '$J', par1: 0, par2: this.pasdunegraduationY })
this.add({ nom: '$L', v: 10 * this.pasdunegraduationY })
this.add({ nom: '$K', par1: 10 * this.pasdunegraduationX, par2: 0 })
// puis ceux fournis (qui ne pourront pas réutiliser les noms réservés des points déjà mis ci-dessus)
for (const obj of props.objets) this.add(obj)
// objetsByNom est désormais complet, et on veut interdire l’ajout direct d’objets (si la section affecte directement la propriété objets tant pis pour elle…)
this.objets.push = (obj) => {
console.error(Error('Il ne faut pas ajouter directement des objets au tableau d’objets, passez par la méthode add'))
this.add(obj)
}
} // Repere
Repere.prototype._check = function _check (props, type) {
if (!props.nom) props.nom = this.getNom('d')
if (props.type !== type) props.type = 'droite'
if (!this.objetsByNom.has(props.nom)) this.add(props)
}
/**
* Construit (ou reconstruit) tout le svg (le repère et ses objets)
*/
Repere.prototype.construit = function () {
// le div contenant le svg
if (this.divRepere) {
if (this.divRepere.parentNode !== this.conteneur) {
// bizarre, il est sorti du dom ? Parti ailleurs ? On s'étonne pas plus que ça et on le remet à sa place
this.conteneur.appendChild(this.divRepere)
}
j3pEmpty(this.divRepere)
} else {
// ALEX : le positionnement absolu posait pb sous chrome avec la bibli...
/** @type {HTMLElement} */
this.divRepere = j3pAddElt(this.conteneur, 'div', '', { id: this.idDivRepere, style: { position: 'relative' } })
}
// création du svg, dans un span qui ne sert que pour le mouseout
const svg = j3pCreeSVG(this.divRepere, { id: this.idRepere, width: this.larg, height: this.haut })
this.svg = svg
// les lignes qui suivent semblent totalement inutiles, pourquoi récupérer la largeur du svg qu’on vient de construire en lui imposant une largeur ?
// la remarque de la ligne suivante semble complètement obsolète (width, height ne correspondent pas à un décalage, et _moveListener ne s’en sert pas)
// et on note le décalage du svg (ça sert au mouseMove, pas la peine de le recalculer à chaque fois)
const { width, height } = this.svg.getBoundingClientRect()
// la taille
// Dans les fênetres contruites par j3pCreeFenetres (comme à la correction de limites_ln1), this.larg et this.haut possèdent bien des valeurs
// utilisées ligne 696. Sauf quand ligne 699, width et height valent tous deux 0. Je ne sais pas pourquoi
// Voilà pourquoi j’ai ajouté ces if (width) et if (height) dans les lignes ci-dessous
// @todo il faudra peut-être trouver d’où vient le pb et virer cette rustine
if (width) this.larg = width
if (height) this.haut = height
// fin des lignes à virer
// on veut mettre le listener au mousedown et le virer au mouseup, il faut donc une ref dessus
// mais il faut aussi que la méthode construitpoint puisse le mettre (et que ce soit ce mouseup qui le vire)
// => on l’ajoute en propriété
this._moveListener = onMove.bind(this)
// mouseup sur le document, pour toujours le choper (sauf si la souris sort de la fenêtre bouton enfoncé, d’ou le mouseout)
const abort = () => {
svg.removeEventListener('mousemove', this._moveListener, false)
this.pointselectionne = ''
this.hasMouseDown = false
}
svg.addEventListener('mouseup', abort)
svg.addEventListener('mousedown', (event) => {
if (this.fixe || event.target !== event.currentTarget) return
// le svg déplaçable est le seul cas où on veut le moveListener même sans point sélectionné (pour translater le svg)
this.hasMouseDown = true
this._lastClickPos = getPosRelSouris(this, event)
svg.addEventListener('mousemove', this._moveListener, false)
})
svg.addEventListener('mouseleave', (event) => {
// il faut utiliser mouseleave est surtout pas mouseout (cf https://developer.mozilla.org/fr/docs/Web/API/Element/mouseleave_event)
if (event.target !== event.currentTarget) return
abort()
}, false)
// si on reconstruit le svg en étant appelé par onMove, il faut remettre le listener
if (this.hasMouseDown) svg.addEventListener('mousemove', this._moveListener, false)
if (this.visible || this.trame) {
const epaisseur = this.visible ? 2 : 1
const opacite = this.visible ? 1 : 0.3
// les axes
j3pCreeSegment(this.svg, {
id: this._getId('axeX'),
x1: 0,
y1: this.yO,
x2: this.larg,
y2: this.yO,
couleur: '#000000',
epaisseur,
opacite
})
j3pCreeSegment(this.svg, {
id: this._getId('axeY'),
x1: this.xO,
y1: 0,
x2: this.xO,
y2: this.haut,
couleur: '#000000',
epaisseur,
opacite
})
// Rémi - Modif de l’option fixe dans chacune des 4 lignes (this.fixe et non plus false)'
if (this.visible) {
// Si c’est pas fixe faut créer un point pour pouvoir le bouger
// Si fixe, son étiquette suffirait, mais on crée quand même le point pour rester homogène
// (O est un point réservé et ne peut être ajouté)
this.construitpoint({
nom: 'O',
visible: false,
abscisse: 0,
ordonnee: 0,
taillepoint: 3,
taillepolice: 16,
police: 'sans-serif',
couleur: this.couleur.ptm,
epaisseur: 2,
nbdecimales: 2,
fixe: this.fixe
})
// étiquette de l’origine
j3pCreeTexte(this.svg, {
x: this.xO + 3,
y: this.yO + 15,
texte: '0',
taille: 12,
couleur: 'blue',
italique: false,
fonte: 'serif'
})
}
let k
// graduations de (Ox)
const nbLignesVert = Math.floor((this.larg - this.xO) / this.pixelspargraduationX)
for (k = 1; k <= nbLignesVert; k++) {
const x = this.xO + k * this.pixelspargraduationX
if (this.visible) {
j3pCreeSegment(this.svg, {
x1: x,
y1: this.yO - 4,
x2: x,
y2: this.yO + 4,
couleur: '#000000',
epaisseur: 2,
opacite: 1
})
j3pCreeTexte(this.svg, {
x: x - 5,
y: this.yO + 15,
texte: this.pasdunegraduationX * k,
taille: 12,
couleur: 'blue',
fonte: 'serif',
bold: true
})
}
if (this.trame) {
// ligne verticale de quadrillage
j3pCreeSegment(this.svg, {
x1: x,
y1: this.negatifs ? 1500 : this.yO,
x2: x,
y2: 0,
couleur: '#000000',
epaisseur: 1,
opacite: 0.3
})
}
}
// graduations de (Oy)
for (k = 1; k <= Math.floor((this.yO) / this.pixelspargraduationY); k++) {
const y = this.yO - k * this.pixelspargraduationY
if (this.visible) {
j3pCreeSegment(this.svg, {
x1: this.xO - 4,
y1: y,
x2: this.xO + 4,
y2: y,
couleur: '#000000',
epaisseur: 2,
opacite: 1
})
j3pCreeTexte(this.svg, {
x: -2 + this.xO - decalage('' + j3pArrondi(Number(this.debuty) + this.pasdunegraduationY * k, 2)),
y: y + 5,
texte: j3pArrondi(Number(this.debuty) + this.pasdunegraduationY * k, 2),
taille: 12,
couleur: 'blue',
italique: false,
fonte: 'serif',
bold: true
})
}
if (this.trame) {
// ligne horizontale de quadrillage
if (this.negatifs) {
j3pCreeSegment(this.svg, {
x1: 0,
y1: y,
x2: 1500,
y2: y,
couleur: '#000000',
epaisseur: 1,
opacite: 0.3
})
} else {
j3pCreeSegment(this.svg, {
x1: this.xO,
y1: y,
x2: 1500,
y2: y,
couleur: '#000000',
epaisseur: 1,
opacite: 0.3
})
}
}
}
// idem pour les négatifs
if (this.negatifs) {
// x négatifs
for (k = 1; k <= Math.floor((this.xO) / this.pixelspargraduationX); k++) {
const x = this.xO - k * this.pixelspargraduationX
if (this.visible) {
j3pCreeSegment(this.svg, {
x1: x,
y1: this.yO - 4,
x2: x,
y2: this.yO + 4,
couleur: '#000000',
epaisseur: 2,
opacite: 1
})
j3pCreeTexte(this.svg, {
x: x - 5,
y: this.yO + 15,
texte: '-' + this.pasdunegraduationX * k,
taille: 12,
couleur: 'blue',
italique: false,
fonte: 'serif',
bold: true
})
}
if (this.trame) {
j3pCreeSegment(this.svg, {
x1: x,
y1: 1500,
x2: x,
y2: 0,
couleur: '#000000',
epaisseur: 1,
opacite: 0.3
})
}
}
// y négatifs
for (k = 1; k <= Math.floor((this.haut - this.yO) / this.pixelspargraduationY); k++) {
const y = this.yO + k * this.pixelspargraduationY
if (this.visible) {
j3pCreeSegment(this.svg, {
x1: this.xO - 4,
y1: y,
x2: this.xO + 4,
y2: y,
couleur: '#000000',
epaisseur: 2,
opacite: 1
})
j3pCreeTexte(this.svg, {
x: -2 + this.xO - decalage('-' + this.pasdunegraduationY * k),
y: y + 5,
texte: '-' + this.pasdunegraduationY * k,
taille: 12,
couleur: 'blue',
italique: false,
fonte: 'serif',
bold: true
})
}
if (this.trame) {
j3pCreeSegment(this.svg, {
x1: 0,
y1: y,
x2: 1500,
y2: y,
couleur: '#000000',
epaisseur: 1,
opacite: 0.3
})
}
}
}
if (this.visible && !this.fixe) {
// on ajoute les points pour changer l’échelle (revient à zoom/dézoom)
this.construitpoint({
nom: '$I',
visible: false,
abscisse: this.pasdunegraduationX,
ordonnee: 0,
taillepoint: 3,
taillepolice: 16,
police: 'sans-serif',
couleur: this.couleur.ptm,
epaisseur: 2,
nbdecimales: 2,
fixe: this.fixe
})
// this.add({nom:'I',x:this.pasdunegraduationX,y:0})
this.construitpoint({
nom: '$J',
visible: false,
abscisse: 0,
ordonnee: this.pasdunegraduationY,
taillepoint: 3,
taillepolice: 16,
police: 'sans-serif',
couleur: this.couleur.ptm,
epaisseur: 2,
nbdecimales: 2,
fixe: this.fixe
})
// $L à (0, 10), pour quoi faire ?
this.construitpoint({
nom: '$L',
visible: false,
abscisse: 0,
ordonnee: 10 * this.pasdunegraduationY,
taillepoint: 3,
taillepolice: 16,
police: 'sans-serif',
couleur: this.couleur.ptm,
epaisseur: 2,
nbdecimales: 2,
fixe: this.fixe
})
// $K à (10, 0)
this.construitpoint({
nom: '$K',
visible: false,
abscisse: 10 * this.pasdunegraduationX,
ordonnee: 0,
taillepoint: 3,
taillepolice: 16,
police: 'sans-serif',
couleur: this.couleur.ptm,
epaisseur: 2,
nbdecimales: 2,
fixe: this.fixe
})
}
} // fin du cas visible ou trame
// on ajoute les objets fournis au constructeur ou ajoutés ensuite
construitObjets(this)
}
Repere.prototype.RenvoiePixels = function (abscisse, ordonnee) {
// 50 pixels signifie 50pixels à droite du centre de la droite graduée, cad du SVG la contenant.
return [
this.xO + (abscisse / this.pasdunegraduationX) * this.pixelspargraduationX,
this.yO - ((ordonnee) / this.pasdunegraduationY) * this.pixelspargraduationY
]
}
/**
* Attention, objet n’a pas les propriétés d’un élément de this.objets !!!
* Cf Repere.prototype.point
* @param objet
*/
Repere.prototype.construitpoint = function (objet) {
// objet={nom,abscisse,ordonnee,etiquette,taillepoint,taillepolice,police,couleur,epaisseur,nbdecimales,fixe}
const repere = this
const coordM = this.RenvoiePixels(objet.abscisse, objet.ordonnee)
if (typeof objet.taillepoint === 'undefined') objet.taillepoint = 0
if (typeof objet.visible === 'undefined') objet.visible = true
const nomToId = this._idCleaner(objet.nom)
if (objet.visible && (objet.taillepoint || this._weakMode)) {
// en _weakMode faut continuer à créer des segments de longueur nulle, sinon la section cherche ces ids et plante
j3pCreeSegment(this.svg, {
id: this._getId('ligne1' + nomToId),
className: 'croixPoint',
x1: coordM[0] - objet.taillepoint,
y1: coordM[1] - objet.taillepoint,
x2: coordM[0] + objet.taillepoint,
y2: coordM[1] + objet.taillepoint,
couleur: objet.couleur,
epaisseur: objet.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('ligne2' + nomToId),
className: 'croixPoint',
x1: coordM[0] + objet.taillepoint,
y1: coordM[1] - objet.taillepoint,
x2: coordM[0] - objet.taillepoint,
y2: coordM[1] + objet.taillepoint,
couleur: objet.couleur,
epaisseur: objet.epaisseur
})
}
if (objet.etiquette) {
let dx, dy
if (typeof objet.decal === 'undefined') {
dx = -9
dy = -14
} else if (typeof objet.decal === 'string') {
let posVirgule = objet.decal.indexOf(',')
let objNom = objet.decal.substring(0, posVirgule)
const obj1 = this.getObjet(objNom)
objet.decal = objet.decal.substring(posVirgule + 1)
posVirgule = objet.decal.indexOf(',')
objNom = objet.decal.substring(0, posVirgule)
const obj2 = this.getObjet(objNom)
objNom = objet.decal.substring(1 + posVirgule)
const obj3 = this.getObjet(objNom)
const pB = [obj1.x, obj1.y]
const pA = [obj2.x, obj2.y]
const pC = [obj3.x, obj3.y]
const d1 = j3pDistance(pA, pB)
const d2 = j3pDistance(pA, pC)
const vecAB = [(pB[0] - pA[0]) / d1, (pB[1] - pA[1]) / d1]
const vecAC = [(pC[0] - pA[0]) / d2, (pC[1] - pA[1]) / d2]
let vecu = [-vecAB[0] - vecAC[0], -vecAB[1] - vecAC[1]]
const d3 = j3pDistance([0, 0], vecu)
vecu = [vecu[0] / d3, vecu[1] / d3]
const poseti = [pA[0] + vecu[0] / 1.5, pA[1] + vecu[1] / 1.5]
dx = this.RenvoiePixels(poseti[0], poseti[1])[0] - this.RenvoiePixels(pA[0], pA[1])[0]
dy = this.RenvoiePixels(poseti[1], poseti[1])[1] - this.RenvoiePixels(pA[0], pA[1])[1]
if ((pA[1] > pB[1]) && (pA[1] > pC[1])) {
if ((pA[0] < pB[0]) && (pA[0] < pC[0])) {
// au-dessus à gauche des 2
dx += 0
dy += 0
} else {
dx -= 10
dy += 5
}
} else {
if ((pA[0] < pB[0]) && (pA[0] < pC[0])) {
// pas au-dessus mais à gauche
dx += 0
dy += 0
} else {
dx += 5
dy += 0
}
}
} else {
dx = -9 + objet.decal[0]
dy = -14 + objet.decal[1]
}
if (objet.visible) {
j3pCreeTexte(this.svg, {
id: this._getId('nompoint' + nomToId),
x: coordM[0] + dx,
y: coordM[1] + dy,
texte: objet.label || objet.nom,
taille: objet.taillepolice,
couleur: objet.couleur,
italique: objet.italique,
fonte: objet.police
})
}
}
if (!objet.fixe) {
// on crée un cadre autour du point (12px de coté) pour pouvoir le capturer
const lecadrepoint = j3pCreeRectangle(this.svg, {
id: this._getId('cadrepoint' + nomToId),
x: coordM[0] - 6,
y: coordM[1] - 6,
width: 12,
height: 12,
couleur: 'black',
couleurRemplissage: '#F00',
opaciteRemplissage: 0.01,
epaisseur: 0
})
lecadrepoint.addEventListener('mousedown', (event) => {
if (event.target !== lecadrepoint) return
repere.pointselectionne = objet.nom
repere.hasMouseDown = true
repere.svg.addEventListener('mousemove', repere._moveListener, false)
})
// pas besoin de mouseup (qui faisait la même chose que le listener sur le svg, et en plus virait lecadrepoint
// ce qui était un bug ! (si on clique sur le point sans le bouger assez pour recontruire le tout,
// puis qu’on le lâche => on ne pouvait plus le recliquer)
}
} // construitpoint
/**
* Retourne la propriété x de l’objet
* @param {string} nomObjet
* @return {number}
*/
Repere.prototype.abs = function (nomObjet) {
return this.getObjet(nomObjet).x
}
/**
* Retourne un nom qui n’existe pas encore dans le repère, soit prefix soit prefix_nn où nn est un entier (démarre à 2)
* @param prefix
* @return {string}
*/
Repere.prototype.getNom = function getNom (prefix) {
if (!prefix || typeof prefix !== 'string') throw Error(`nom ${prefix} invalide`)
let i = 2
let nom = prefix
while (this.objetsByNom.has(nom)) nom = `${prefix}_${i++}`
return nom
}
/**
* Ajoute un objet au repère (sans le construire)
* Accepte plusieurs params.
* @param {Object} objet
*/
Repere.prototype.add = function add (objet) {
// au cas où il resterait des appels avec plusieurs arguments
if (arguments.length > 1) {
j3pNotify(Error('appel de repere.add avec plusieurs arguments'))
for (const arg of arguments) this.add(arg)
return
}
if (!objet || typeof objet !== 'object') throw Error('objet invalide')
if (objet.nom && typeof objet.nom !== 'string') throw Error('nom invalide')
if (!objet.nom) objet.nom = this.getNom('_obj')
const { nom } = objet
if (this.objetsByNom.has(nom)) {
objet.nom = this.getNom(nom)
console.error(Error(`Il y a déjà un objet avec le nom ${nom}, ce nom sera remplacé par ${objet.nom}`))
}
this.objetsByNom.set(objet.nom, objet)
// on peut plus utiliser this.objets.push() puisqu’on la redéfinit et ça nous appelle
Array.prototype.push.call(this.objets, objet)
}
/**
* Retire l’objet nom
* @param {string} nom
* @return {boolean} false s’il n’y était pas, true sinon
*/
Repere.prototype.remove = function remove (nom) {
if (!this.objetsByNom.has(nom)) return false
this.objetsByNom.remove(nom)
// on pourrait utiliser Array.from(this.objetsByNom) mais l’ordre ne serait pas forcément conservé
this.objets = this.objets.filter(o => o.nom !== nom)
}
/**
* Retourne la propriété y de l’objet
* @param {string} nomObjet
* @return {number}
*/
Repere.prototype.ord = function (nomObjet) {
return this.getObjet(nomObjet).y
}
/**
* Init l’objet point et appelle construitpoint
* @param {Object} props les propriétés de l’objet point
* @param {number} props.x
* @param {number} props.y
* @param {boolean} props.fixe
* @param {boolean} props.etiquette s’il faut afficher le libellé du point (label ou nom)
* @param {Object} props.style
* @param {string} props.label
* @param style
*/
Repere.prototype.point = function point (objPoint) {
this._check(objPoint, 'point')
const { nom, x, y, fixe, etiquette, style = {}, label } = objPoint
this.construitpoint({
nom,
decal: style.decal || [0, 0],
abscisse: x,
ordonnee: y,
taillepoint: style.taillepoint,
taillepolice: style.taille,
police: 'sans-serif',
italique: style.italique,
couleur: style.couleur,
epaisseur: style.epaisseur,
nbdecimales: 2,
fixe,
etiquette,
label
})
}
/**
* Ajoute l’objet de type pointsur au svg
* @param {RepereObject} props
* @param {} props.nom
* @param {} props.par1
* @param {} props.par2
* @param {} props.fixe
* @param {} props.etiquette
* @param {} props.style
* @param {} props.label
*/
Repere.prototype.pointsur = function pointsur (objPoint) {
this._check(objPoint, 'pointsur')
const { nom, par1, par2, fixe, etiquette, style, label } = objPoint
let ext1, ext2
if (typeof par2 === 'string') {
// on suppose l’objet de ref être droite|segment|demidroite, mais on vérifie rien…
ext1 = par1
ext2 = par2
} else {
// on ignore par2 et on suppose par1 suffisant (un segment ?)
const objRef = this.getObjet(par1)
if (objRef.type === 'cercle') {
const rapport = this.pixelspargraduationX / this.pasdunegraduationX
const rayon = objRef.rayon / rapport
const objCentre = this.getObjet(objRef.par1)
const coord = [objCentre.x, objCentre.y]
objPoint.x = coord[0] + Math.cos((2 * par2 / 360) * Math.PI) * rayon
objPoint.y = coord[1] + Math.sin((2 * par2 / 360) * Math.PI) * rayon
} else {
ext1 = objRef.par1
ext2 = objRef.par2
}
}
const dx = style.decal?.[0] ?? 0
const dy = style.decal?.[1] ?? 0
let pt1, pt2
// attention, ext1 et ext2 ne sont pas initialisé pour le cercle en par1 (sans par2)
if (ext1) {
const { x, y } = this.getObjet(ext1)
pt1 = [x, y]
const objPt2 = this.getObjet(ext2)
pt2 = [objPt2.x, objPt2.y]
objPoint.x = pt1[0] + par2 * (pt2[0] - pt1[0])
objPoint.y = pt1[1] + par2 * (pt2[1] - pt1[1])
}
this.construitpoint({
visible: objPoint.visible,
nom,
decal: [dx, dy],
abscisse: objPoint.x,
ordonnee: objPoint.y,
taillepoint: style.taillepoint,
taillepolice: style.taille,
police: 'sans-serif',
couleur: style.couleur,
epaisseur: style.epaisseur,
nbdecimales: 2,
fixe,
etiquette,
label
})
if (style.codage && pt1 && pt1) {
// seulement pour les milieux
const rapport = this.pixelspargraduationX / this.pasdunegraduationX
const vecteurU = normalise([pt2[0] - pt1[0], pt2[1] - pt1[1]])
const vecteurV = j3pImageRotation(0, 0, 45, vecteurU[0], vecteurU[1])
const milieu1 = [(pt1[0] + objPoint.x) / 2, (pt1[1] + objPoint.y) / 2]
const taille = 3
const taille2 = 6
const milieu1avant = [milieu1[0] - (taille / rapport) * vecteurU[0], milieu1[1] - (taille / rapport) * vecteurU[1]]
let ext1milieu1avant, ext2milieu1avant
if (style.codage === '/') {
ext1milieu1avant = this.RenvoiePixels(milieu1avant[0] - (taille2 / rapport) * vecteurV[0], milieu1avant[1] - (taille2 / rapport) * vecteurV[1])
ext2milieu1avant = this.RenvoiePixels(milieu1avant[0] + (taille2 / rapport) * vecteurV[0], milieu1avant[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu1avant[0],
y1: ext1milieu1avant[1],
x2: ext2milieu1avant[0],
y2: ext2milieu1avant[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
} else {
ext1milieu1avant = this.RenvoiePixels(milieu1avant[0] - (taille2 / rapport) * vecteurV[0], milieu1avant[1] - (taille2 / rapport) * vecteurV[1])
ext2milieu1avant = this.RenvoiePixels(milieu1avant[0] + (taille2 / rapport) * vecteurV[0], milieu1avant[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu1avant[0],
y1: ext1milieu1avant[1],
x2: ext2milieu1avant[0],
y2: ext2milieu1avant[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
const milieu1apres = [milieu1[0] + (taille / rapport) * vecteurU[0], milieu1[1] + (taille / rapport) * vecteurU[1]]
const ext1milieu1apres = this.RenvoiePixels(milieu1apres[0] - (taille2 / rapport) * vecteurV[0], milieu1apres[1] - (taille2 / rapport) * vecteurV[1])
const ext2milieu1apres = this.RenvoiePixels(milieu1apres[0] + (taille2 / rapport) * vecteurV[0], milieu1apres[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu1apres[0],
y1: ext1milieu1apres[1],
x2: ext2milieu1apres[0],
y2: ext2milieu1apres[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
}
const milieu2 = [(pt2[0] + objPoint.x) / 2, (pt2[1] + objPoint.y) / 2]
let ext1milieu2avant, ext2milieu2avant
const milieu2avant = [milieu2[0] - (taille / rapport) * vecteurU[0], milieu2[1] - (taille / rapport) * vecteurU[1]]
if (style.codage === '/') {
ext1milieu2avant = this.RenvoiePixels(milieu2[0] - (taille2 / rapport) * vecteurV[0], milieu2[1] - (taille2 / rapport) * vecteurV[1])
ext2milieu2avant = this.RenvoiePixels(milieu2[0] + (taille2 / rapport) * vecteurV[0], milieu2[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu2avant[0],
y1: ext1milieu2avant[1],
x2: ext2milieu2avant[0],
y2: ext2milieu2avant[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
} else {
ext1milieu2avant = this.RenvoiePixels(milieu2avant[0] - (taille2 / rapport) * vecteurV[0], milieu2avant[1] - (taille2 / rapport) * vecteurV[1])
ext2milieu2avant = this.RenvoiePixels(milieu2avant[0] + (taille2 / rapport) * vecteurV[0], milieu2avant[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu2avant[0],
y1: ext1milieu2avant[1],
x2: ext2milieu2avant[0],
y2: ext2milieu2avant[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
const milieu2apres = [milieu2[0] + (taille / rapport) * vecteurU[0], milieu2[1] + (taille / rapport) * vecteurU[1]]
const ext1milieu2apres = this.RenvoiePixels(milieu2apres[0] - (taille2 / rapport) * vecteurV[0], milieu2apres[1] - (taille2 / rapport) * vecteurV[1])
const ext2milieu2apres = this.RenvoiePixels(milieu2apres[0] + (taille2 / rapport) * vecteurV[0], milieu2apres[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu2apres[0],
y1: ext1milieu2apres[1],
x2: ext2milieu2apres[0],
y2: ext2milieu2apres[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
}
} else if (style.codage) {
console.error(Error('On ne peut pas utiliser le codage sur un cercle (option codage ignorée)'))
}
}
/**
* Retourne l’équation cartésienne d’une droite
* @param d
* @return {number[]} Un tableau de 3 nombres (vide si c’est un truc non géré)
*/
Repere.prototype.equation = function (d) {
// retourne l’équation cartésienne
const objet = this.getObjet(d)
const { cd, type } = objet
switch (type) {
case 'droite':
case 'demidroite': {
const pt1 = this.getObjet(objet.par1)
const pt2 = this.getObjet(objet.par2)
const xp1 = pt1.par1
const yp1 = pt1.par2
const xp2 = pt2.par1
const yp2 = pt2.par2
return [-(yp2 - yp1), xp2 - xp1, -(yp2 - yp1) * xp1 + (xp2 - xp1) * yp1]
}
case 'droiteparallele': {
const { par1, par2 } = this.getObjet(objet.par1)
if (cd === Number.POSITIVE_INFINITY) return [-1, 0, par1]
return [-cd, 1, -cd * par1 + par2]
}
case 'droiteperpendiculaire': {
const { x, y } = this.getObjet(objet.par1)
if (cd === Number.POSITIVE_INFINITY) return [1, 0, x]
if (cd === 0) return Number.POSITIVE_INFINITY
return [-cd, 1, -cd * x + y]
}
default:
console.error(Error('Le type d’objet ' + type + ' n’est pas valide pour trouver l’équation cartésienne d’une droite'))
return []
}
}
/**
* Retourne la distance entre deux points
* @param {string} nom1
* @param {string} nom2
* @return {number}
*/
Repere.prototype.distance = function (nom1, nom2) {
const p1 = this.getObjet(nom1)
const p2 = this.getObjet(nom2)
return j3pDistance(p1.x, p1.y, p2.x, p2.y)
}
Repere.prototype.intersection = function (d1, d2) {
const tab1 = this.equation(d1)
const tab2 = this.equation(d2)
const det = tab1[0] * tab2[1] - tab1[1] * tab2[0]
if (!det) return null
return [
(tab1[2] * tab2[1] - tab1[1] * tab2[2]) / det,
(tab1[0] * tab2[2] - tab2[0] * tab1[2]) / det
]
}
// tab = [a,b,c] : ax+by+c=0
Repere.prototype.intersection2 = function (d1, tab) {
const tab1 = this.equation(d1)
const tab2 = tab
const det = tab1[0] * tab2[1] - tab1[1] * tab2[0]
if (det === 0) return null
return [(tab1[2] * tab2[1] - tab1[1] * tab2[2]) / det, (tab1[0] * tab2[2] - tab2[0] * tab1[2]) / det]
}
Repere.prototype.projeteOrthogonal = function (tab, coord) {
const xM = coord[0]
const yM = coord[1]
const xH = (tab[1] * tab[1] * xM - tab[0] * tab[1] * yM - tab[0] * tab[2]) / (tab[0] * tab[0] + tab[1] * tab[1])
const yH = (tab[0] * tab[0] * yM - tab[0] * tab[1] * xM - tab[1] * tab[2]) / (tab[0] * tab[0] + tab[1] * tab[1])
return [xH, yH]
}
Repere.prototype.pointintersection = function (pt1, par1, par2, pfixe, petiquette, style) {
const point = this.getObjet(pt1)
const inter = this.intersection(par1, par2)
if (inter !== 'vide') {
const x1 = inter[0]
const y1 = inter[1]
point.x = x1
point.y = y1
this.construitpoint({
nom: pt1,
abscisse: x1,
ordonnee: y1,
taillepoint: style.taillepoint,
taillepolice: style.taille,
police: 'sans-serif',
decal: style.decal,
italique: style.italique,
couleur: style.couleur,
epaisseur: style.epaisseur,
nbdecimales: 2,
fixe: pfixe,
etiquette: petiquette,
label: point.label
})
}
}
/**
* ex d’appel this.construitsegment('L','K',{couleur:'#000',epaisseur:2})
* @param {Object} props
* @param {string} [props.nom]
* @param {string} props.par1
* @param {string} props.par2
* @param {Object} props.style style du segment
* @param {Object} [props.graduations] Le style des graduations (si omis y’en a pas)
*/
Repere.prototype.segment = function (props) {
if (!props.nom) props.nom = this.getNom('_')
const { par1: pt1, par2: pt2, style, graduations } = props
if (!pt1 || !pt2) {
console.error(Error('props invalides pour un segment'), props)
}
const ptext1 = this.getObjet(pt1)
if (!ptext1) return // getObjet a déjà ralé
let tab = this.RenvoiePixels(ptext1.x, ptext1.y)
let xpt1 = tab[0]
let ypt1 = tab[1]
const ptext2 = this.getObjet(pt2)
if (!ptext2) return
tab = this.RenvoiePixels(ptext2.x, ptext2.y)
let xpt2 = tab[0]
let ypt2 = tab[1]
if (isNaN(xpt1)) { xpt1 = ptext1.par1 }
if (isNaN(ypt1)) { ypt1 = ptext1.par2 }
if (isNaN(xpt2)) { xpt2 = ptext2.par1 }
if (isNaN(ypt2)) { ypt2 = ptext2.par2 }
j3pCreeSegment(this.svg, {
id: this._getId('segment' + this._idCleaner(pt1 + pt2)),
x1: xpt1,
y1: ypt1,
x2: xpt2,
y2: ypt2,
couleur: style.couleur,
epaisseur: style.epaisseur,
pointilles: style.pointilles
})
if (typeof graduations !== 'undefined') {
j3pGradueSegment(this.svg, {
x1: xpt1,
y1: ypt1,
x2: xpt2,
y2: ypt2,
style: graduations
})
}
if (style.codage) {
// seulement pour les milieux
const rapport = this.pixelspargraduationX / this.pasdunegraduationX
const vecteurU = normalise([pt2[0] - pt1[0], pt2[1] - pt1[1]])
const vecteurV = j3pImageRotation(0, 0, 45, vecteurU[0], vecteurU[1])
const milieu1 = [(pt1[0] + pt2[0]) / 2, (pt1[1] + pt2[1]) / 2]
const taille = 3
const taille2 = 6
const milieu1avant = milieu1
let ext1milieu1avant, ext2milieu1avant
if (style.codage === '/') {
ext1milieu1avant = this.RenvoiePixels(milieu1avant[0] - (taille2 / rapport) * vecteurV[0], milieu1avant[1] - (taille2 / rapport) * vecteurV[1])
ext2milieu1avant = this.RenvoiePixels(milieu1avant[0] + (taille2 / rapport) * vecteurV[0], milieu1avant[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu1avant[0],
y1: ext1milieu1avant[1],
x2: ext2milieu1avant[0],
y2: ext2milieu1avant[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
} else {
ext1milieu1avant = this.RenvoiePixels(milieu1avant[0] - (taille2 / rapport) * vecteurV[0], milieu1avant[1] - (taille2 / rapport) * vecteurV[1])
ext2milieu1avant = this.RenvoiePixels(milieu1avant[0] + (taille2 / rapport) * vecteurV[0], milieu1avant[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu1avant[0],
y1: ext1milieu1avant[1],
x2: ext2milieu1avant[0],
y2: ext2milieu1avant[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
const milieu1apres = [milieu1[0] + (taille / rapport) * vecteurU[0], milieu1[1] + (taille / rapport) * vecteurU[1]]
const ext1milieu1apres = this.RenvoiePixels(milieu1apres[0] - (taille2 / rapport) * vecteurV[0], milieu1apres[1] - (taille2 / rapport) * vecteurV[1])
const ext2milieu1apres = this.RenvoiePixels(milieu1apres[0] + (taille2 / rapport) * vecteurV[0], milieu1apres[1] + (taille2 / rapport) * vecteurV[1])
j3pCreeSegment(this.svg, {
x1: ext1milieu1apres[0],
y1: ext1milieu1apres[1],
x2: ext2milieu1apres[0],
y2: ext2milieu1apres[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
}
}
}
/**
* @param nom
* @param par1
* @param par2
* @param par3
* @param par4
* @param style
*/
Repere.prototype.secteur = function (nom, par1, par2, par3, par4, style) {
// console.warn(Error('Il faut remplacer cette méthode par l’usage de j3pCreeSecteur'))
const pt1 = this.getObjet(par1)
const coord1 = [pt1.x, pt1.y]
const tab1 = this.RenvoiePixels(coord1[0], coord1[1])
const lerayon = Number(par4) * this.pixelspargraduationX
j3pCreeSecteur(this.svg, {
id: this._getId('secteur' + this._idCleaner(nom)),
centreX: tab1[0],
centreY: tab1[1],
rayon: lerayon,
angleDebut: Number(par2),
anglefin: Number(par3),
couleur: style.couleur,
opacite: style.opacite
})
}
/**
* @param nom
* @param par1
* @param par2
* @param par3
* @param par4
* @param style
*/
Repere.prototype.angle = function (nom, par1, par2, par3, par4, style) {
const objPar1 = this.getObjet(par1)
const coord1 = [objPar1.x, objPar1.y]
const objPar2 = this.getObjet(par2)
const coord2 = [objPar2.x, objPar2.y]
const objPar3 = this.getObjet(par3)
const coord3 = [objPar3.x, objPar3.y]
const vec21 = [objPar1.x - objPar2.x, objPar1.y - objPar2.y]
const norm21 = j3pDistance(coord1, coord2)
const pt1 = [objPar2.x + (par4 / norm21) * vec21[0], objPar2.y + (par4 / norm21) * vec21[1]]
const vec23 = [coord3[0] - coord2[0], coord3[1] - coord2[1]]
const norm23 = j3pDistance(coord3, coord2)
const pt3 = [coord2[0] + (par4 / norm23) * vec23[0], coord2[1] + (par4 / norm23) * vec23[1]]
const tab1 = this.RenvoiePixels(pt1[0], pt1[1])
const tab2 = this.RenvoiePixels(coord2[0], coord2[1])
const tab3 = this.RenvoiePixels(pt3[0], pt3[1])
j3pCreeAngle(this.svg, {
id: this._getId('angle' + this._idCleaner(par1 + par2 + par3)),
tab1,
tab2,
tab3,
epaisseur: style.epaisseur,
couleur: style.couleur,
opacite: style.opacite
})
}
/**
* @param nom
* @param par1
* @param par2
* @param style
* @return {SVGCircleElement}
*/
Repere.prototype.cercle = function (nom, par1, par2, style) {
const pt1 = this.getObjet(par1)
const [x1, y1] = this.RenvoiePixels(pt1.x, pt1.y)
const props = {
id: this._getId('svgcercle' + this._idCleaner(par1 + par2)),
cx: x1 + 'px',
cy: y1 + 'px',
couleur: style.couleur,
epaisseur: style.epaisseur,
couleurRemplissage: style.couleurRemplissage,
opaciteRemplissage: style.opaciteRemplissage
}
const obj = this.getObjet(nom)
if (typeof par2 === 'number') {
// c’est le rayon
obj.rayon = par2
props.rayon = par2
} else if (typeof par2 === 'string') {
// c’est un point
const objPar2 = this.getObjet(par2)
const [x2, y2] = this.RenvoiePixels(objPar2.x, objPar2.y)
const vec = [x2 - x1, y2 - y1]
const rayon = Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1])
obj.rayon = rayon
props.rayon = rayon
} else {
console.error(Error('par2 invalide pour construire le cercle', par2))
return null
}
return j3pCreeCercle(this.svg, props)
}
/**
*
* @param {string} nom nom du cercle (qui doit déjà exister dans les objets…)
* @param {string} par1 nom d’un point du diamètre
* @param {string} par2 nom de l’autre point du diamètre
* @param {Object} style
* @return {SVGCircleElement}
*/
Repere.prototype.cercledia = function (nom, par1, par2, style) {
const cercle = this.getObjet(nom)
const p1 = this.getObjet(par1)
const [x1, y1] = this.RenvoiePixels(p1.x, p1.y)
const p2 = this.getObjet(par2)
const [x2, y2] = this.RenvoiePixels(p2.x, p2.y)
const vec = [(x2 - x1) / 2, (y2 - y1) / 2]
const rayon = Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1])
cercle.rayon = rayon
const xOrig = (x1 + x2) / 2
const yOrig = (y1 + y2) / 2
return j3pCreeCercle(this.svg, {
id: this._getId('cercledia' + this._idCleaner(nom)),
cx: xOrig + 'px',
cy: yOrig + 'px',
rayon,
couleur: style.couleur,
epaisseur: style.epaisseur,
couleurRemplissage: style.couleurRemplissage,
opaciteRemplissage: style.opaciteRemplissage
})
}
Repere.prototype.distancepointdroite = function (pt, d) {
const { x, y } = this.getObjet(pt)
const eq = this.equation(d)
return (Math.abs(eq[0] * x + eq[1] * y - eq[2]) / Math.sqrt(eq[0] * eq[0] + eq[1] * eq[1]))
}
Repere.prototype.polygone = function (nom, points, style) {
// points = ['A',...]
const coords = points.map(point => {
const objPoint = this.getObjet(point)
let px, py
if ((objPoint.type === 'pointsur') || (objPoint.type === 'pointintersection')) {
px = objPoint.x
py = objPoint.y
} else {
px = objPoint.par1
py = objPoint.par2
}
return this.RenvoiePixels(px, py)
})
j3pPolygone(this.svg, {
id: this._getId('id' + this._idCleaner(nom)),
tab: coords,
couleur: style.couleur,
couleurRemplissage: style.couleurRemplissage,
opaciteRemplissage: style.opaciteRemplissage,
epaisseur: style.epaisseur
})
}
Repere.prototype.face = function (nom, par1, style) {
creeFace(this.svg, {
id: this._getId('arc' + this._idCleaner(nom)),
d: par1,
couleur: style.couleur,
couleurRemplissage: style.couleurRemplissage,
opaciteRemplissage: style.opaciteRemplissage,
pointilles: style.pointilles
})
}
Repere.prototype.ellipse3 = function (nom, cx, cy, rx, ry, angledeb, anglefin, rotation, style) {
// par1 Nomcentre par2 rayon par3 angleDebut par4 anglefin
const Rx = Number(rx) * this.pixelspargraduationX
const Ry = Number(ry) * this.pixelspargraduationX
const Angledeb = Number(angledeb)
let Anglefin = Number(anglefin)
if (Anglefin < Angledeb) { Anglefin += 360 }
const Rotation = Number(rotation)
const Cx = Number(cx) * this.pixelspargraduationX
const Cy = 300 - (Number(cy) * this.pixelspargraduationX)
creeEllipse3(this.svg, {
id: this._getId('arc' + this._idCleaner(nom)),
centreX: Cx,
centreY: Cy,
rx: Rx,
ry: Ry,
angledeb: Angledeb,
anglefin: Anglefin,
rotation: Rotation,
style,
grad: this.pixelspargraduationX
})
}
Repere.prototype.ellipse2 = function (nom, par1, par2, par3, par4, par5, par6, par7, par8, par9, style) {
// par1 Nomcentre par2 rayon par3 angledebut par4 anglefin
const rx = Number(par5) * this.pixelspargraduationX
const ry = Number(par6) * this.pixelspargraduationX
const cx = Number(par1) * this.pixelspargraduationX
const cy = Number(par2) * this.pixelspargraduationX
const ax = Number(par3) * this.pixelspargraduationX
const ay = Number(par4) * this.pixelspargraduationX
const c7 = Number(par7) * this.pixelspargraduationX
const c8 = Number(par8) * this.pixelspargraduationX
const c9 = Number(par9) * this.pixelspargraduationX
creeEllipse2(this.svg, {
id: this._getId('arc' + this._idCleaner(nom)),
centreX: cx,
centreY: cy,
rx,
ry,
ArriveX: ax,
ArriveY: ay,
p7: c7,
p8: c8,
p9: c9,
couleur: style.couleur,
epaisseur: style.epaisseur,
couleurIn: style.couleur_remplissage,
opacite: style.opacite_remplissage,
pointilles: style.pointilles
})
}
// this.prisme(this.objets[k].nom,this.objets[k].par1,this.objets[k].par2,this.objets[k].par3,this.objets[k].par4,this.objets[k].style)
Repere.prototype.prisme = function (nom, par1, par2, par3, par4, par5, style) {
// par3 peut être du type 'distance(p1,p2)'
if (typeof par3 === 'string') {
// donc distance
const p1 = par3.substring(9, par3.indexOf(','))
const p2 = par3.substring(1 + par3.indexOf(','), par3.indexOf(')'))
par3 = this.distance(p1, p2)
}
// 6 polygones pleins à construire
// sommets stockés dans A,B,C,D,E,F,G,H qui sont des tableaux de coordonnées pixels
const o = this.getObjet(par1)
const A = this.RenvoiePixels(o.par1, o.par2)
const B = this.RenvoiePixels(o.par1 + par2, o.par2)
const E = this.RenvoiePixels(o.par1, o.par2 + par3)
const F = this.RenvoiePixels(o.par1 + par2, o.par2 + par3)
const D = this.RenvoiePixels(o.par1 + par4, o.par2 + par5)
const C = this.RenvoiePixels(o.par1 + par4 + par2, o.par2 + par5)
const H = this.RenvoiePixels(o.par1 + par4, o.par2 + par3 + par5)
const G = this.RenvoiePixels(o.par1 + par4 + par2, o.par2 + par3 + par5)
// face1 et face 2 devant
const nomForId = this._idCleaner(nom)
j3pPolygone(this.svg, {
id: this._getId('id' + nomForId + 'face1'),
tab: [A, B, F, E],
couleur: 'none',
couleurRemplissage: style.couleurdevant,
opaciteRemplissage: style.opaciteRemplissage / 2
})
j3pPolygone(this.svg, {
id: this._getId('id' + nomForId + 'face2'),
tab: [B, C, G, F],
couleur: 'none',
couleurRemplissage: style.couleurdevant,
opaciteRemplissage: style.opaciteRemplissage / 2
})
j3pPolygone(this.svg, {
id: this._getId('id' + nomForId + 'face3'),
tab: [C, G, H, D],
couleur: 'none',
couleurRemplissage: style.couleurderriere,
opaciteRemplissage: style.opaciteRemplissage
})
j3pPolygone(this.svg, {
id: this._getId('id' + nomForId + 'face4'),
tab: [A, D, H, E],
couleur: 'none',
couleurRemplissage: style.couleurderriere,
opaciteRemplissage: style.opaciteRemplissage
})
j3pPolygone(this.svg, {
id: this._getId('id' + nomForId + 'facedessous'),
tab: [A, B, C, D],
couleur: 'none',
couleurRemplissage: style.couleurdessous,
opaciteRemplissage: style.opaciteRemplissage
})
j3pPolygone(this.svg, {
id: this._getId('id' + nomForId + 'facedessus'),
tab: [E, F, G, H],
couleur: 'none',
couleurRemplissage: style.couleurdessus,
opaciteRemplissage: style.opaciteRemplissage
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's1'),
x1: A[0],
y1: A[1],
x2: B[0],
y2: B[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's2'),
x1: B[0],
y1: B[1],
x2: C[0],
y2: C[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's3'),
x1: C[0],
y1: C[1],
x2: D[0],
y2: D[1],
couleur: style.couleur,
epaisseur: style.epaisseur,
pointilles: '5,5'
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's4'),
x1: D[0],
y1: D[1],
x2: A[0],
y2: A[1],
couleur: style.couleur,
epaisseur: style.epaisseur,
pointilles: '5,5'
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's5'),
x1: A[0],
y1: A[1],
x2: E[0],
y2: E[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's6'),
x1: B[0],
y1: B[1],
x2: F[0],
y2: F[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's7'),
x1: C[0],
y1: C[1],
x2: G[0],
y2: G[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's8'),
x1: D[0],
y1: D[1],
x2: H[0],
y2: H[1],
couleur: style.couleur,
epaisseur: style.epaisseur,
pointilles: '5,5'
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's9'),
x1: E[0],
y1: E[1],
x2: F[0],
y2: F[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's10'),
x1: F[0],
y1: F[1],
x2: G[0],
y2: G[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's11'),
x1: G[0],
y1: G[1],
x2: H[0],
y2: H[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
j3pCreeSegment(this.svg, {
id: this._getId('id' + nomForId + 's11'),
x1: H[0],
y1: H[1],
x2: E[0],
y2: E[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
}
Repere.prototype.courbe = function courbe (nom, par1, par2, par3, par4, style) {
const tab = j3pTableauValeurs(par1, par2, par3, par4)
this.courbe_tabvaleur(nom, tab, style)
}
Repere.prototype.courbe_tabvaleur = function courbeTabvaleur (nom, tabVal, style) {
// cette fonction construit la courbe lorsqu’on a déjà le tableau de valeurs
// finalement c’est la deuxième partie de la méthode courbe
// la ligne suivante sera peut-être utile ?
// this.construitpoint({nom:'pt1',abscisse:tabVal[0][0],ordonnee:tabVal[0][1],taillepoint:3,taillepolice:16,police:'sans-serif',couleur:style.couleur,epaisseur:style.epaisseur})
for (let k = 1; k < tabVal.length; k = k + 1) {
// this.construitpoint({nom:'ptcourbe'+k,abscisse:tab[k][0],ordonnee:tab[k][1],taillepoint:3,taillepolice:16,police:'sans-serif',couleur:style.couleur,epaisseur:style.epaisseur})
const coordM1 = this.RenvoiePixels(tabVal[k - 1][0], tabVal[k - 1][1])
const coordM2 = this.RenvoiePixels(tabVal[k][0], tabVal[k][1])
j3pCreeSegment(this.svg, {
x1: coordM1[0],
y1: coordM1[1],
x2: coordM2[0],
y2: coordM2[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
}
}
/**
* Trace une droite
* @param {Object} props
* @param {string} props.par1 nom du 1er point (qui doit exister)
* @param {string} props.par2 nom du 2e point (qui doit exister)
* @param {Object} props.style
*/
Repere.prototype.droite = function droite (props) {
this._check(props, 'droite')
const { nom, par1, par2, style, couleur, label } = props
const o1 = this.getObjet(par1)
const o2 = this.getObjet(par2)
if (isNaN(o1.x)) o1.x = o1.par1
if (isNaN(o1.y)) o1.y = o1.par2
if (isNaN(o2.x)) o2.x = o2.par1
if (isNaN(o2.y)) o2.y = o2.par2
const id = this._getId('droite' + this._idCleaner(par1 + par2))
if (o1.x === o2.x) {
const coefY = (this.haut / this.pixelspargraduationY)
const [x1, y1] = this.RenvoiePixels(o1.x, -2 * coefY)
const [x2, y2] = this.RenvoiePixels(o1.x, 2 * coefY)
droite.cd = Number.POSITIVE_INFINITY
if (typeof style.opacite === 'undefined') {
j3pCreeSegment(this.svg, {
id,
x1,
y1,
x2,
y2,
couleur: style.couleur,
epaisseur: style.epaisseur
})
} else {
j3pCreeSegment(this.svg, {
id,
x1,
y1,
x2,
y2,
couleur: style.couleur,
epaisseur: style.epaisseur,
opacite: style.opacite
})
}
} else {
const cd = (o2.y - o1.y) / (o2.x - o1.x)
const oo = o2.y - cd * o2.x
droite.cd = cd
const absmin = -2 * (this.larg / this.pixelspargraduationX)
const absmax = 2 * (this.larg / this.pixelspargraduationX)
const yy1 = cd * absmin + oo
const yy2 = cd * absmax + oo
const [x1, y1] = this.RenvoiePixels(absmin, yy1)
const [x2, y2] = this.RenvoiePixels(absmax, yy2)
if (typeof style.opacite === 'undefined') {
j3pCreeSegment(this.svg, {
id,
x1,
y1,
x2,
y2,
couleur: style.couleur,
epaisseur: style.epaisseur
})
} else {
j3pCreeSegment(this.svg, {
id,
x1,
y1,
x2,
y2,
couleur: style.couleur,
epaisseur: style.epaisseur,
opacite: style.opacite
})
}
}
const minx = -this.xO / this.pixelspargraduationX
const maxx = (this.larg - this.xO) / this.pixelspargraduationX
const milieux = (minx + maxx) / 2
const maxy = this.yO / this.pixelspargraduationY
const miny = -(this.haut - this.yO) / this.pixelspargraduationY
const milieuy = (miny + maxy) / 2
let intersup = this.intersection2(nom, [0, 1, this.yO / this.pixelspargraduationY])
let dx, dy
if (style.nommage) {
if ((intersup[0] <= maxx) && (intersup[0] >= minx)) {
if (intersup[0] < milieux) {
dx = 10
dy = 12
} else {
dx = -30
dy = 12
}
intersup = this.RenvoiePixels(intersup[0], intersup[1])
j3pCreeTexte(this.svg, {
id: this._getId('nomdroite' + nom),
x: intersup[0] + dx,
y: intersup[1] + dy,
texte: label || '(' + nom + ')',
taille: 12,
couleur,
italique: false,
fonte: 'sans-serif'
})
} else {
intersup = this.intersection2(nom, [1, 0, (this.larg - this.xO) / this.pixelspargraduationX])
if (intersup[1] < milieuy) {
dx = -22
dy = 0
} else {
dx = -22
dy = 30
}
intersup = this.RenvoiePixels(intersup[0], intersup[1])
j3pCreeTexte(this.svg, {
id: this._getId('nomdroite' + nom),
x: intersup[0] + dx,
y: intersup[1] + dy,
texte: label || '(' + nom + ')',
taille: 12,
couleur: droite.couleur,
italique: false,
fonte: 'sans-serif'
})
}
}
} // droite
Repere.prototype.demidroite = function (props) {
this._check(props, 'demidroite')
const { nom, par1, par2, style, couleur, label } = props
const o1 = this.getObjet(par1)
const o2 = this.getObjet(par2)
let tab, tab1, tab2
const id = this._getId('droite' + this._idCleaner(par1 + par2))
if (o1.x === o2.x) {
const coefY = (this.haut / this.pixelspargraduationY)
tab = this.RenvoiePixels(o1.x, o1.y)
tab2 = this.RenvoiePixels(o1.x, 2 * coefY)
props.cd = Number.POSITIVE_INFINITY
if (o2.y < o1.y) tab2[1] = -tab2[1]
if (typeof style.opacite === 'undefined') {
j3pCreeSegment(this.svg, {
id,
x1: tab[0],
y1: tab[1],
x2: tab2[0],
y2: tab2[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
} else {
j3pCreeSegment(this.svg, {
id,
x1: tab[0],
y1: tab[1],
x2: tab2[0],
y2: tab2[1],
couleur: style.couleur,
epaisseur: style.epaisseur,
opacite: style.opacite
})
}
} else {
const cd = (o2.y - o1.y) / (o2.x - o1.x)
const oo = o2.y - cd * o2.x
props.cd = cd
const absmin = -2 * (this.larg / this.pixelspargraduationX)
const absmax = 2 * (this.larg / this.pixelspargraduationX)
const yy1 = cd * absmin + oo
const yy2 = cd * absmax + oo
const tabmin = this.RenvoiePixels(absmin, yy1)
const tabmax = this.RenvoiePixels(absmax, yy2)
tab1 = this.RenvoiePixels(o1.x, o1.y)
tab2 = this.RenvoiePixels(o2.x, o2.y)
let tab3
if (j3pPointDansSegment(tab2, tab1, tabmin)) {
tab3 = tabmin
} else {
tab3 = tabmax
}
if (typeof style.opacite === 'undefined') {
j3pCreeSegment(this.svg, {
id,
x1: tab1[0],
y1: tab1[1],
x2: tab3[0],
y2: tab3[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
} else {
j3pCreeSegment(this.svg, {
id,
x1: tab1[0],
y1: tab1[1],
x2: tab3[0],
y2: tab3[1],
couleur: style.couleur,
epaisseur: style.epaisseur,
opacite: style.opacite
})
}
}
const minx = -this.xO / this.pixelspargraduationX
const maxx = (this.larg - this.xO) / this.pixelspargraduationX
const milieux = (minx + maxx) / 2
const maxy = this.yO / this.pixelspargraduationY
const miny = -(this.haut - this.yO) / this.pixelspargraduationY
const milieuy = (miny + maxy) / 2
let intersup = this.intersection2(nom, [0, 1, this.yO / this.pixelspargraduationY])
let dx, dy
if (style.nommage) {
if ((intersup[0] <= maxx) && (intersup[0] >= minx)) {
if (intersup[0] < milieux) {
dx = 10
dy = 12
} else {
dx = -30
dy = 12
}
intersup = this.RenvoiePixels(intersup[0], intersup[1])
j3pCreeTexte(this.svg, {
id: this._getId('nomdroite' + nom),
x: intersup[0] + dx,
y: intersup[1] + dy,
texte: label || '(' + nom + ')',
taille: 12,
couleur,
italique: false,
fonte: 'sans-serif'
})
} else {
intersup = this.intersection2(nom, [1, 0, (this.larg - this.xO) / this.pixelspargraduationX])
if (intersup[1] < milieuy) {
dx = -22
dy = 0
} else {
dx = -22
dy = 30
}
intersup = this.RenvoiePixels(intersup[0], intersup[1])
j3pCreeTexte(this.svg, {
id: this._getId('nomdroite' + nom),
x: intersup[0] + dx,
y: intersup[1] + dy,
texte: label || '(' + nom + ')',
taille: 12,
couleur,
italique: false,
fonte: 'sans-serif'
})
}
}
} // demidroite
/**
* Trace une droite parallèle passant par un point
* @param {Object} props
* @param {string} props.par1 Nom du point (qui doit exister dans this.objets)
* @param {string} props.par2 Le nom de la droite (qui doit exister dans this.objets)
* @param {Object} style
*/
Repere.prototype.droiteparallele = function droiteparallele (props) {
this._check(props, 'droiteparallele')
const { par1, par2, style } = props
const o1 = this.getObjet(par1)
const o2 = this.getObjet(par2) // la droite parallèle
// les coord du point, qui dépendent de pointsur ou pas
let px, py
if (o1.type === 'pointsur') {
px = o1.x
py = o1.y
} else {
px = o1.par1
py = o1.par2
}
const cd = coefdir(this, o2.par1, o2.par2)
props.cd = cd
// ce qui dépend du cas vertical ou pas
let pt1, pt2
if (cd === Number.POSITIVE_INFINITY) {
// nom2 est une droite verticale
const ratio = this.haut / this.pixelspargraduationY
pt1 = this.RenvoiePixels(px, -2 * ratio)
pt2 = this.RenvoiePixels(px, 2 * ratio)
} else {
const oo = py - cd * px
const absmin = -(this.larg / this.pixelspargraduationX)
const absmax = 2 * (this.larg / this.pixelspargraduationX)
const yy1 = cd * absmin + oo
const yy2 = cd * absmax + oo
pt1 = this.RenvoiePixels(absmin, yy1)
pt2 = this.RenvoiePixels(absmax, yy2)
}
// on trace la droite
j3pCreeSegment(this.svg, {
id: this._getId('droitepar' + par1 + par2),
x1: pt1[0],
y1: pt1[1],
x2: pt2[0],
y2: pt2[1],
couleur: style.couleur,
epaisseur: style.epaisseur
})
} // droiteparallele
Repere.prototype.droiteperpendiculaire = function (num, pt1, par1, par2, style) {
const o1 = this.getObjet(par1)
if (typeof style.visible !== 'boolean') style.visible = true
const id = this._getId('droite' + this._idCleaner(par1 + par2))
// coordonnées de M
let px1, py1
if (o1.type === 'pointsur') {
px1 = o1.x
py1 = o1.y
} else {
px1 = o1.par1
py1 = o1.par2
}
const o2 = this.getObjet(par2) // la droite (d)
let cd = coefdir(this, o2.par1, o2.par2)
let oo, absmin, absmax, yy1, yy2
if (cd === Number.POSITIVE_INFINITY) {
cd = 0
oo = py1 - cd * px1
this.objets[num].cd = cd
absmin = -(this.larg / this.pixelspargraduationX)
absmax = (this.larg / this.pixelspargraduationX)
yy1 = cd * absmin + oo
yy2 = cd * absmax + oo
const [x1, y1] = this.RenvoiePixels(absmin, yy1)
const [x2, y2] = this.RenvoiePixels(absmax, yy2)
if (style.visible) {
j3pCreeSegment(this.svg, {
id,
x1,
y1,
x2,
y2,
couleur: style.couleur,
epaisseur: style.epaisseur
})
}
} else {
if (!cd) {
const coefY = (this.haut / this.pixelspargraduationY)
const [x1, y1] = this.RenvoiePixels(px1, -2 * coefY)
const [x2, y2] = this.RenvoiePixels(px1, 2 * coefY)
this.objets[num].cd = Number.POSITIVE_INFINITY
if (style.visible) {
j3pCreeSegment(this.svg, {
id,
x1,
y1,
x2,
y2,
couleur: style.couleur,
epaisseur: style.epaisseur
})
}
} else {
cd = -1 / cd
this.objets[num].cd = cd
oo = py1 - cd * px1
absmin = -(this.larg / this.pixelspargraduationX)
absmax = (this.larg / this.pixelspargraduationX)
yy1 = cd * absmin + oo
yy2 = cd * absmax + oo
const [x1, y1] = this.RenvoiePixels(absmin, yy1)
const [x2, y2] = this.RenvoiePixels(absmax, yy2)
if (style.visible) {
j3pCreeSegment(this.svg, {
id,
x1,
y1,
x2,
y2,
couleur: style.couleur,
epaisseur: style.epaisseur
})
}
}
const eq1 = this.equation(par2)
const eq2 = [-eq1[1], eq1[0], -eq1[1] * px1 + eq1[0] * py1]
const intersection = j3pIntersection(eq1, eq2)
const distInterPx = Math.sqrt(Math.pow(px1 - intersection[0], 2) + Math.pow(py1 - intersection[1], 2))
// Il faut faire attention si (px1, py1) et intersection ne sont pas confondus
// Si c’est le cas, j’utilise le vecteur (eq1, eq2) directeur de par2 (ou normal, je n’en sais rien)
const normal = (distInterPx < Math.pow(10, -12))
? normalise([px1 + eq1[0], py1 + eq1[1]]) // ça marche aussi avec normalise([px1 - eq1[1], py1 + eq1[0]]) semble-t-il... Jene comprends pas pourquoi
: normalise([px1 - intersection[0], py1 - intersection[1]])
// 1 pour 30 pixels
// 1/3 pour 10 pixels
const rapport = 10 / this.pixelspargraduationX
normal[0] = rapport * normal[0]
normal[1] = rapport * normal[1]
px1 = intersection[0] + normal[0]
py1 = intersection[1] + normal[1]
const px3 = -py1 + intersection[1] + intersection[0]
const py3 = px1 + intersection[1] - intersection[0]
const anglept1 = this.RenvoiePixels(px1, py1)
const anglept2 = this.RenvoiePixels(intersection[0], intersection[1])
const anglept3 = this.RenvoiePixels(px3, py3)
j3pCreeAngle(this.svg, {
id: this._getId('angle' + 'droite' + par1 + par2),
tab1: anglept1,
tab2: anglept2,
tab3: anglept3,
epaisseur: style.epaisseur,
couleur: style.couleur,
opacite: 0.2
})
}
}
/**
* Retourne l’objet de repere.objets dont le nom correspond
* @param {string} nom
* @return {Object}
*/
Repere.prototype.getObjet = function getObjet (nom) {
const obj = this.objetsByNom.get(nom)
if (!obj) console.error(Error(`pas trouvé l’objet ${nom} dans le repere`), this.objets)
return obj
}
export default Repere