/******************************************************************************* * * * libbrewscheme * * * * A collection of Mata classes, methods, functions, and wrappers used by * * programs in the brewscheme package. This includes classes/methods used to * * provide simulated RGB values for different forms of color blindness, and * * utility programs used to search for/look up RGB values used in the different * * programs in the package/toolkit/suite of programs. * * * * Code adopted from: * * * * Wickline, M. (2014). Color.Vision.Simulate. version 0.1. Retrieved from: * * galacticmilk.com/labs/Color-Vision/Javascript/Color.Vision.Simulate.js * * Retrieved on: 24nov2015 * * * * Code also references work of: * * * * Meyer, G. W., & Greenberg, D. P. (1988). Color-Defective Vision and Computer * * Graphics Displays. Computer Graphics and Applications, IEEE 8(5), pp. 28-40. * * * * Smith, V. C., & Pokorny, J. (2005). Spectral sensitivity of the foveal cone * * photopigments between 400 and 500nm. Journal of the Optical Society of * * America A, 22(10) pp. 2060-2071. * * * * Lindbloom, B. (). RGB working space information. Retrieved from: * * http://www.brucelindbloom.com. Retrieved on 24nov2015. * * * *******************************************************************************/ // Change to Mata interpreter/compiler mata: // Define a generic class to store color blindness constants and methods class cbtype { // Private members private: // Constants used to rescale RGB values for different forms of colorblindness real scalar x, y, m, yint // Publicly accessible members public: // Accessor methods for the constants real scalar getConfuseX(), getConfuseY(), getConfuseM(), getConfuseYint() // Setter methods for the constants void setConfuseX(), setConfuseY(), setConfuseM(), setConfuseYint() } // End of base class definition // Accessor for the x member variable real scalar cbtype::getConfuseX() { // Returns the x member variable value return(this.x) } // End of Accessor method definition // Accessor for the y member variable real scalar cbtype::getConfuseY() { // Returns the y member variable value return(this.y) } // End of Accessor method definition // Accessor for the slope member variable real scalar cbtype::getConfuseM() { // Returns the slope member variable value return(this.m) } // End of Accessor method definition // Accessor for the intercept member variable real scalar cbtype::getConfuseYint() { // Returns the intercept member variable value return(this.yint) } // End of Accessor method definition // Method used to set the value of the member variable x void cbtype::setConfuseX(real scalar xarg) { // Set the value of the member variable x to the value passed to the method this.x = xarg } // End of Setter method definition // Method used to set the value of the member variable y void cbtype::setConfuseY(real scalar yarg) { // Set the value of the member variable y to the value passed to the method this.y = yarg } // End of Setter method definition // Method used to set the value of the slope member variable void cbtype::setConfuseM(real scalar slope) { // Set the value of the member variable slope to the value passed to the method this.m = slope } // End of Setter method definition // Method used to set the value of the intercept member variable void cbtype::setConfuseYint(real scalar intercept) { // Set the value of the member variable intercept to the value passed to the method this.yint = intercept } // End of Setter method definition /* Class for Protanopia (Red Colorblindness) inherits from cbtype; the constructor will set the values of the parent class and will implement the getter methods to retrieve those values. */ class Protanopia extends cbtype { // Public Methods public: // Defines the constructor method void new() } // End of class definition for Protanopia // Protanopia class constructor method void Protanopia::new() { // Sets the x member variable's value super.setConfuseX(0.7465) // Sets the y member variable's value super.setConfuseY(0.2535) // Sets the slope member variable's value super.setConfuseM(1.273463) // Sets the intercept variable's value super.setConfuseYint(-0.073894) } // End of constructor method for red-colorblindness class /* Class for Dueteranopia (Green Colorblindness) inherits from cbtype; the constructor will set the values of the parent class and will implement the getter methods to retrieve those values. */ class Deuteranopia extends cbtype { // Public Methods public: // Defines the constructor method void new() } // End of class definition for Dueteranopia // Deuteranopia class constructor method void Deuteranopia::new() { // Sets the x member variable's value super.setConfuseX(1.4) // Sets the y member variable's value super.setConfuseY(-0.4) // Sets the slope member variable's value super.setConfuseM(0.968437) // Sets the intercept variable's value super.setConfuseYint(0.003331) } // End of constructor method for green-colorblindness class /* Class for Tritanopia (Blue Colorblindness) inherits from cbtype; the constructor will set the values of the parent class and will implement the getter methods to retrieve those values. */ class Tritanopia extends cbtype { // Public Methods public: // Defines the constructor method void new() } // End of class definition for Tritanopia // Tritanopia class constructor method void Tritanopia::new() { // Sets the x member variable's value super.setConfuseX(0.1748) // Sets the y member variable's value super.setConfuseY(0.0) // Sets the slope member variable's value super.setConfuseM(0.062921) // Sets the intercept variable's value super.setConfuseYint(0.292119) } // End of constructor method for blue-colorblindness class // Class that implements transformations of RGB values and returns values that // simulate various types of colorblindness. class colorblind { // Private member variables private: // A row vector with the types of colorblindness string rowvector types, typelabs // The initial RGB values, amount parameter, gamma correction parameter, and // inverse gamma correction parameter real scalar inputR, inputG, inputB, amount, gamma, invgamma // Member variable that stores the transformed RGB values real matrix transformedRGB // Class with constants defined for Red color blindness transformations class Protanopia scalar protanope // Class with constants defined for Green color blindness transformations class Deuteranopia scalar deuteranope // Class with constants defined for Blue color blindness transformations class Tritanopia scalar tritanope // Public member variables public: // Setter methods, class constructor, and simulation method void new(), achromatopsia(), setR(), setG(), setB(), setAmount(), setRGB(), simulate() // Getter methods for member variables/returning results to users real scalar getR(), getG(), getB(), getGamma(), getInvGamma(), getConfuseX(), getConfuseY(), getConfuseM(), getConfuseYint(), getAmount(), condMax() // Method to return the string names of the available types string rowvector getTypes(), getTypeLabs() // Returns the name of a single type of color blindness string scalar getType(), getTypeLab() // Returns a matrix with the transformed RGB values real matrix getTransformedRgbs() // Methods that return a single row of numeric data real rowvector getTransformedRgb(), checkRange() // Method used to return the string representation of the transformed RGB void getRgbString() // Method used to return the string representation of all transformed RGB values void getRgbStrings() // Member variables used to store data transformed throughout the simulation // method call real matrix drgb1, drgb2, drgb3, drgb4, drgb5, powrgb, xyz, chroma, sim, diffrgb, fitrgb, trnsconstants } // End of Class definition // Class constructor method void colorblind::new() { // Initialize the color blindness class objects this.protanope = Protanopia() this.deuteranope = Deuteranopia() this.tritanope = Tritanopia() // Initialize the matrix used to store the transformation results this.transformedRGB = J(5, 3, .) // Store the types of colorblindness for macro names this.types = ("baseline", "achromatopsia", "protanopia", "deuteranopia", "tritanopia") // Store the types of colorblindness this.typelabs = ("Normal Vision", "Achromatopsic Vision", /// "Protanopic Vision", "Deuteranopic Vision", "Tritanopic Vision") // Store prototype values for amount, gamma, and inverse gamma parameters this.amount = 1 this.gamma = 2.2 this.invgamma = 1/2.2 // Initialize null matrices used to store data created in the simulation method this.drgb1 = J(5, 3, .) this.powrgb = J(5, 3, .) this.xyz = J(5, 3, .) this.chroma = J(5, 2, .) this.sim = J(5, 8, .) this.diffrgb = J(5, 5, .) this.drgb2 = J(5, 3, .) this.drgb3 = J(5, 4, .) this.drgb4 = J(5, 3, .) this.drgb5 = J(5, 3, .) this.fitrgb = J(5, 3, .) this.trnsconstants = J(5, 4, .) // Set null baseline values for matrices this.drgb1[1, 1..3] = (0, 0, 0) this.powrgb[1, 1..3] = (0, 0, 0) this.xyz[1, 1..3] = (0, 0, 0) this.chroma[1, 1..2] = (0, 0) this.sim[1, 1..8] = (0, 0, 0, 0, 0, 0, 0, 0) this.diffrgb[1, 1..5] = (0, 0, 0, 0, 0) this.drgb2[1, 1..3] = (0, 0, 0) this.drgb3[1, 1..4] = (0, 0, 0, 0) this.drgb4[1, 1..3] = (0, 0, 0) this.drgb5[1, 1..3] = (0, 0, 0) this.fitrgb[1, 1..3] = (0, 0, 0) this.trnsconstants[1, 1..4] = (0, 0, 0, 0) } // End Colorblind class constructor definition // Method to set the amount member variable void colorblind::setAmount(real scalar amt) { // Changes the default amount parameter value to the value passed to the method this.amount = amt } // End of Setter method // Sets the Initial value of the red channel void colorblind::setR(real scalar red) { // Sets the inputR member variable to the value passed to the method this.inputR = red // Set first element of transform matrix to baseline red channel this.transformedRGB[1, 1] = red } // End of Setter method // Sets the Initial value of the green channel void colorblind::setG(real scalar green) { // Sets the inputG member variable to the value passed to the method this.inputG = green // Set first element of transform matrix to baseline green channel this.transformedRGB[1, 2] = green } // End of Setter method // Sets the Initial value of the blue channel void colorblind::setB(real scalar blue) { // Sets the inputB member variable to the value passed to the method this.inputB = blue // Set first element of transform matrix to baseline blue channel this.transformedRGB[1, 3] = blue } // End of Setter method // Sets the Initial values for the red, green, and blue channels void colorblind::setRGB(real scalar red, real scalar green, real scalar blue) { // Set the red channel setR(red) // Set the green channel setG(green) // Set the blue channel setB(blue) } // End of Setter method declaration // Gets the red channel parameter value real scalar colorblind::getR() { // Returns the user specified red channel value return(this.inputR) } // End Accessor method declaration for red channel input // Gets the green channel parameter value real scalar colorblind::getG() { // Returns the user specified green channel value return(this.inputG) } // End Accessor method declaration for green channel input // Gets the blue channel parameter value real scalar colorblind::getB() { // Returns the user specified blue channel value return(this.inputB) } // End Accessor method declaration for blue channel input // Gets the gamma correction parameter value real scalar colorblind::getGamma() { // Returns the gamma correction parameter value return(this.gamma) } // End Accessor method declaration for gamma correction parameter // Gets the inverse gamma correction parameter value real scalar colorblind::getInvGamma() { // Returns the inverse gamma correction parameter value return(this.invgamma) } // End Accessor method declaration for inverse gamma correction parameter // Gets the color blind types member variable string rowvector colorblind::getTypes() { // Returns the names of all of the color blindness types return(this.types) } // End Accessor method declaration for color blindness types // Gets the color blind type labels member variable string rowvector colorblind::getTypeLabs() { // Returns labels used for returned macros return(this.typelabs) } // End Accessor method declaration for color blind type labels // Gets color blindness type label for specific form of color sight impairment string scalar colorblind::getTypeLab(real scalar typ) { // Returns the label to use for graphs/etc... return(this.typelabs[1, typ]) } // End Accessor method declaration for individual color blindness type label // Gets a specific type of color blindness string scalar colorblind::getType(real scalar typ) { // Returns the name of specified color blindness type return(this.types[1, typ]) } // End Accessor method declaration for individual color blindness type // Gets the transformed RGB values for all available transformations requested real matrix colorblind::getTransformedRgbs() { // Returns the matrix containing all transformed values return(this.transformedRGB) } // End Accessor method declaration for // Gets a specific transformed RGB value real rowvector colorblind::getTransformedRgb(real scalar type) { // Returns the RGB values for a specified transformation return(this.transformedRGB[type, .]) } // End Accessor method declaration for single transformed RGB value // Method to return a column vector of transformed RGB values void colorblind::getRgbStrings() { // Declares column vector variable to use to build the return value string matrix x // Declares variable to use for looping over elements in the transformed matrix real scalar i // Initializes the column vector with null strings x = J(rows(getTransformedRgbs()), 2, "") // Loop over the rows of the matrix with the transformed results for(i = 1; i <= rows(getTransformedRgbs()); i++) { // Set the first element in the row to the name of the vision type x[i, 1] = getTypeLab(i) // Populate each element of the column vector with the numeric values // appended into a single string value x[i, 2] = strofreal(this.transformedRGB[i, 1]) + " " + /// strofreal(this.transformedRGB[i, 2]) + " " + /// strofreal(this.transformedRGB[i, 3]) // Set Stata locals with labels and values st_local((getType(i) + "lab"), x[i, 1]) // Set Stata locals with values st_local(getType(i), (x[i, 2])) } // End Loop over rows of the transformed matrix } // End of getter method // Getter method used to retrieve a single RGB string for the specified transform void colorblind::getRgbString(real scalar type) { // String local variable used to store the retrieved RGB value for return string scalar x // Populate each element of the column vector with the numeric values // appended into a single string value x = strofreal(this.transformedRGB[type, 1]) + " " + /// strofreal(this.transformedRGB[type, 2]) + " " + /// strofreal(this.transformedRGB[type, 3]) // Set return value with labels and values st_local((getType(type) + "lab"), getTypeLab(type)) // Set Stata locals with values st_local(getType(type), x) } // End of getter method // Getter method used to access the amount member variable real scalar colorblind::getAmount() { // Returns the current value of the amount member variable for the object return(this.amount) } // End of getter method // Getter method used to access the x member variable of the IDd colorblind object real scalar colorblind::getConfuseX(real scalar type) { // If value passed to method is 3 if (type == 3) { // Return the constant for Protanopia transformation return(this.protanope.getConfuseX()) } // End IF Block for Protanopia // If value 4 is passed else if (type == 4) { // Return the constant for Deuteranopia transformation return(this.deuteranope.getConfuseX()) } // End ELSEIF Block for Deuteranopia // If a value of 5 is passed else if (type == 5) { // Return the constant for Tritanopia transformation return(this.tritanope.getConfuseX()) } // End ELSEIF Block for Tritanopia } // End of getter method for the color blind x variable // Getter method used to access the y member variable of the IDd colorblind object real scalar colorblind::getConfuseY(real scalar type) { // If value passed to method is 3 if (type == 3) { // Return the constant for Protanopia transformation return(this.protanope.getConfuseY()) } // End IF Block for Protanopia // If value 4 is passed else if (type == 4) { // Return the constant for Deuteranopia transformation return(this.deuteranope.getConfuseY()) } // End ELSEIF Block for Deuteranopia // Otherwise else if (type == 5) { // Return the constant for Tritanopia transformation return(this.tritanope.getConfuseY()) } // End ELSEIF Block for Tritanopia } // End of getter method for the color blind y variable // Getter method used to access the slope member variable of the IDd colorblind object real scalar colorblind::getConfuseM(real scalar type) { // If value passed to method is 3 if (type == 3) { // Return the constant for Protanopia transformation return(this.protanope.getConfuseM()) } // End IF Block for Protanopia // If value 4 is passed else if (type == 4) { // Return the constant for Deuteranopia transformation return(this.deuteranope.getConfuseM()) } // End ELSEIF Block for Deuteranopia // If a value of 5 is passed else if (type == 5) { // Return the constant for Tritanopia transformation return(this.tritanope.getConfuseM()) } // End ELSEIF Block for Tritanopia } // End of getter method for the color blind slope variable // Getter method used to access the intercept member variable of the IDd colorblind object real scalar colorblind::getConfuseYint(real scalar type) { // If value passed to method is 3 if (type == 3) { // Return the constant for Protanopia transformation return(this.protanope.getConfuseYint()) } // End IF Block for Protanopia // If value 4 is passed else if (type == 4) { // Return the constant for Deuteranopia transformation return(this.deuteranope.getConfuseYint()) } // End ELSEIF Block for Deuteranopia // If a value of 5 is passed else if (type == 5) { // Return the constant for Tritanopia transformation return(this.tritanope.getConfuseYint()) } // End ELSEIF Block for Tritanopia } // End of getter method for the color blind intercept variable // Method to ensure that RGB values are within [0, 255] real rowvector colorblind::checkRange(real rowvector rgb) { // Row vector to store updated RGB values real rowvector updateRGB // Scalar used for iterator in loop below real scalar i // Initialized a null row vector to store updated values updateRGB = J(1, cols(rgb), .) // Loop over the elements of the row vector for(i = 1; i <= cols(rgb); i++) { // If the value is < 0 if (rgb[1, i] < 0) { // Force value to 0 updateRGB[1, i] = 0 } // End IF Block for values < 0 // If the value is > 255 else if (rgb[1, i] > 255) { // Force the value to 255 updateRGB[1, i] = 255 } // End ELSEIF Block for values > 255 // Otherwise else { // Truncate the value and store only the integer portion of it updateRGB[1, i] = trunc(rgb[1, i]) } // End ELSE Block for values in [0, 255] } // End Loop over row vector elements // Return the updated/validated RGB values return(updateRGB) } // End of method to validate range for RGB values // Total colorblindness method (type == 1) void colorblind::achromatopsia() { // Declare local variables for method real scalar dr, dg, db // Declare rowvector used to store the transformed RGB values real rowvector nrgb // Convert to Monochrome using sRGB WhitePoint dr = (getR() * 0.212656 + getG() * 0.715158 + getB() * 0.072186) // Anomylize colors dr = (getR() * (1.0 - getAmount())) + dr * getAmount() dg = (getR() * (1.0 - getAmount())) + dr * getAmount() db = (getR() * (1.0 - getAmount())) + dr * getAmount() // Pass the values and check that they are in [0, 255] nrgb = checkRange((dr, dg, db)) // Update the matrix containing the transformed values this.transformedRGB[2, .] = nrgb } // End Method used to transform value to complete colorblind values // Conditional maximum method for simulations real scalar colorblind::condMax(real rowvector fits) { // Declares variable used for loop real scalar i // Declares variable used to store the new RGB rowvector real rowvector x // Initializes a null rowvector x = J(1, 3, .) // Loop over the elements of the rowvector passed as an argument for(i = 1; i <= cols(fits); i++) { // If the value is [0, 1] use that value if (fits[1, i] >= 0 & fits[1, i] <= 1) { // Populate element with passed value x[1, i] = fits[1, i] } // End IF Block to check for range in [0, 1] // If the value is not [0, 1] else { // Set a default value of 0 x[1, i] = 0 } // End ELSE Block for values outside of [0, 1] } // End Loop over elements of rowvector argument // Return the adjusted rowvector return(max(x)) } // End method declaration for conditional maximum value // Simulate colorblind RGB values and populate the transformed matrix with new values void colorblind::simulate(| real rowvector types) { // Declares scalar variables local to this method real scalar dr, dg, db, powr, powg, powb, x, y, z, chromax, chromay, m, yint, deviatex, deviatey, neutralx, neutralz, diffx, diffz, diffr, diffg, diffb, fitr, fitg, fitb, adjust, i, thetype // Declares row vector variables local to this method real rowvector tomax, updateRGB // If no value specified iterate over all values if (args() == 0) { // Create a row vector with all possible values types = (1, 2, 3, 4) } // End IF Block for optional argument handling // Add baseline color to color matrix this.transformedRGB[1, .] = ((getR(), getG(), getB())) // Loop over the arguments passed to the method for(i = 1; i <= cols(types); i++) { // Get the transformation/simulation type thetype = types[1, i] + 1 // For type = 1 (Full Color Blindness if (thetype == 2) { // Transform values for full/complete color blindness achromatopsia() } // End IF Block for full color blindness /* For all other types of color blindness the type codes are: 3 = Protanopia (Red Color Blindness) 4 = Deuteranopia (Green Color Blindness) 5 = Tritanopia (Blue Color Blindness) */ else { // Transform value variables dr = getR() dg = getG() db = getB() // Stores the starting RGB values for this iteration this.drgb1[thetype, .] = (dr, dg, db) // RGB Adjusted for gamma level 2.2 powr = dr^getGamma() powg = dg^getGamma() powb = db^getGamma() // Stores the RGB values raised to Gamma for debugging this.powrgb[thetype, .] = (powr, powg, powb) // XYZ color space representation of RGB values x = powr * 0.412424 + powg * 0.357579 + powb * 0.180464 y = powr * 0.212656 + powg * 0.715158 + powb * 0.0721856 z = powr * 0.0193324 + powg * 0.119193 + powb * 0.950444 // Stores the xyz color space transformation values for debugging this.xyz[thetype, .] = (x, y, z) // Convert XYZ into xyY Chromacity Coordinates (xy) and Luminance (Y) chromax = x / (x + y + z) chromay = y / (x + y + z) // Stores the chroma transformation values for debugging this.chroma[thetype, .] = (chromax, chromay) // Stores the transformation constants for debugging this.trnsconstants[thetype, .] = (getConfuseX(thetype), getConfuseY(thetype), getConfuseM(thetype), getConfuseYint(thetype)) // Generate the "Confusion Line" between the source color and the Confusion Point // slope of the confusion line m = (chromay - getConfuseY(thetype)) / (chromax - getConfuseX(thetype)) // Intercept for confusion line yint = chromay - chromax * m // How far the xy coords deviate from the simulation deviatex = (getConfuseYint(thetype) - yint) / (m - getConfuseM(thetype)) deviatey = (m * deviatex) + yint // Compute the simulated color's XYZ coords x = deviatex * y / deviatey z = (1.0 - (deviatex + deviatey)) * y / deviatey // Neutral grey calculated from luminance (in D65) neutralx = 0.312713 * y / 0.329016 neutralz = 0.358271 * y / 0.329016 // Stores simulation variable values for debugging this.sim[thetype, .] = (yint, m, deviatex, deviatey, neutralx, neutralz, x, z) // Difference between simulated color and neutral grey diffx = neutralx - x diffz = neutralz - z // Used to compensate transformed values into RGB color space diffr = diffx * 3.24071 + diffz * -0.498571 diffg = diffx * -0.969258 + diffz * 0.0415557 diffb = diffx * 0.0556352 + diffz * 1.05707 // Stores difference variables for debugging this.diffrgb[thetype, .] = (diffr, diffg, diffb, diffx, diffz) // Convert XYZ to RGB color space (xyz -> rgb (sRGB:D65)) dr = x * 3.24071 + y * -1.53726 + z * -0.498571 dg = x * -0.969258 + y * 1.87599 + z * 0.0415557 db = x * 0.0556352 + y * -0.203996 + z * 1.05707 // Stores initial transform values for debugging this.drgb2[thetype, .] = (dr, dg, db) // Compensate simulated color towards a neutral fit in RGB space // If the transformed red is >= 0 if (dr >= 0) { // Fitted value for red is 1 - transformedRed / red residual fitr = (1 - dr) / diffr } // End IF Block for transformed red values >= 0 // Otherwise else { // Value is 0 - transformedRed / red residual fitr = (0 - dr) / diffr } // End ELSE Block for fitted red values // If the transformed green is >= 0 if (dg >= 0) { // Fitted value for green is 1 - transformedRed / green residual fitg = (1 - dg) / diffg } // End IF Block for transformed green values >= 0 // Otherwise else { // Value is 0 - transformedGreen / green residual fitg = (0 - dg) / diffg } // End ELSE Block for fitted green values // If the transformed blue is >= 0 if (db >= 0) { // Fitted value for blue is 1 - transformedBlue / blue residual fitb = (1 - db) / diffb } // End IF Block for transformed blue values >= 0 // Otherwise else { // Value is 0 - transformedBlue / blue residual fitb = (0 - db) / diffb } // End ELSE Block for fitted blue values // Store the fitted values for debugging this.fitrgb[thetype, .] = (fitr, fitg, fitb) // Used to get the maxmimum parameter value across r, g, and b parameters tomax = (fitb, fitg, fitr) // Maximum value across all parameters adjust = condMax(tomax) // Shift proportional to the greatest shift dr = dr + (adjust * diffr) dg = dg + (adjust * diffg) db = db + (adjust * diffb) // Store the proportionally adjusted RGB values and the adjustment factor this.drgb3[thetype, .] = (dr, dg, db, adjust) // Apply gamma correction dr = dr^getInvGamma() dg = dg^getInvGamma() db = db^getInvGamma() // Store RGB values after gamma correction this.drgb4[thetype, .] = (dr, dg, db) // Anomylize colors dr = (getR() * (1.0 - getAmount())) + dr * getAmount() dg = (getG() * (1.0 - getAmount())) + dg * getAmount() db = (getB() * (1.0 - getAmount())) + db * getAmount() // Store anomylized colors this.drgb5[thetype, .] = (dr, dg, db) // Check to make sure ranges are in [0, 255] updateRGB = (dr, dg, db) // Update the matrix with the RGB values this.transformedRGB[thetype, .] = checkRange(updateRGB) } // End ELSE Block for non achromatope types of color blindness } // End Loop over the number of transformations } // End Method definition // Function for simple color transform void translateColor(real scalar red, real scalar green, real scalar blue) { // Declare a colorblind type member variable class colorblind scalar x // Initialize the object x = colorblind() // Set the RGB values x.setRGB(red, green, blue) // Simulate for all types of color blindness x.simulate() // Return the results to the end user x.getRgbStrings() // Destroy the variable x = J(0, 0, .) } // End of Mata wrapper for simple color transforms /******************************************************************************* * * * Additional Utilities for brewscheme * * * * * * brewNameSearch() - searches for named colors by palette name and meta * * variable values. * * * * brewColorSearch() - searches for color sight impairment RGB values given * * a Stata formatted RGB string. * * * * * *******************************************************************************/ // Create class for brew colors class brewcolors { // Private members private: // String matrices containing color matrices string matrix meta, color // Public members public: // Methods to construct the class, search for colors by name, and search by // RGB string(s) void new(), brewNameSearch(), brewColorSearch(), getNames() } // End Class definition // Class constructor method void brewcolors::new() { // Declares variables used in the function string matrix results // String scalar to store file path string scalar cdb, metadb // Stores the file path to the color data base cdb = st_macroexpand("`" + "c(sysdir_personal)" + "'") + "brewcolors/colordb.dta" // Stores the file path for the meta database metadb = st_macroexpand("`" + "c(sysdir_personal)" + "'") + "b/brewmeta.dta" // Load the color palette data base stata(`"use ""' + cdb + `"", clear"') // Create a copy of the color data base this.color = st_sdata(., ("palette", "meta", "rgb", "achromatopsia", "protanopia", "deuteranopia", "tritanopia")) // Load the metadatabase stata(`"use ""' + metadb + `"", clear"') // Create a copy of the meta database this.meta = st_sdata(., ("palette", "meta", "rgb", "achromatopsia", "protanopia", "deuteranopia", "tritanopia")) } // End Class constructor definition // Method used to search for RGB values based on named style definitions void brewcolors::brewNameSearch(string scalar name) { // Declare matrix for results string matrix results // First pass search on the palette name results = select(this.color, this.color[., 1] :== name) // If there are results if (rows(results) != 0) { // Return the results in Stata macros st_local("rgb", results[1, 3]) st_local("achromatopsia", results[1, 4]) st_local("protanopia", results[1, 5]) st_local("deuteranopia", results[1, 6]) st_local("tritanopia", results[1, 7]) } // End IF Block for success on palette names // If palette name search not successful else { // Search the meta data variable (helpful for XKCD variables) results = select(this.color, this.color[., 2] :== name) // If results are found if (rows(results) != 0) { // Return the results in Stata macros st_local("rgb", results[1, 3]) st_local("achromatopsia", results[1, 4]) st_local("protanopia", results[1, 5]) st_local("deuteranopia", results[1, 6]) st_local("tritanopia", results[1, 7]) } // End IF Block for success on meta variable search // If no success else { // Return the argument passed to the method st_local("rgb", name) st_local("achromatopsia", name) st_local("protanopia", name) st_local("deuteranopia", name) st_local("tritanopia", name) } // End ELSE Block for no success } // End ELSE Block for search on meta variable } // End function definition // Search for colorblind values given an RGB string void brewcolors::brewColorSearch(string scalar rgbstring) { // Declares variables used in the function string matrix results // First pass search on the palette name results = select(this.color, this.color[., 3] :== rgbstring) // If there are results if (rows(results) != 0) { // Return the results in Stata macros st_local("rgb", results[1, 3]) st_local("achromatopsia", results[1, 4]) st_local("protanopia", results[1, 5]) st_local("deuteranopia", results[1, 6]) st_local("tritanopia", results[1, 7]) } // End IF Block for success on palette names // First pass search on the palette name results = select(this.meta, this.meta[., 3] :== rgbstring) // If there are results if (rows(results) != 0) { // Return the results in Stata macros st_local("rgb", results[1, 3]) st_local("achromatopsia", results[1, 4]) st_local("protanopia", results[1, 5]) st_local("deuteranopia", results[1, 6]) st_local("tritanopia", results[1, 7]) } // End IF Block for success on palette names // If no success else { // Return the argument originally passed st_local("rgb", rgbstring) st_local("achromatopsia", rgbstring) st_local("protanopia", rgbstring) st_local("deuteranopia", rgbstring) st_local("tritanopia", rgbstring) } // End ELSE Block for no success } // End Function definition for RGB color search // Method to retrieve defined colornames void brewcolors::getNames(| real scalar metaOrNames, real scalar stataonly) { // Check argument if (args() == 0) metaOrNames = 1 else if (metaOrNames < 1 | metaOrNames > 2) metaOrNames = 1 // Declare string column vector for the names string rowvector names string matrix statacols statacols = (this.color \ this.meta) if (stataonly == 1) names = uniqrows(select(statacols[., 1], statacols[., 2] :== "Stata"))' // Get all unique color names based on the palette variable else names = uniqrows((this.color[., metaOrNames] \ this.meta[., metaOrNames]))' // Return the names in a Stata macro st_local("colornames", invtokens(names)) } // End of Method declaration // Exit mata and return to Stata prompt end