web-dev-qa-db-fra.com

Production mathématique d'une grille hexagonale en forme de sphère

J'essaie de créer une forme similaire à celle-ci, des hexagones avec 12 pentagones, à une taille arbitraire.

https://i.stack.imgur.com/F35J0.png

( Source de l'image )

La seule chose est que je n'ai absolument aucune idée du type de code qui serait nécessaire pour le générer!

Le but est de pouvoir prendre un point dans l'espace 3D et le convertir en coordonnées de position sur la grille, ou vice versa et prendre une position de grille et obtenir les sommets appropriés pour dessiner le maillage.

Je ne sais même pas comment on pourrait stocker les positions de la grille pour cela. Est-ce que chaque "section triagle" entre 3 pentagones a son propre ensemble de coordonnées 2D?

J'utiliserai très probablement C # pour cela, mais je suis plus intéressé par les algorithmes à utiliser pour cela et une explication de leur fonctionnement, plutôt que par quelqu'un qui me donne juste un morceau de code.

11
Aaron Franke

D'abord une analyse de l'image dans la question: le triangle sphérique enjambé par les centres voisins du pentagone semble être équilatéral. Lorsque cinq triangles équilatéraux se rencontrent dans un coin et couvrent toute la sphère, cela ne peut être que la configuration induite par un icosaèdre . Il y a donc 12 pentagones et 20 patchs d'une découpe triangulaire d'un maillage hexongal mappé à la sphère.

C'est donc une façon de construire une telle grille hexagonale sur la sphère:

  1. Créer une découpe triangulaire de la grille hexagonale: un triangle fixe (j'ai choisi (-0,5,0), (0,5,0), (0, sqrt (3)/2)) se superpose à une grille hexagonale avec la résolution souhaitée n st les coins du triangle coïncident avec les centres des hexagones, voir les exemples pour n = 0,1,2,20: enter image description here

  2. Calculez les coins de icosaèdre et définissez ses 20 faces triangulaires (voir code ci-dessous). Les coins de l'icosaèdre définissent les centres des pentagones, les faces de l'icosaèdre définissent les plaques des grilles hexagonales cartographiées. (L'icosaèdre donne la plus fine division régulière de la surface de la sphère en triangles, c'est-à-dire une division en triangles équilatéraux congrus. D'autres divisions de ce type peuvent être dérivées d'un tétraèdre ou un octaèdre; puis, aux coins des triangles, il y aura des triangles ou des carrés, respectivement. De plus, les triangles plus petits et plus grands rendraient la distorsion inévitable dans toute cartographie d'un maillage plan sur une surface courbe plus visible. Donc, choisir l'icosaèdre comme une base pour les patchs triangulaires aide à minimiser la distorsion des hexagones.)

  3. Mappez la découpe triangulaire de la grille hexagonale aux triangles sphériques correspondant aux faces de l'icosaeder: un double - slerp basé sur coordonnées barycentriques fait l'affaire. Ci-dessous est une illustration de la cartographie d'une découpe triangulaire d'une grille hexagonale avec une résolution n = 10 sur un triangle sphérique (défini par une face d'un icosaeder), et une illustration de la cartographie de la grille sur tous ces triangles sphériques couvrant toute la sphère (différentes couleurs pour différents mappages):

enter image description hereenter image description here

Voici Python code pour générer les coins (coordonnées) et les triangles (indices ponctuels) d'un icosaèdre:

from math import sin,cos,acos,sqrt,pi
s,c = 2/sqrt(5),1/sqrt(5)
topPoints = [(0,0,1)] + [(s*cos(i*2*pi/5.), s*sin(i*2*pi/5.), c) for i in range(5)]
bottomPoints = [(-x,y,-z) for (x,y,z) in topPoints]
icoPoints = topPoints + bottomPoints
icoTriangs = [(0,i+1,(i+1)%5+1) for i in range(5)] +\
             [(6,i+7,(i+1)%5+7) for i in range(5)] +\
             [(i+1,(i+1)%5+1,(7-i)%5+7) for i in range(5)] +\
             [(i+1,(7-i)%5+7,(8-i)%5+7) for i in range(5)]

Et voici le code Python pour mapper (points de) le triangle fixe à un triangle sphérique à l'aide d'un double slerp:

# barycentric coords for triangle (-0.5,0),(0.5,0),(0,sqrt(3)/2)
def barycentricCoords(p):
  x,y = p
  # l3*sqrt(3)/2 = y
  l3 = y*2./sqrt(3.)
  # l1 + l2 + l3 = 1
  # 0.5*(l2 - l1) = x
  l2 = x + 0.5*(1 - l3)
  l1 = 1 - l2 - l3
  return l1,l2,l3

from math import atan2
def scalProd(p1,p2):
  return sum([p1[i]*p2[i] for i in range(len(p1))])
# uniform interpolation of arc defined by p0, p1 (around Origin)
# t=0 -> p0, t=1 -> p1
def slerp(p0,p1,t):
  assert abs(scalProd(p0,p0) - scalProd(p1,p1)) < 1e-7
  ang0Cos = scalProd(p0,p1)/scalProd(p0,p0)
  ang0Sin = sqrt(1 - ang0Cos*ang0Cos)
  ang0 = atan2(ang0Sin,ang0Cos)
  l0 = sin((1-t)*ang0)
  l1 = sin(t    *ang0)
  return Tuple([(l0*p0[i] + l1*p1[i])/ang0Sin for i in range(len(p0))])

# map 2D point p to spherical triangle s1,s2,s3 (3D vectors of equal length)
def mapGridpoint2Sphere(p,s1,s2,s3):
  l1,l2,l3 = barycentricCoords(p)
  if abs(l3-1) < 1e-10: return s3
  l2s = l2/(l1+l2)
  p12 = slerp(s1,s2,l2s)
  return slerp(p12,s3,l3)
9
coproc

La forme que vous avez est l'une des soi-disant "polyèdres de Goldberg", est également un polyèdres géodésiques .

L'algorithme ( plutôt élégant) pour générer ceci (et beaucoup d'autres) peut être codé de manière succincte dans quelque chose appelé Notation de polyèdre Conway .

La construction est facile à suivre étape par étape, vous pouvez cliquer sur les images ci-dessous pour obtenir un aperçu en direct.

  1. Le polyèdre que vous recherchez peut être généré à partir d'un isosaèdre - Initialisez un maillage avec un isosaèdre. isosahedron

  2. Nous appliquons une opération "Tronquer" (notation Conway t) au maillage (la cartographie sphérique de celui-ci est un ballon de football). enter image description here

  3. Nous appliquons l'opérateur "Dual" (notation Conway d). enter image description here

  4. Nous appliquons à nouveau une opération "Tronquer". À ce stade, la recette est tdtI (lire à droite!). Vous pouvez déjà voir où cela va. enter image description here

  5. Appliquez les étapes 3 et 4 à plusieurs reprises jusqu'à ce que vous soyez satisfait.

Par exemple, ci-dessous se trouve le maillage de dtdtdtdtI. enter image description here

C'est assez facile à mettre en œuvre. Je suggérerais d'utiliser une structure de données qui facilite la traversée du voisinage pour donner un sommet, un bord, etc. tels que des infrastructures de données à bord ailé ou demi-bord pour votre maillage. Il vous suffit d'implémenter des opérateurs tronqués et doubles pour la forme que vous recherchez.

9
hkrish

[Réédition complète 18.10.2017]

le stockage de la géométrie est sur vous. Soit vous le stockez dans une sorte de Mesh, soit vous le générez à la volée. Je préfère le stocker. Sous forme de 2 tableaux. Un tenant tous les sommets (pas de doublons) et l'autre tenant 6 index de points utilisés par chaque hex que vous avez obtenu et quelques informations supplémentaires comme la position sphérique pour faciliter le post-traitement.

Maintenant, comment générer cela:

  1. créer un triangle hexagonal

    la taille doit être le rayon de votre sphère. n'incluez pas l'hexagone d'angle et sautez également la dernière ligne du triangle (à la fois radiale et axiale, il y a donc 1 espace hexadécimal entre les triangles voisins sur la sphère) car cela se chevaucherait lors de la jonction de segments de triangle.

  2. convertir le triangle hexagonal 60deg en tarte 72deg

    convertir simplement en coordonnées polaires (radius,angle), centrer le triangle autour de 0 deg. Multipliez ensuite le rayon par cos(angle)/cos(30); qui convertira le triangle en Pie. Et puis redimensionnez l'angle avec le rapport 72/60. Cela rendra notre triangle joignable ...

  3. copier et faire pivoter le triangle pour remplir 5 segments de pentagone

    il suffit de faire pivoter les points du premier triangle et de les stocker comme neufs.

  4. calculer z

    sur la base de cette travail hexagonal de l'hémisphère vous pouvez convertir la distance dans la carte 2D en longueur d'arc pour limiter les distorsions comme autant que possible.

    Cependant, quand je l'ai essayé (exemple ci-dessous), les hexagones sont un peu déformés, donc la profondeur et la mise à l'échelle nécessitent quelques ajustements. Ou post-traitement dernier.

  5. copiez la demi-sphère pour former une sphère

    copiez simplement les points/hexs et annulez l'axe z (ou faites une rotation de 180 degrés si vous souhaitez conserver le bobinage).

  6. ajoutez l'équateur et tous les pentagones et hexagones manquants

    Vous devez utiliser les coordonnées des hexagones voisins afin de ne plus ajouter de distorsion et de chevauchements à la grille. Voici un aperçu:

    img

    Bleu est le triangle de départ. Le bleu foncé sont ses copies. Le rouge sont des pentagones polaires. Le vert foncé est l'équateur, Le vert plus clair sont les lignes de jonction entre les triangles . Dans jaunâtre se trouvent les hexagones d'équateur manquants près de pentagones orange foncé .

Voici un exemple simple OpenGL C++ (fait à partir de la réponse liée dans # 4 ):

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
#include "gl/OpenGL3D_double.cpp"
#include "PolyLine.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
OpenGLscreen scr;
bool _redraw=true;
double animx=  0.0,danimx=0.0;
double animy=  0.0,danimy=0.0;
//---------------------------------------------------------------------------
PointTab     pnt;   // (x,y,z)

struct _hexagon
    {
    int ix[6];      // index of 6 points, last point duplicate for pentagon
    int a,b;        // spherical coordinate
    DWORD col;      // color

    // inline
    _hexagon()      {}
    _hexagon(_hexagon& a)   { *this=a; }
    ~_hexagon() {}
    _hexagon* operator = (const _hexagon *a) { *this=*a; return this; }
    //_hexagon* operator = (const _hexagon &a) { ...copy... return this; }
    };
List<_hexagon> hex;
//---------------------------------------------------------------------------
// https://stackoverflow.com/a/46787885/2521214
//---------------------------------------------------------------------------
void hex_sphere(int N,double R)
    {
    const double c=cos(60.0*deg);
    const double s=sin(60.0*deg);
    const double sy=       R/(N+N-2);
    const double sz=sy/s;
    const double sx=sz*c;
    const double sz2=0.5*sz;

    const int na=5*(N-2);
    const int nb=  N;
    const int b0=  N;
    double *q,p[3],ang,len,l,l0,ll;
    int i,j,n,a,b,ix;
    _hexagon h,*ph;

    hex.allocate(na*nb);
    hex.num=0;
    pnt.reset3D(N*N);
    b=0; a=0; ix=0;

    // generate triangle hex grid
    h.col=0x00804000;
    for (b=1;b<N-1;b++)                             // skip first line b=0
     for (a=1;a<b;a++)                              // skip first and last line
        {
        p[0]=double(a       )*(sx+sz);
        p[1]=double(b-(a>>1))*(sy*2.0);
        p[2]=0.0;
        if (int(a&1)!=0) p[1]-=sy;
        ix=pnt.add(p[0]+sz2+sx,p[1]   ,p[2]); h.ix[0]=ix; //  2 1
        ix=pnt.add(p[0]+sz2   ,p[1]+sy,p[2]); h.ix[1]=ix; // 3   0
        ix=pnt.add(p[0]-sz2   ,p[1]+sy,p[2]); h.ix[2]=ix; //  4 5
        ix=pnt.add(p[0]-sz2-sx,p[1]   ,p[2]); h.ix[3]=ix;
        ix=pnt.add(p[0]-sz2   ,p[1]-sy,p[2]); h.ix[4]=ix;
        ix=pnt.add(p[0]+sz2   ,p[1]-sy,p[2]); h.ix[5]=ix;
        h.a=a;
        h.b=N-1-b;
        hex.add(h);
        } n=hex.num; // remember number of hexs for the first triangle

    // distort points to match area
    for (ix=0;ix<pnt.nn;ix+=3)
        {
        // point pointer
        q=pnt.pnt.dat+ix;
        // convert to polar coordinates
        ang=atan2(q[1],q[0]);
        len=vector_len(q);
        // match area of pentagon (72deg) triangle as we got hexagon (60deg) triangle
        ang-=60.0*deg;  // rotate so center of generated triangle is angle 0deg
        while (ang>+60.0*deg) ang-=pi2;
        while (ang<-60.0*deg) ang+=pi2;
        len*=cos(ang)/cos(30.0*deg);        // scale radius so triangle converts to pie
        ang*=72.0/60.0;                     // scale up angle so rotated triangles merge
        // convert back to cartesian
        q[0]=len*cos(ang);
        q[1]=len*sin(ang);
        }

    // copy and rotate the triangle to cover pentagon
    h.col=0x00404000;
    for (ang=72.0*deg,a=1;a<5;a++,ang+=72.0*deg)
     for (ph=hex.dat,i=0;i<n;i++,ph++)
        {
        for (j=0;j<6;j++)
            {
            vector_copy(p,pnt.pnt.dat+ph->ix[j]);
            rotate2d(-ang,p[0],p[1]);
            h.ix[j]=pnt.add(p[0],p[1],p[2]);
            }
        h.a=ph->a+(a*(N-2));
        h.b=ph->b;
        hex.add(h);
        }

    // compute z
    for (q=pnt.pnt.dat,ix=0;ix<pnt.nn;ix+=pnt.dn,q+=pnt.dn)
        {
        q[2]=0.0;
        ang=vector_len(q)*0.5*pi/R;
        q[2]=R*cos(ang);
        ll=fabs(R*sin(ang)/sqrt((q[0]*q[0])+(q[1]*q[1])));
        q[0]*=ll;
        q[1]*=ll;
        }

    // copy and mirror the other half-sphere
    n=hex.num;
    for (ph=hex.dat,i=0;i<n;i++,ph++)
        {
        for (j=0;j<6;j++)
            {
            vector_copy(p,pnt.pnt.dat+ph->ix[j]);
            p[2]=-p[2];
            h.ix[j]=pnt.add(p[0],p[1],p[2]);
            }
        h.a= ph->a;
        h.b=-ph->b;
        hex.add(h);
        }

    // create index search table
    int i0,i1,j0,j1,a0,a1,ii[5];
    int **ab=new int*[na];
    for (a=0;a<na;a++)
        {
        ab[a]=new int[nb+nb+1];
        for (b=-nb;b<=nb;b++) ab[a][b0+b]=-1;
        }
    n=hex.num;
    for (ph=hex.dat,i=0;i<n;i++,ph++) ab[ph->a][b0+ph->b]=i;

    // add join ring
    h.col=0x00408000;
    for (a=0;a<na;a++)
        {
        h.a=a;
        h.b=0;
        a0=a;
        a1=a+1; if (a1>=na) a1-=na;
        i0=ab[a0][b0+1];
        i1=ab[a1][b0+1];
        j0=ab[a0][b0-1];
        j1=ab[a1][b0-1];
        if ((i0>=0)&&(i1>=0))
         if ((j0>=0)&&(j1>=0))
            {
            h.ix[0]=hex[i1].ix[1];
            h.ix[1]=hex[i0].ix[0];
            h.ix[2]=hex[i0].ix[1];
            h.ix[3]=hex[j0].ix[1];
            h.ix[4]=hex[j0].ix[0];
            h.ix[5]=hex[j1].ix[1];
            hex.add(h);
            ab[h.a][b0+h.b]=hex.num-1;
            }
        }

    // add 2x5 join lines
    h.col=0x00008040;
    for (a=0;a<na;a+=N-2)
     for (b=1;b<N-3;b++)
        {
        // +b hemisphere
        h.a= a;
        h.b=+b;
        a0=a-b; if (a0<  0) a0+=na; i0=ab[a0][b0+b+0];
        a0--;   if (a0<  0) a0+=na; i1=ab[a0][b0+b+1];
        a1=a+1; if (a1>=na) a1-=na; j0=ab[a1][b0+b+0];
                                    j1=ab[a1][b0+b+1];
        if ((i0>=0)&&(i1>=0))
         if ((j0>=0)&&(j1>=0))
            {
            h.ix[0]=hex[i0].ix[5];
            h.ix[1]=hex[i0].ix[4];
            h.ix[2]=hex[i1].ix[5];
            h.ix[3]=hex[j1].ix[3];
            h.ix[4]=hex[j0].ix[4];
            h.ix[5]=hex[j0].ix[3];
            hex.add(h);
            }
        // -b hemisphere
        h.a= a;
        h.b=-b;
        a0=a-b; if (a0<  0) a0+=na; i0=ab[a0][b0-b+0];
        a0--;   if (a0<  0) a0+=na; i1=ab[a0][b0-b-1];
        a1=a+1; if (a1>=na) a1-=na; j0=ab[a1][b0-b+0];
                                    j1=ab[a1][b0-b-1];
        if ((i0>=0)&&(i1>=0))
         if ((j0>=0)&&(j1>=0))
            {
            h.ix[0]=hex[i0].ix[5];
            h.ix[1]=hex[i0].ix[4];
            h.ix[2]=hex[i1].ix[5];
            h.ix[3]=hex[j1].ix[3];
            h.ix[4]=hex[j0].ix[4];
            h.ix[5]=hex[j0].ix[3];
            hex.add(h);
            }
        }

    // add pentagons at poles
    _hexagon h0,h1;
    h0.col=0x00000080;
    h0.a=0; h0.b=N-1; h1=h0; h1.b=-h1.b;
    p[2]=sqrt((R*R)-(sz*sz));
    for (ang=0.0,a=0;a<5;a++,ang+=72.0*deg)
        {
        p[0]=2.0*sz*cos(ang);
        p[1]=2.0*sz*sin(ang);
        h0.ix[a]=pnt.add(p[0],p[1],+p[2]);
        h1.ix[a]=pnt.add(p[0],p[1],-p[2]);
        }
    h0.ix[5]=h0.ix[4]; hex.add(h0);
    h1.ix[5]=h1.ix[4]; hex.add(h1);

    // add 5 missing hexagons at poles
    h.col=0x00600060;
    for (ph=&h0,b=N-3,h.b=N-2,i=0;i<2;i++,b=-b,ph=&h1,h.b=-h.b)
        {
        a =  1; if (a>=na) a-=na; ii[0]=ab[a][b0+b];
        a+=N-2; if (a>=na) a-=na; ii[1]=ab[a][b0+b];
        a+=N-2; if (a>=na) a-=na; ii[2]=ab[a][b0+b];
        a+=N-2; if (a>=na) a-=na; ii[3]=ab[a][b0+b];
        a+=N-2; if (a>=na) a-=na; ii[4]=ab[a][b0+b];
        for (j=0;j<5;j++)
            {
            h.a=((4+j)%5)*(N-2)+1;
            h.ix[0]=ph->ix[ (5-j)%5 ];
            h.ix[1]=ph->ix[ (6-j)%5 ];
            h.ix[2]=hex[ii[(j+4)%5]].ix[4];
            h.ix[3]=hex[ii[(j+4)%5]].ix[5];
            h.ix[4]=hex[ii[ j     ]].ix[3];
            h.ix[5]=hex[ii[ j     ]].ix[4];
            hex.add(h);
            }
        }

    // add 2*5 pentagons and 2*5 missing hexagons at equator
    h0.a=0; h0.b=N-1; h1=h0; h1.b=-h1.b;
    for (ang=36.0*deg,a=0;a<na;a+=N-2,ang-=72.0*deg)
        {
        p[0]=R*cos(ang);
        p[1]=R*sin(ang);
        p[2]=sz;
        i0=pnt.add(p[0],p[1],+p[2]);
        i1=pnt.add(p[0],p[1],-p[2]);
        a0=a-1;if (a0<  0) a0+=na;
        a1=a+1;if (a1>=na) a1-=na;
        ii[0]=ab[a0][b0-1]; ii[2]=ab[a1][b0-1];
        ii[1]=ab[a0][b0+1]; ii[3]=ab[a1][b0+1];
        // hexagons
        h.col=0x00008080;
        h.a=a; h.b=0;
        h.ix[0]=hex[ii[0]].ix[0];
        h.ix[1]=hex[ii[0]].ix[1];
        h.ix[2]=hex[ii[1]].ix[1];
        h.ix[3]=hex[ii[1]].ix[0];
        h.ix[4]=i0;
        h.ix[5]=i1;
        hex.add(h);
        h.a=a; h.b=0;
        h.ix[0]=hex[ii[2]].ix[2];
        h.ix[1]=hex[ii[2]].ix[1];
        h.ix[2]=hex[ii[3]].ix[1];
        h.ix[3]=hex[ii[3]].ix[2];
        h.ix[4]=i0;
        h.ix[5]=i1;
        hex.add(h);
        // pentagons
        h.col=0x000040A0;
        h.a=a; h.b=0;
        h.ix[0]=hex[ii[0]].ix[0];
        h.ix[1]=hex[ii[0]].ix[5];
        h.ix[2]=hex[ii[2]].ix[3];
        h.ix[3]=hex[ii[2]].ix[2];
        h.ix[4]=i1;
        h.ix[5]=i1;
        hex.add(h);
        h.a=a; h.b=0;
        h.ix[0]=hex[ii[1]].ix[0];
        h.ix[1]=hex[ii[1]].ix[5];
        h.ix[2]=hex[ii[3]].ix[3];
        h.ix[3]=hex[ii[3]].ix[2];
        h.ix[4]=i0;
        h.ix[5]=i0;
        hex.add(h);
        }

    // release index search table
    for (a=0;a<na;a++) delete[] ab[a];
    delete[] ab;
    }
//---------------------------------------------------------------------------
void hex_draw(GLuint style)     // draw hex
    {
    int i,j;
    _hexagon *h;
    for (h=hex.dat,i=0;i<hex.num;i++,h++)
        {
        if (style==GL_POLYGON) glColor4ubv((BYTE*)&h->col);
        glBegin(style);
        for (j=0;j<6;j++) glVertex3dv(pnt.pnt.dat+h->ix[j]);
        glEnd();
        }
    if (0)
    if (style==GL_POLYGON)
        {
        scr.text_init_pixel(0.1,-0.2);
        glColor3f(1.0,1.0,1.0);
        for (h=hex.dat,i=0;i<hex.num;i++,h++)
         if (abs(h->b)<2)
            {
            double p[3];
            vector_ld(p,0.0,0.0,0.0);
            for (j=0;j<6;j++)
             vector_add(p,p,pnt.pnt.dat+h->ix[j]);
            vector_mul(p,p,1.0/6.0);
            scr.text(p[0],p[1],p[2],AnsiString().sprintf("%i,%i",h->a,h->b));
            }
        scr.text_exit_pixel();
        }
    }
//---------------------------------------------------------------------------
void TMain::draw()
    {
    scr.cls();
    int x,y;

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0,0.0,-5.0);
    glRotated(animx,1.0,0.0,0.0);
    glRotated(animy,0.0,1.0,0.0);

    hex_draw(GL_POLYGON);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0,0.0,-5.0+0.01);
    glRotated(animx,1.0,0.0,0.0);
    glRotated(animy,0.0,1.0,0.0);

    glColor3f(1.0,1.0,1.0);
    glLineWidth(2);
    hex_draw(GL_LINE_LOOP);
    glCirclexy(0.0,0.0,0.0,1.5);
    glLineWidth(1);

    scr.exe();
    scr.rfs();
    }
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
    {
    scr.init(this);
    hex_sphere(10,1.5);
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
    {
    scr.exit();
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
    {
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
    {
    scr.resize();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60,float(scr.xs)/float(scr.ys),0.1,100.0);
    _redraw=true;
    }
//-----------------------------------------------------------------------
void __fastcall TMain::Timer1Timer(TObject *Sender)
    {
    animx+=danimx; if (animx>=360.0) animx-=360.0; _redraw=true;
    animy+=danimy; if (animy>=360.0) animy-=360.0; _redraw=true;
    if (_redraw) { draw(); _redraw=false; }
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormKeyDown(TObject *Sender, Word &Key, TShiftState Shift)
    {
    Caption=Key;
    if (Key==40){ animx+=2.0; _redraw=true; }
    if (Key==38){ animx-=2.0; _redraw=true; }
    if (Key==39){ animy+=2.0; _redraw=true; }
    if (Key==37){ animy-=2.0; _redraw=true; }
    }
//---------------------------------------------------------------------------

Je sais que c'est un peu un gâchis d'index et que la règle d'enroulement n'est pas garantie car j'étais trop paresseux pour faire une indexation uniforme. Attention, les index a de chaque hex ne sont pas linéaires et si vous voulez les utiliser pour mapper sur 2D la carte, vous devrez recalculer en utilisant atan2 sur x,y de sa position centrale.

Voici les aperçus:

previewpreview

Certaines distorsions sont toujours présentes. Ils sont causés par le fait que nous utilisons 5 triangles pour nous connecter à l'équateur (donc la connexion est garantie). Cela signifie que la circonférence est 5*R Au lieu de 6.28*R. Cependant, cela peut encore être amélioré par une simulation sur le terrain. Prenez simplement tous les points et ajoutez des forces de rétraction en fonction de leur distance et liées à la surface de la sphère. Exécutez la simulation et lorsque les oscillations descendent sous le seuil, vous obtenez votre grille de sphères ...

Une autre option serait de trouver une équation pour remapper les points de la grille (de la même manière que j'ai fait pour la conversion triangle en tarte) qui aurait de meilleurs résultats.

6
Spektre