//cette fonction convertie une coordonné virtuel en sa position dans l'image, et sa position virtuel relative à la caméra, en prenant également en considération l'angle verticale actuel de la caméra. private p3d2d convVirtualToUI(double x, double y, double z, int imgWidth, int imgHeight, double coss, double sinn) { double MulFactor = (double)imgHeight / 2d / this.ViewAngle; //il suffit de multiplier un angle/rapport par cette valeur pour connaitre en pixel la distance graphique qu'il représente à l'image //il faut effectuer une rotation sur l'axe x, dans la direction de z+ vers y+, et d'un angle de rotation étant this.camAngle. //la multiplication complexe est utilisé pour effectuer cette rotation. //double zz = (z * coss) - (y * sinn); //double yy = (z * sinn) + (y * coss); double[] rotated = this.RotateOnX(y, z, coss, sinn); double zz = rotated[0]; double yy = rotated[1]; double dist = this.camZ - zz; //il faut calculer la distance z (z relatif à la caméra) entre le point et la caméra, qui a changé à cause de la rotation sur l'axe x double deltaX = x - this.camX; double deltaY = yy; p3d2d rep = new p3d2d(0, 0); rep.uix = (int)(((double)imgWidth / 2d) + (deltaX / dist * MulFactor) + 0.5d); //+0.5d pour l'arrondir correctement rep.uiy = (int)(((double)imgHeight / 2d) - (deltaY / dist * MulFactor) + 0.5d); rep.x = deltaX; rep.y = yy; rep.z = zz; return(rep); }
public void Refresh() { int imgWidth = this.Width; int imgHeight = this.Height; if (imgWidth < 100) { imgWidth = 100; } if (imgHeight < 100) { imgHeight = 100; } Bitmap img = new Bitmap(imgWidth, imgHeight); Graphics g = Graphics.FromImage(img); g.Clear(Color.White); double MulFactor = (double)imgHeight / 2d / this.ViewAngle; //ces 2 valeurs sont utilisées pour effectuer une rotation des coordonnés sur le cylindre double coss = Math.Cos(this.camAngle); double sinn = Math.Sin(this.camAngle); ////dessine le cylindre int uicTopSpace = (int)((this.ViewAngle - (1d / this.camZ)) * MulFactor); //espacement verticale entre le cylindre et les bords de l'image en haut et en bas int uicHeight = imgHeight - uicTopSpace - uicTopSpace; //hauteur graphique du cylindre //position graphique de la gauche du cylindre int uicLeft = (imgWidth / 2) - (int)((this.CylinderBorderWidth + this.camX) / this.camZ * MulFactor); if (uicLeft < 0) { uicLeft = 0; } //position graphique de la droite du cylindre int uicRight = (imgWidth / 2) + (int)((this.VirtualRightEnd - this.camX) / this.camZ * MulFactor); //dessine le corps principale du cylindre g.FillRectangle(this.CylinderBrush, uicLeft, uicTopSpace, uicRight - uicLeft, uicHeight); //dessine le bord gauche s'il le faut if (uicLeft > 0) { //calcule la position graphique du bord gauche, mais la position du cercle qui est la plus proche de la caméra int uicCloseLeft = (imgWidth / 2) - (int)((this.CylinderBorderWidth + this.camX) / (this.camZ - 1d) * MulFactor); g.FillEllipse(this.CylinderBrush, uicCloseLeft, uicTopSpace, 2 * (uicLeft - uicCloseLeft), uicHeight); } //dessine le bord droit s'il le faut if (uicRight < imgWidth) { //calcule la position graphique du bord droit, mais la position du cercle qui est la plus proche de la caméra int uicCloseRight = (imgWidth / 2) + (int)((this.VirtualRightEnd - this.camX) / (this.camZ - 1d) * MulFactor); g.FillEllipse(this.CylinderBrush, 2 * uicRight - uicCloseRight, uicTopSpace, 2 * (uicCloseRight - uicRight), uicHeight); } //si l'élément actuellement sélectionné est dans l'image (ca devrait être presque toujours le cas), cette variable sera true et le p3d2d sera gardé dans l'autre variable bool SelectedElemFound = false; //devient true si l'élément actuel est dans l'image clElement SelectedElem = null; p3d2d SelectedPos = new p3d2d(0, 0); double minimumZ = this.ViewAngle; // coordonné z+ minimum pour qu'un point de la surface du cylindre soit sur la face visible par la caméra ////dessine les élément int index = 0; while (index < this.listElem.Count) { clElement elem = this.listElem[index]; //on doit d'abord obtenir ses coordonnés réel. double elemX = elem.x; double elemY = -Math.Sin(elem.angle); double elemZ = Math.Cos(elem.angle); //maintenant on obtien ses coordonnés relatives à la caméra p3d2d relpos = this.convVirtualToUI(elemX, elemY, elemZ, imgWidth, imgHeight, coss, sinn); double dist = this.camZ - relpos.z; //check s'il sort de l'écran. si c'est le cas, on peut arrêter car tout les suivant seront eux aussi dehors de l'image if (relpos.uix > imgWidth) { break; } //check si l'item est actuellement visible à l'écran en vérifiant si le côté droit du texte est dans l'image int uiWidth = (int)(elem.VirtualTextWidth / dist * MulFactor); if (relpos.uix + uiWidth >= 0) { //si on est sur l'élément sélectionné par l'user, on le garde de côté if (index == this.SelectedIndex) { SelectedElemFound = true; SelectedElem = elem; SelectedPos = relpos; } //on check si l'élément est du côté visible du cylindre if (relpos.z > minimumZ) { //on calcul l'applatissement verticale du text. float TextYScale = (float)(relpos.z); //on calcule le height que la font doit avoir Font elemFont = new Font(this.uiFontName, (float)((double)imgHeight * this.uiCloseFontMulFactor * (this.camZ - 1d) / dist)); //on calcule la taille graphique qu'aura le text SizeF elemTextSize = g.MeasureString(elem.Text, elemFont); //on dessine le text de l'élément, centré verticalement g.TranslateTransform((float)(relpos.uix), (float)(relpos.uiy) - (elemTextSize.Height * TextYScale / 2f)); g.ScaleTransform(1f, TextYScale); g.DrawString(elem.Text, elemFont, Brushes.Black, 0f, 0f); g.ResetTransform(); //g.DrawString(elem.Text, elemFont, Brushes.Black, (float)(relpos.uix), (float)(relpos.uiy) - (elemTextSize.Height / 2f)); elemFont.Dispose(); } } //next iteration index++; } //si l'élément actuellement sélectionné a été rencontré, on dessine la flèche bleu à côté if (SelectedElemFound) { //make sure que l'élément actuel est sur la face visible if (SelectedPos.z > minimumZ) { int arrowHeight = (int)((double)(this.imgArrow.Height) * SelectedPos.z); if (arrowHeight >= this.imgArrow.Height - 5) { arrowHeight = this.imgArrow.Height; } g.DrawImage(this.imgArrow, SelectedPos.uix - 3 - this.imgArrow.Width, SelectedPos.uiy - (arrowHeight / 2), this.imgArrow.Width, arrowHeight); } } ////dessine les composant de l'interface graphique foreach (uiButton b in this.listButton) { Brush BackBrush = Brushes.Silver; if (b.IsMouseLeftDown) { BackBrush = Brushes.White; } g.FillRectangle(BackBrush, b.rec); g.DrawRectangle(Pens.Black, b.rec); //dessine le text du button, au milieux SizeF btnTextSizeF = g.MeasureString(b.Text, this.uiBtnFont); g.DrawString(b.Text, this.uiBtnFont, Brushes.Black, (float)(b.Left + (b.Width / 2)) - (btnTextSizeF.Width / 2f), (float)(b.Top + (b.Height / 2)) - (btnTextSizeF.Height / 2f)); } g.Dispose(); if (this.ImageBox.Image != null) { this.ImageBox.Image.Dispose(); } this.ImageBox.Image = img; this.ImageBox.Refresh(); }