From: <md...@us...> - 2010-09-18 04:29:30
|
Revision: 8710 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8710&view=rev Author: mdehoon Date: 2010-09-18 04:29:24 +0000 (Sat, 18 Sep 2010) Log Message: ----------- Adding gouraud plots to the Mac OS X native backend. Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py trunk/matplotlib/src/_macosx.m Modified: trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2010-09-17 07:04:08 UTC (rev 8709) +++ trunk/matplotlib/lib/matplotlib/backends/backend_macosx.py 2010-09-18 04:29:24 UTC (rev 8710) @@ -15,7 +15,6 @@ from matplotlib.colors import colorConverter - from matplotlib.widgets import SubplotTool import matplotlib @@ -104,6 +103,10 @@ self.gc.set_hatch(None) return self.gc + def draw_gouraud_triangle(self, gc, points, colors, transform): + points = transform.transform(points) + gc.draw_gouraud_triangle(points, colors) + def draw_image(self, gc, x, y, im): im.flipud_out() nrows, ncols, data = im.as_rgba_str() @@ -228,7 +231,7 @@ """ Create a new figure manager instance """ - if not _macosx.get_main_display_id(): + if not _macosx.verify_main_display(): import warnings warnings.warn("Python is not installed as a framework. The MacOSX backend may not work correctly if Python is not installed as a framework. Please see the Python documentation for more information on installing Python as a framework on Mac OS X") FigureClass = kwargs.pop('FigureClass', Figure) Modified: trunk/matplotlib/src/_macosx.m =================================================================== --- trunk/matplotlib/src/_macosx.m 2010-09-17 07:04:08 UTC (rev 8709) +++ trunk/matplotlib/src/_macosx.m 2010-09-18 04:29:24 UTC (rev 8710) @@ -1876,7 +1876,347 @@ return Py_None; } +static int _find_minimum(CGFloat values[3]) +{ + int i = 0; + CGFloat minimum = values[0]; + if (values[1] < minimum) + { + minimum = values[1]; + i = 1; + } + if (values[2] < minimum) + i = 2; + return i; +} + +static int _find_maximum(CGFloat values[3]) +{ + int i = 0; + CGFloat maximum = values[0]; + if (values[1] > maximum) + { + maximum = values[1]; + i = 1; + } + if (values[2] > maximum) + i = 2; + return i; +} + +static void +_rgba_color_evaluator(void* info, const CGFloat input[], CGFloat outputs[]) +{ + const CGFloat c1 = input[0]; + const CGFloat c0 = 1.0 - c1; + CGFloat(* color)[4] = info; + outputs[0] = c0 * color[0][0] + c1 * color[1][0]; + outputs[1] = c0 * color[0][1] + c1 * color[1][1]; + outputs[2] = c0 * color[0][2] + c1 * color[1][2]; + outputs[3] = c0 * color[0][3] + c1 * color[1][3]; +} +static void +_gray_color_evaluator(void* info, const CGFloat input[], CGFloat outputs[]) +{ + const CGFloat c1 = input[0]; + const CGFloat c0 = 1.0 - c1; + CGFloat(* color)[2] = info; + outputs[0] = c0 * color[0][0] + c1 * color[1][0]; + outputs[1] = c0 * color[0][1] + c1 * color[1][1]; +} + +static int +_shade_one_color(CGContextRef cr, CGFloat colors[3], CGPoint points[3], int icolor) +{ + const int imin = _find_minimum(colors); + const int imax = _find_maximum(colors); + + float numerator; + float denominator; + float ac; + float as; + float phi; + float distance; + CGPoint start; + CGPoint end; + static CGFunctionCallbacks callbacks = {0, &_rgba_color_evaluator, free}; + CGFloat domain[2] = {0.0, 1.0}; + CGFloat range[8] = {0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0}; + CGFunctionRef function; + + CGFloat(* rgba)[4] = malloc(2*sizeof(CGFloat[4])); + if (!rgba) return -1; + else { + rgba[0][0] = 0.0; + rgba[0][1] = 0.0; + rgba[0][2] = 0.0; + rgba[0][3] = 1.0; + rgba[1][0] = 0.0; + rgba[1][1] = 0.0; + rgba[1][2] = 0.0; + rgba[1][3] = 1.0; + } + + denominator = (points[1].x-points[0].x)*(points[2].y-points[0].y) + - (points[2].x-points[0].x)*(points[1].y-points[0].y); + numerator = (colors[1]-colors[0])*(points[2].y-points[0].y) + - (colors[2]-colors[0])*(points[1].y-points[0].y); + ac = numerator / denominator; + numerator = (colors[2]-colors[0])*(points[1].x-points[0].x) + - (colors[1]-colors[0])*(points[2].x-points[0].x); + as = numerator / denominator; + phi = atan2(as, ac); + + start.x = points[imin].x; + start.y = points[imin].y; + + rgba[0][icolor] = colors[imin]; + rgba[1][icolor] = colors[imax]; + + distance = (points[imax].x-points[imin].x) * cos(phi) + (points[imax].y-points[imin].y) * sin(phi); + + end.x = start.x + distance * cos(phi); + end.y = start.y + distance * sin(phi); + + function = CGFunctionCreate(rgba, + 1, /* one input (position) */ + domain, + 4, /* rgba output */ + range, + &callbacks); + if (function) + { + CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGShadingRef shading = CGShadingCreateAxial(colorspace, + start, + end, + function, + true, + true); + CGFunctionRelease(function); + if (shading) + { + CGContextDrawShading(cr, shading); + CGShadingRelease(shading); + return 1; + } + } + free(rgba); + return -1; +} + +static CGRect _find_enclosing_rect(CGPoint points[3]) +{ + CGFloat left = points[0].x; + CGFloat right = points[0].x; + CGFloat bottom = points[0].y; + CGFloat top = points[0].y; + if (points[1].x < left) left = points[1].x; + if (points[1].x > right) right = points[1].x; + if (points[2].x < left) left = points[2].x; + if (points[2].x > right) right = points[2].x; + if (points[1].y < bottom) bottom = points[1].y; + if (points[1].y > top) top = points[1].y; + if (points[2].y < bottom) bottom = points[2].y; + if (points[2].y > top) top = points[2].y; + return CGRectMake(left,bottom,right-left,top-bottom); +} + +static int +_shade_alpha(CGContextRef cr, CGFloat alphas[3], CGPoint points[3]) +{ + const int imin = _find_minimum(alphas); + const int imax = _find_maximum(alphas); + + if (alphas[imin]==1.0) return 0; + + CGRect rect = _find_enclosing_rect(points); + const size_t width = (size_t)rect.size.width; + const size_t height = (size_t)rect.size.height; + if (width==0 || height==0) return 0; + + void* data = malloc(width*height); + + CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray(); + CGContextRef bitmap = CGBitmapContextCreate(data, + width, + height, + 8, + width, + colorspace, + 0); + CGColorSpaceRelease(colorspace); + + if (imin==imax) + { + CGRect bitmap_rect = rect; + bitmap_rect.origin = CGPointZero; + CGContextSetGrayFillColor(bitmap, alphas[0], 1.0); + CGContextFillRect(bitmap, bitmap_rect); + } + else + { + float numerator; + float denominator; + float ac; + float as; + float phi; + float distance; + CGPoint start; + CGPoint end; + CGFloat(*gray)[2] = malloc(2*sizeof(CGFloat[2])); + + static CGFunctionCallbacks callbacks = {0, &_gray_color_evaluator, free}; + CGFloat domain[2] = {0.0, 1.0}; + CGFloat range[2] = {0.0, 1.0}; + CGShadingRef shading = NULL; + CGFunctionRef function; + + gray[0][1] = 1.0; + gray[1][1] = 1.0; + + denominator = (points[1].x-points[0].x)*(points[2].y-points[0].y) + - (points[2].x-points[0].x)*(points[1].y-points[0].y); + numerator = (alphas[1]-alphas[0])*(points[2].y-points[0].y) + - (alphas[2]-alphas[0])*(points[1].y-points[0].y); + ac = numerator / denominator; + numerator = (alphas[2]-alphas[0])*(points[1].x-points[0].x) + - (alphas[1]-alphas[0])*(points[2].x-points[0].x); + as = numerator / denominator; + phi = atan2(as, ac); + + start.x = points[imin].x - rect.origin.x; + start.y = points[imin].y - rect.origin.y; + + gray[0][0] = alphas[imin]; + gray[1][0] = alphas[imax]; + + distance = (points[imax].x-points[imin].x) * cos(phi) + (points[imax].y-points[imin].y) * sin(phi); + + end.x = start.x + distance * cos(phi); + end.y = start.y + distance * sin(phi); + + function = CGFunctionCreate(gray, + 1, /* one input (position) */ + domain, + 1, /* one output (gray level) */ + range, + &callbacks); + if (function) + { + shading = CGShadingCreateAxial(colorspace, + start, + end, + function, + true, + true); + CGFunctionRelease(function); + } + if (shading) + { + CGContextDrawShading(bitmap, shading); + CGShadingRelease(shading); + } + else + { + free(gray); + } + } + + CGImageRef mask = CGBitmapContextCreateImage(bitmap); + CGContextClipToMask(cr, rect, mask); + CGImageRelease(mask); + free(data); + return 0; +} + +static PyObject* +GraphicsContext_draw_gouraud_triangle (GraphicsContext* self, PyObject* args) + +{ + PyObject* coordinates; + PyObject* colors; + + CGPoint points[3]; + CGFloat intensity[3]; + + int i = 0; + + CGContextRef cr = self->cr; + if (!cr) + { + PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); + return NULL; + } + + if(!PyArg_ParseTuple(args, "OO", &coordinates, &colors)) return NULL; + + /* ------------------- Check coordinates array ------------------------ */ + + coordinates = PyArray_FromObject(coordinates, NPY_DOUBLE, 2, 2); + if (!coordinates || + PyArray_DIM(coordinates, 0) != 3 || PyArray_DIM(coordinates, 1) != 2) + { + PyErr_SetString(PyExc_ValueError, "Invalid coordinates array"); + Py_XDECREF(coordinates); + return NULL; + } + points[0].x = *((double*)(PyArray_GETPTR2(coordinates, 0, 0))); + points[0].y = *((double*)(PyArray_GETPTR2(coordinates, 0, 1))); + points[1].x = *((double*)(PyArray_GETPTR2(coordinates, 1, 0))); + points[1].y = *((double*)(PyArray_GETPTR2(coordinates, 1, 1))); + points[2].x = *((double*)(PyArray_GETPTR2(coordinates, 2, 0))); + points[2].y = *((double*)(PyArray_GETPTR2(coordinates, 2, 1))); + + /* ------------------- Check colors array ----------------------------- */ + + colors = PyArray_FromObject(colors, NPY_DOUBLE, 2, 2); + if (!colors || + PyArray_DIM(colors, 0) != 3 || PyArray_DIM(colors, 1) != 4) + { + PyErr_SetString(PyExc_ValueError, "colors must by a 3x4 array"); + Py_DECREF(coordinates); + Py_XDECREF(colors); + return NULL; + } + + /* ----- Draw the gradients separately for each color component ------- */ + CGContextSaveGState(cr); + CGContextMoveToPoint(cr, points[0].x, points[0].y); + CGContextAddLineToPoint(cr, points[1].x, points[1].y); + CGContextAddLineToPoint(cr, points[2].x, points[2].y); + CGContextClip(cr); + intensity[0] = *((double*)(PyArray_GETPTR2(colors, 0, 3))); + intensity[1] = *((double*)(PyArray_GETPTR2(colors, 1, 3))); + intensity[2] = *((double*)(PyArray_GETPTR2(colors, 2, 3))); + if (_shade_alpha(cr, intensity, points)!=-1) { + CGContextBeginTransparencyLayer(cr, NULL); + CGContextSetBlendMode(cr, kCGBlendModeScreen); + for (i = 0; i < 3; i++) + { + intensity[0] = *((double*)(PyArray_GETPTR2(colors, 0, i))); + intensity[1] = *((double*)(PyArray_GETPTR2(colors, 1, i))); + intensity[2] = *((double*)(PyArray_GETPTR2(colors, 2, i))); + if (!_shade_one_color(cr, intensity, points, i)) break; + } + CGContextEndTransparencyLayer(cr); + } + CGContextRestoreGState(cr); + + Py_DECREF(coordinates); + Py_DECREF(colors); + + if (i < 3) /* break encountered */ + { + PyErr_SetString(PyExc_MemoryError, "insufficient memory in draw_gouraud_triangle"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + #ifdef COMPILING_FOR_10_5 static CTFontRef #else @@ -2791,6 +3131,11 @@ METH_VARARGS, "Draws a mesh in the graphics context." }, + {"draw_gouraud_triangle", + (PyCFunction)GraphicsContext_draw_gouraud_triangle, + METH_VARARGS, + "Draws a Gouraud-shaded triangle in the graphics context." + }, {"draw_text", (PyCFunction)GraphicsContext_draw_text, METH_VARARGS, @@ -5139,14 +5484,15 @@ } static PyObject* -get_main_display_id(PyObject* self) +verify_main_display(PyObject* self) { CGDirectDisplayID display = CGMainDisplayID(); if (display == 0) { PyErr_SetString(PyExc_RuntimeError, "Failed to obtain the display ID of the main display"); return NULL; } - return PyInt_FromLong(display); + Py_INCREF(Py_True); + return Py_True; } static struct PyMethodDef methods[] = { @@ -5165,10 +5511,10 @@ METH_VARARGS, "Sets the active cursor." }, - {"get_main_display_id", - (PyCFunction)get_main_display_id, + {"verify_main_display", + (PyCFunction)verify_main_display, METH_NOARGS, - "Returns the display ID of the main display. This function fails if Python is not built as a framework." + "Verifies if the main display can be found. This function fails if Python is not built as a framework." }, {NULL, NULL, 0, NULL}/* sentinel */ }; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |