Me suena raro eso de que te den: "una semana para ponerte al día con el lenguaje".
Al grano... de entrada si se busca eficiencia, python al ser un lenguaje interpretado, no va a ir sobrado de eficiencia precisamente. Un lenguaje interpretado es entre 4 y 30 veces más lento que el mismo compilado (hoy día décadas atrás la diferencia podría ser entre 10 y 100 veces).
Por otro lado como ya te dije en otro mensaje, lo ideal es partir de datos binarios no de datos textuales... los archivos de geometría van bien cuando lo que se trata es de una pequeña figura (imagina por ejemplo una figura de ajedrez con unos pocos cientos de vértices).
En un principio, se dió a entender que podría tratarse de un ejercicio escolar, y que por tanto no cabe cambiar los datos de entrada... Si finalmente es un trabajo, ya no hay ninguna razón por la que los datos deban ser mantenidos en texto... será más eficiente al guardar y al leer.
En tu caso son puntos, lo que tienes que dibujar, supongamos un caso similar... un objeto 3d... lo inicial es diseñar la estructura del fichero, y lo adecuado es que así lo expongas a tus fejes y compañeros: los actuales deberán ser convertidos a binario y el programa que genere os datos deberia reformarse para adaptar su salida a esto...
El cambio será sencillo, ya que tú no deberías cambiar el orden de los datos, tan solo los tipos de los mismos...
Nota que a falt a de los detalles de tu fichero de geometría, yo parto de lo típico preciso para un fichero que describe la geometría de un objeto 3d, que se pretende graficar alambrado.
estructura Punto3D
float X
float Y
float Z
// entero Valor
fin estructura
estructura Punto2D
entero X
entero Y
// entero Color
fin estructura
estructura Poligono
entero NumVertices
lista de enteros listavertices[]
fin estructura
// estructura del fichero de datos de Geometría
entero NumVertices, NumPoligonos, NumAristas, NumVerticesPoligono
por cada vertice de los NumVertices:
float x, y, z
por cada polígono de los Numpoligonos
entero numVerticesPol // si NumVerticesPoligono vale 0, este valor no aparece, es fijo para cada poligono. numVerticesPol = NumVerticesPoligono
por cada vertice de los numVerticesPol
entero vertice
// Declaraciones
entero NumVertices // cantidad de vertices que tiene la figura 3d
entero NumPoligonos // " polígonos "
entero NumAristas // " aristas "
entero NumVerticesPoligono // " de vértices que tienen todos los polígonos
array de punto3d vertices[] // (x,y,z)
array de poligono poligonos[]
array de punto2d mapa[] // el cálculo de la proyección de los vértices, se guarda en este array que luego será el que se dibuje.
// 1- Las cantidades que define lo que vendrá debajo de cada 'cosa'.
Si abrirfichero(ruta) = TRUE
leer cabecera (numVertices, numPoligonos, numAristas, NumVerticesPoligono)
leer el resto del fichero
graficar
si no
error al tratar de abrir el fichero...
fin si
Tomemos como ejemplo un cubo, que es fácil de imaginar y si falla la imaginación puede uno tomar una caja y 'contar' directamente.
Tiene 8 vertices, que forman 6 caras (polígonos) y 12 aristas. Estos son los datos de partida en el fichero...
NumVerticesPoligono: Señala cuantos vértices tiene cada polígono... esto puede simplificar un fichero cuando todos los polígonos tiene la misma cantidad de vertices, si cada polígono tiene una cantidad distinta este valor debe valer 0.
Seguimos: Debajo de las cantidades que son la cabecera del fichero, aparece la lista de vértices. Como ya sabemos cuantos son podemos crear el array del tamaño preciso. Y luego procede leerlos.
funcion leer resto del fichero
entero k,n
// leer los datos de los vértices:
memoria para Array de punto3d Vertices[0 a NumVertices-1] //alojar espacio para el array 3d
memoria para Array de punto2d Mapa[0 a NumVertices-1] //alojar espacio para el array 2d
bucle para k desde 1 a NumVertices
Vertices[k] = LeerSiguienteVertice // el punto3d X,Y,Z
siguiente
// leer los datos de los poligonos:
memoria para array de poligono Poligonos[0 a numPoligonos -1] //alojar espacio para el array
Si (NumVerticesPoligono = 0)
// cada políigono tiene una cantidad distinta de vertices, dicho dato aparece en el fichero.
bucle para k desde 1 a numPoligonos
n = leer entero
Poligonos[k].numVertices = n
memoria para Poligonos[k].listaVertices[0 a n-1]
bucle para j desde 0 a n-1
Poligonos[k].listaVertices[j] = leer entero // el entero leído es el índice del vértice (del array vértices)
siguiente
siguiente
si no
// el fichero omite la cantidad de vertices de cada polígono, porque el valor es el mismo para cada uno.
bucle para k desde 1 a numPoligonos
Poligonos[k].numVertices = NumVerticesPoligono
memoria para Poligonos[k].listaVertices[0 a NumVerticesPoligono-1]
bucle para j desde 0 a n-1
Poligonos[k].listaVertices[j] = leer entero // el entero leído es el índice del vértice (del array vértices)
siguiente
siguiente
fin si
fin funcion
Punto3D = funcion LeerSiguienteVertice
punto3d p
leer p.x
leer p.y
leer p.z
devlver p
fin funcion
En esta función se lee el resto dle fichero, puede notarse que la cabecera incluye un dato para determinar si todos los polígonos tienenel mismo número de vértices o cada uno tiene su valor.
Por ejemplo para el caso de un cubo, cada polígono está formado por exactamente 4 vértices, luego la lista de vertices que forman cada polígono, son 4... en el ejemplo de una figura como la mencionada más arriba (una figura de ajedrez), cada polígono tendrá un número distinto de vertices, esto se indica marcando en la cabecera un valor 0, e implica qwue en el fichero precediendo a la lista de vértices de cada políugono hay un entero que indica cuantos vertices componen ese polígono.
Por su parte la lista de vértices, lo que tiene es el índice del vértice del array vertices[], luego si un polígono dice tener:
4 114 83 97 140
Vendría a decir que ese polígono tiene 4 vertices y los forman los vértices: vertices[113], vertices[83], vertices[97] y vertices[140], luego el poligono supongamos que fuera el 520, en memoria sería:
poligonos[520].NumVertices = 4
poligonos[520].listaVertices[0] = 114
poligonos[520].listaVertices[1] = 83
poligonos[520].listaVertices[2] = 97
poligonos[520].listaVertices[3] = 140
Creo que está claro, no?...
Solo queda graficar.
En el caso de una figura 3D que no solo se quiere dibujar puntos, sino líneas, es solo un oco más de trabajo, porque hay que dibujar una línea entre cada par de puntos de un polígono, para cada polígono. En el ejemplo previo: del vértice 114, se dibujaria una línea hasta el 83, dle 83 hasta el 97, del 97 hasta el 140 y desde el 140 al primero, al 114.
En tu caso es más sencillo, no tendrás ni polígonos, aunque tal vez tengas que asignar colores a cada punto basado en un valor que se adjunta con las cordenadas x,y,z,valor.
Como debes o deberías saber, al graficar debes calcular para vertice 3d, la proyección 2d, lo cual requiere señalar el ángulo de visión, la escala, desplazamiento y rotación desde el que se ha de ver, que se calcula con multiplicaciones de matrices:
funcion Graficar
CalcularMatrices // Calcula la matriz resultado que se ubicará en la matriz 'Tem[]
CalcularProyeccion // calcula las cordenadas 2D donde se proyectará el array de vértices 3D, dada la matriz Temp[]
CalcularGrafico // borra el gráfico previo y calcula el nuevo en un backbuffer
Dibujargrafico // vuelca el backbuffer a la ventana de la pantalla.
fin funcion
funcion CalcularMatrices
MatrizBaseSetZero // Asignar los valores por defecto de la matriz base.
// Temp[] y Tmp2[] son las matrices que recoge el resultado temporal del cálculo, se va borrando su valor previo a su asignación.
// RotX[], RotY[] y RotZ[] son las matrices que recoge los valores de rotacion en el eje X,Y y Z de la 'cámara'.
// Escala[] y Desplazamiento[] son las matrices que recogen la escala en los ejes x,y,z y el desplazmaiento en los mismos ejes (a falta de datos, será el origen de cordenadas).
MultiplicarMatrices(RotX[], Base[], Temp[])
MultiplicarMatrices(RotY[], Temp(), Tmp2[])
MultiplicarMatrices(RotZ[], Tmp2[], Temp[])
MultiplicarMatrices(Escala[], Temp[], Tmp2[])
MultiplicarMatrices(Deplazamiento[], Tmp2[], Temp[]) // el array Temp contiene los valores de salida que se usarán en el cálculo de la proyección.
fin funcion
Lógicamente para que funciones bien, cada vez que se cambie la vista de la cámara, la escala o el desplazamiento, debe actualizarse la matriz correspondiente, e invocar la función graficar, para que actualice la vista.
En tu caso posiblemente la proyección exija un array de una estructura que no es emramente un Punto2D... pues como dije más arriba, seguramente tus puntos incluyen también un valor.
estructura punto3DEspecial
float X
float Y
float Z
float Valor
fin estructura
Lo que precisará un punto 2D también espacial... es probable que el valor deba ser transformado en un color...
estructura Punto2DEspecial
entero X
entero Y
entero Color
La proyección calculará para cada vértice 3D, el destino que ocupara en el espacio 2D, previamente definido. Al mismo tiempo para cada vértice si hay un dato asociado (como el sugerido valor), puede al mismo tiempo calcular el color que le corresponde...
La función calcularGrafico, borra el backbufer previo o lo elimina y genera uno nuevo y transfiere el array 2D al mismo.
La función DibujarGrafico, vuelca el backbuffer a la pantalla. Si se borra la pantalla entera o parcialmente, esta misma función puede usarse para redibujarlo entero o parcialmente (puede admitir parámetros para el 'rect' a dibujar o dibujarlo todo siempre).
Que sean 2 milones de puntos, no debe ser motivo para que sea lento.
Leer desde fichero un array de 2 millones de vértices, requiere mucho menos de 1sg. a no ser que se esté leyendo desde una unidad que requiere una preparación (esto es un CD, DVD, o una unidad que entra en un estado bajo de energía, etc...).
El cálculo igualmente debiera ser apenas de 1 décima de sg. o menos, transferir y volcar el contenido lo mismo... por lo que la actualización de los datos (por ejemplo si cambia la posición de la cámara, escalas, etc... debiera ser inmediato (menor que un parpadeo), y solo ser un pelín más lento cuando cargas desde fichero, tan solo por la lectura de los datos que requiere además la creación de los arrays, etc... supongamos que en ese caso 1sg. si la unidad está lista.
Si el fichero es mucho más grande, puede leerse entero en bytes y luego ir asignando los valores desde memoria... en este caso solo será más rápido si sabes manejarlo, es decir si conoces el lenguaje en cierta profundidad, si no posiblemente andes más liado, de ser el caso, lee el fichero como el pseudocódigo señala.
Una optimización que puede hacerse a cambio de un gasto de memoria algo mayor (en el ejemplo de una figura 3d), sería asumir un valor alto de vértice spor cada polígono, asegurando así que sea cual sea el valor siempre encaja (por ejemplo 20 vértices), esto premite rediseñar la estructura 'Poligono', y con ello el array poligonos, para ser un array de dos dimensiones, de esto:
estructura poligono
entero numVertices
lista de enteros listavertices[]
fin estructura
array de poligono poligonos[]
//...
... a esto:
array de enteros poligonos[]
//...
memoria para poligonos(0 a numPoligonos, 0 a 20)
El índice 0, podría contener el numero de vértices y la lista de vertices comenzar en el indice 1 hasta el 20 (comparar con lo mostrado más arriba)
poligonos[520, 0] = 4 // número de vértices
poligonos[520, 1] = 114
poligonos[520, 2] = 83
poligonos[520, 3] = 97
poligonos[520, 4] = 140
Este cambio permite reservar memoria de una sola vez, 1 solo array en vez de un array donde cada elemento tendrá luego otro mini array.
Lógicamente como tu no necesitas polígonos, para tu trabajo, este cambio no te afecta, pero debe ilustrarte en cuanto a la idea de operar más rápido a cambio de algo más de memoria.
Y sin más datos explicitos, no conviene aventurar supuestas optimizaciones.
Por cierto 2 milones de puntos, no son 'muchos datos'.
p.d.: Una figura de ejemplo... tiene 314 vértices (puntos), 320 polígonos (caras) y 1264 aristas (líneas). Dibujado en malla y luego en puntos.
p.d.:
Ejemplo dle fichero, para el cubo:
8 6 12 0
1 1 1
1 -1 1
1 -1 -1
1 1 -1
-1 1 1
-1 -1 1
-1 -1 -1
-1 1 -1
4 4 8 7 3
4 4 3 2 1
4 4 8 5 1
4 6 2 1 5
4 6 5 8 7
4 6 7 3 2
A pesar de que el cubo tiene 4 vertices por cada polígono, si consigno el valor 0 (final en la primera línea), le estoy indicando en el fichero que cuando se lea, lea el valor del numero de vértices para cada polígono. De haber consignado el valor 4 (en vez de 0), nos habríamos ahorrado poner 4, para cada poligono. Como aquí son solo 6 polígonos, no ahorramos gran cosa.
Lógicamente aunque aquí se ve textual (para poderlo leer), los datos se deben guardar en binario, no hay saltos de línea ni otra separación que el número de bytes que compone cada dato.
La primera línea son datos de tipo entero
Las 8 líneas de los vertices, son de tipo decimal con signo (aunque dado el cubo de lado 1, no reqiere dígitos decimales).
Las 6 lineas de los poligonos son de tipo entero.
Ejemplo para un dodecaedro (ahora en los vértices si se ven los decimales) y tambien se ha hecho constar el númeor de vértices en la primera linea (5):
20 12 60 5
1 -1 -1
1.61783 0.61783 0
1.61783 -0.61783 0
1 1 -1
0.61783 0 -1.61783
-0.61783 0 -1.61783
-1 1 -1
0 1.61783 -0.61783
0 1.61783 0.61783
1 1 1
-1 1 1
-1.61783 0.61783 0
-1.61783 -0.61783 0
-1 -1 -1
0 -1.61783 -0.61783
-0.61783 0 1.61783
-1 -1 1
0.61783 0 1.61783
1 -1 1
0 -1.61783 0.61783
4 5 6 7 8
4 5 1 3 2
2 4 8 9 10
7 8 9 11 12
6 7 12 13 14
1 5 6 14 15
17 16 18 19 20
11 12 13 17 16
13 14 15 20 17
1 3 19 20 15
3 2 10 18 19
10 9 11 16 18