Beispiel #1
0
// Construct full surface representation of data
void Surface::constructFull(PrimitiveList& primitiveList, const Axes& axes, const Array<double>& displayAbscissa, List<DisplayDataSet>& displayData, ColourScale colourScale)
{
	// Forget all data in current primitives
	primitiveList.forgetAll();

	// Get extents of displayData to use based on current axes limits
	Vec3<int> minIndex, maxIndex;
	if (!calculateExtents(axes, displayAbscissa, displayData, minIndex, maxIndex)) return;
	int nZ = (maxIndex.z - minIndex.z) + 1;

	// Copy and transform abscissa values (still in data space) into axes coordinates
	Array<double> x(displayAbscissa, minIndex.x, maxIndex.x);
	axes.transformX(x);
	int nX = x.nItems();

	// Check for specific actions when we have a low number of display datasets
	if (nZ == 1)
	{
		// Special case, if there is exactly one dataset, draw a standard XY line surface instead
		constructLineXY(primitiveList, axes, displayAbscissa, displayData, colourScale);
		return;
	}
	if (nX == 1)
	{
		// Special case, if there is exactly one dataset, draw a standard XY line surface instead
		constructLineZY(primitiveList, axes, displayAbscissa, displayData, colourScale);
		return;
	}

	// Resize primitive list so it's large enough for our needs
	primitiveList.reinitialise(nZ-1, false, GL_TRIANGLES, true);

	// Temporary variables
	Array< Vec3<double> > normA, normB;
	Array<double> yA, yB, yC;
	Array<DisplayDataSet::DataPointType> typeA, typeB, typeC;
	Array< Vec4<GLfloat> > colourA, colourB;
	QColor colour;
	double zA, zB, zC;
	Vec3<double> nrm(0.0, 1.0, 0.0);

	// Construct first slice data and set initial min/max values
	yA.copy(displayData[minIndex.z]->y(), minIndex.x, maxIndex.x);
	typeA.copy(displayData[minIndex.z]->yType(), minIndex.x, maxIndex.x);
	axes.transformY(yA, typeA);
	zA = axes.transformZ(displayData[minIndex.z]->z());
	if ((minIndex.z+1) <= maxIndex.z)	// Safety check - this should always be true because of the checks above
	{
		yB.copy(displayData[minIndex.z+1]->y(), minIndex.x, maxIndex.x);
		typeB.copy(displayData[minIndex.z+1]->yType(), minIndex.x, maxIndex.x);
		axes.transformY(yB, typeB);
		zB = axes.transformZ(displayData[minIndex.z+1]->z());
	}
	constructSurfaceStrip(x, yA, zA, axes, normA, colourA, colourScale, yC, 0.0, yB, zB);

	// Create triangles in strips between the previous and target Y/Z values
	int nBit, nPlusOneBit, totalBit;
	int vertexAn = -1, vertexBn = -1, vertexAnPlusOne = -1, vertexBnPlusOne = -1;
	Primitive* currentPrimitive = primitiveList[0];
	for (int index = minIndex.z+1; index <=maxIndex.z; ++index)
	{
		// Grab next data (if we are not at the end of the index range)
		if (index < maxIndex.z)
		{
			yC.copy(displayData[index+1]->y(), minIndex.x, maxIndex.x);
			typeC.copy(displayData[index+1]->yType(), minIndex.x, maxIndex.x);
			axes.transformY(yC, typeC);
			zC = axes.transformZ(displayData[index+1]->z());
		}
		else yC.clear();

		// Construct data for current slice
		constructSurfaceStrip(x, yB, zB, axes, normB, colourB, colourScale, yA, zA, yC, zC);

		// Use a simple bit to quickly determine which triangles to draw, given possible lack of datapoints in slices
		//
		//			n	n+1	n	n+1
		//	Slice A		4-------1	4-------1	0 = Both	5 = None
		//			| ....TR|	|TL.... |	1 = BL		6 = None
		//			|   ....|	|....   |	2 = TL		7 = None
		//			|BL   ..|	|..   BR|	3 = None	8 = TR
		//	Slice B		8-------2	8-------2	4 = BR		9+= None

		// Set initial bit, and generate initial vertices
		nBit = 0;
		if (typeA.value(0) == DisplayDataSet::NoPoint)
		{
			nBit += 4;
			vertexAn = -1;
		}
		else vertexAn = currentPrimitive->defineVertex(x.value(0), yA.value(0), zA, normA[0], colourA[0]);
		if (typeB.value(0) == DisplayDataSet::NoPoint)
		{
			nBit += 8;
			vertexBn = -1;
		}
		else vertexBn = currentPrimitive->defineVertex(x.value(0), yB.value(0), zB, normB[0], colourB[0]);

		for (int n=0; n<nX-1; ++n)
		{
			// Construct bit for n+1
			nPlusOneBit = 0;
			if (typeA.value(n+1) == DisplayDataSet::NoPoint) nPlusOneBit += 1;
			if (typeB.value(n+1) == DisplayDataSet::NoPoint) nPlusOneBit += 2;
			totalBit = nBit + nPlusOneBit;

			// Reset indices for current (n+1) column
			vertexAnPlusOne = -1;
			vertexBnPlusOne = -1;

			// Add triangles for this quadrant
			if (totalBit == 0)
			{
				// Draw both
				if (vertexAn == -1) vertexAn = currentPrimitive->defineVertex(x.value(n), yA.value(n), zA, normA[n], colourA[n]);
				if (vertexBn == -1) vertexBn = currentPrimitive->defineVertex(x.value(n), yB.value(n), zB, normB[n], colourB[n]);
				vertexAnPlusOne = currentPrimitive->defineVertex(x.value(n+1), yA.value(n+1), zA, normA[n+1], colourA[n+1]);
				vertexBnPlusOne = currentPrimitive->defineVertex(x.value(n+1), yB.value(n+1), zB, normB[n+1], colourB[n+1]);
				currentPrimitive->defineIndices(vertexAn, vertexAnPlusOne, vertexBnPlusOne);
				currentPrimitive->defineIndices(vertexAn, vertexBn, vertexBnPlusOne);
			}
			else if (totalBit == 1)
			{
				// Bottom left corner only
				if (vertexAn == -1) vertexAn = currentPrimitive->defineVertex(x.value(n), yA.value(n), zA, normA[n], colourA[n]);
				if (vertexBn == -1) vertexBn = currentPrimitive->defineVertex(x.value(n), yB.value(n), zB, normB[n], colourB[n]);
				vertexBnPlusOne = currentPrimitive->defineVertex(x.value(n+1), yB.value(n+1), zB, normB[n+1], colourB[n+1]);
				currentPrimitive->defineIndices(vertexAn, vertexBnPlusOne, vertexBn);
			}
			else if (totalBit == 2)
			{
				// Top left corner only
				if (vertexAn == -1) vertexAn = currentPrimitive->defineVertex(x.value(n), yA.value(n), zA, normA[n], colourA[n]);
				if (vertexBn == -1) vertexBn = currentPrimitive->defineVertex(x.value(n), yB.value(n), zB, normB[n], colourB[n]);
				vertexAnPlusOne = currentPrimitive->defineVertex(x.value(n+1), yA.value(n+1), zA, normA[n+1], colourA[n]);
				currentPrimitive->defineIndices(vertexAn, vertexAnPlusOne, vertexBn);
			}
			else if (totalBit == 4)
			{
				// Bottom right corner only
				if (vertexBn == -1) vertexBn = currentPrimitive->defineVertex(x.value(n), yB.value(n), zB, normB[n], colourB[n]);
				vertexAnPlusOne = currentPrimitive->defineVertex(x.value(n+1), yA.value(n+1), zA, normA[n+1], colourA[n+1]);
				vertexBnPlusOne = currentPrimitive->defineVertex(x.value(n+1), yB.value(n+1), zB, normB[n+1], colourB[n+1]);
				currentPrimitive->defineIndices(vertexAnPlusOne, vertexBnPlusOne, vertexBn);
			}
			else if (totalBit == 8)
			{
				// Top right corner only
				if (vertexAn == -1) vertexAn = currentPrimitive->defineVertex(x.value(n), yA.value(n), zA, normA[n], colourA[n]);
				vertexAnPlusOne = currentPrimitive->defineVertex(x.value(n+1), yA.value(n+1), zA, normA[n+1], colourA[n+1]);
				vertexBnPlusOne = currentPrimitive->defineVertex(x.value(n+1), yB.value(n+1), zB, normB[n+1], colourB[n+1]);
				currentPrimitive->defineIndices(vertexAn, vertexAnPlusOne, vertexBnPlusOne);
			}

			// Store new nBit for next index
			nBit = nPlusOneBit*4;
			vertexAn = vertexAnPlusOne;
			vertexBn = vertexBnPlusOne;
		}

		// Shuffle data backwards...
		yA = yB;
		zA = zB;
		typeA = typeB;
		normA = normB;
		colourA = colourB;
		yB = yC;
		zB = zC;
		typeB = typeC;
		
		// Increment primitive pointer
		currentPrimitive = currentPrimitive->next;
	}
}
Beispiel #2
0
// Construct grid surface representation of data
void Surface::constructGrid(PrimitiveList& primitiveList, const Axes& axes, const Array<double>& displayAbscissa, List<DisplayDataSet>& displayData, ColourScale colourScale)
{
    // Forget all data in current primitives
    primitiveList.forgetAll();

    // Get extents of displayData to use based on current axes limits
    Vec3<int> minIndex, maxIndex;
    if (!calculateExtents(axes, displayAbscissa, displayData, minIndex, maxIndex)) return;
    int nZ = (maxIndex.z - minIndex.z) + 1;

    // Copy and transform abscissa values (still in data space) into axes coordinates
    Array<double> x(displayAbscissa, minIndex.x, maxIndex.x);
    axes.transformX(x);
    int nX = x.nItems();
    if (nX < 2) return;

    // Decide how large to make VertexChunks in Primitives
    // We will consider multiple slices at once in order to take most advantage of the post-transform cache
    // forming (abscissa.nItems()/(cacheSize-1)+1) Primitives (we divide by (cacheSize-1) because we will force an overlap
    // of one gridpoint between adjacent strips).
    const int cacheSize = 12;
    int nPrimitives = x.nItems()/(cacheSize-1) + 1;

    // Get some values from axes so we can calculate colours properly
    bool yLogarithmic = axes.logarithmic(1);
    double yStretch = axes.stretch(1);

    // Reinitialise primitive list
    primitiveList.reinitialise(nPrimitives, true, GL_LINES, true);

    // Temporary variables
    int n, offset = 0, i, nLimit, nMax;
    Vec4<GLfloat> colour;
    Vec3<double> nrm(0.0, 1.0, 0.0);
    Array<double> y;
    Array<DisplayDataSet::DataPointType> yType;

    DisplayDataSet** slices = displayData.array();
    Primitive* currentPrimitive = primitiveList[0];
    int verticesA[cacheSize], verticesB[cacheSize];
    double z;

    // Loop over abscissa indices
    while (offset <= x.nItems())
    {
        // Set nLimit to ensure we don't go beyond the end of the data arrays
        nLimit = std::min(cacheSize, x.nItems()-offset);

        // Loop over remaining displayData
        for (int slice = minIndex.z; slice <= maxIndex.z; ++slice)
        {
            // Grab arrays
            y.copy(slices[slice]->y(), minIndex.x+offset, minIndex.x+offset+nLimit-1);
            yType.copy(slices[slice]->yType(), minIndex.x+offset, minIndex.x+offset+nLimit-1);
            axes.transformY(y, yType);
            z = axes.transformZ(slices[slice]->z());

            // Generate vertices for this row
            for (n=0; n<nLimit; ++n)
            {
                i = offset+n;
                if (yType.value(n) != DisplayDataSet::NoPoint)
                {
                    // A value exists here, so define a vertex
                    colourScale.colour(yLogarithmic ? pow(10.0, y.value(n) / yStretch) : y.value(n) / yStretch, colour);
                    verticesB[n] = currentPrimitive->defineVertex(x.value(i), y.value(n), z, nrm, colour);

                    // If the previous vertex on this row also exists, draw a line here
                    if ((n != 0) && (verticesB[n-1] != -1)) currentPrimitive->defineIndices(verticesB[n-1], verticesB[n]);
                }
                else verticesB[n] = -1;
            }

            // Draw lines across slices (if slice != 0)
            if (slice != 0)
            {
                nMax = (maxIndex.z-slice) > 1 ? nLimit-1 : nLimit;
                for (n=0; n<nMax; ++n)
                {
                    if ((verticesA[n] != -1) && (verticesB[n] != -1)) currentPrimitive->defineIndices(verticesA[n], verticesB[n]);
                }
            }

            // Store current vertices for next pass
            for (n=0; n<cacheSize; ++n) verticesA[n] = verticesB[n];
        }

        // Move to next primitive and increase offset
        currentPrimitive = currentPrimitive->next;
        offset += cacheSize-1;
    }
}