Glad that you find RevitLookup useful.
If you want to approach the task seriously, you will probably find the attached C module useful, originally implemented by John Walker, the main founder of Autodesk. I massaged it a bit to use in a C++ environment about 23 years ago. Time flies.
Cheers,
Jeremy
//
// colext.cpp - colour conversion
//
// Adapted for C++ by Jeremy Tammik, 1998-02-12.
//
/*
Autodesk ADS sample file.
DESCRIPTION:
AutoCAD colour manipulation functions
Designed and implemented in October of 1989 by John Walker
This ADS application permits AutoCAD colours to be specified
in any of a number of commonly-used colour systems. The
application provides AutoLisp functions for each colour system
which accept the parameters which define a colour in that
system and return the AutoCAD colour number from the standard
palette for 256 colour devices which best approximates the
requested colour.
In addition, functions are provided to convert either AutoCAD
colour indices or Red-Green-Blue colour triples to their
representation in each of the colour systems.
Finally, functions which convert colours in non-RGB colour
systems to RGB triples are provided.
COLOUR SYSTEMS
==============
The colour systems implemented are:
RGB Colours are specified as the intensities of the
three additive primary colours Red, Green, and Blue
which generate the colour. The intensities are given
in the range from 0 to 1, with 0 indicating no light
of that colour and 1 representing maximum intensity.
CMY Colours are specified by the intensities of the
subtractive primary colours Cyan, Magenta, and
Yellow. The CMY system is used in composing ink and
toner mixtures for subtractive printing processes.
Intensities are specified in the range from 0 to 1,
with 0 indicating none of the specified pigment
present and 1 indicating the maximum amount.
YIQ The YIQ system is used to encode colours for
television broadcasting. The YIQ system remaps the
RGB space such that Y represents a primary scaled to
the human luminosity response curve. Two colours
with the same Y values will be indistinguishable when
viewed on a monochrome monitor; to guarantee colours
are distinct when viewed in monochrome, they should
be converted to YIQ and checked for a substantial
difference in Y. Y ranges from 0 to 1, with zero
indicating black and 1 maximum intensity. Since the
YIQ system is a remapping of the RGB colour cube by a
nontrivial affine transformation, the I and Q values
in the YIQ system do not "come out even"; I ranges
from -0.6 to 0.6, and Q ranges from -0.52 to 0.52.
HSV The HSV system approximates the intuitive concepts of
hue (tint), saturation (shade), and value (tone or
brightness), by mapping colours into a hexcone. Hue
is specified by a number from 0 to 1, representing
the angle around the hue circle in fraction of the
circumference of the hue wheel with red at 0, yellow
at 1/6, and so on. Saturation is expressed as a
number from 0 to 1, with 0 indicating a totally
desaturated shade (grey scale), and 1 a completely
saturated shade (no admixture of white). Value
expresses intensity from 0 to 1, with zero indicating
black and 1 maximum intensity.
HLS The HLS system encodes the intuitive concepts of hue
(tint), lightness (intensity), and saturation
(shade), by mapping colours into a double hexcone.
The HLS system is closely related to the HSV system,
and can be thought of as the result of stretching the
flat end of the HSV hexcone upward until a
symmetrical double hexcone is formed. Hue is
specified by a number from 0 to 1, representing the
angle around the hue circle in fraction of the
circumference of the hue wheel with red at 0, yellow
at 1/6, and so on. Lightness expresses intensity as
a number from 0 to 1, with zero indicating black and
1 maximum intensity. Saturation is expressed as a
number from 0 to 1, with 0 indicating a totally
desaturated shade (grey scale), and 1 a completely
saturated shade (no admixture of white).
CTEMP The CTEMP system specifies colours emitted by a
Planckian (black body) radiator with a given
temperature in degrees Kelvin. Typical colour
temperatures are:
The star Spica 28000 K
The star Sirius 10700 K
North sky light 7500 K
Average daylight 6500 K
Xenon lamp 6000 K
Typical sunlight + skylight 5500 K
The star Betelgeuse 3400 K
Tungsten/halogen lamps 3300 K
Incandescent bulbs (100-200 W) 2900 K
Sunlight at sunset 2000 K
Candle flame 1900 K
CNS The CNS system expresses colours as English language
names.
A colour is specified in the CNS system by a sequence
of English words. CNS colours may be achromatic
(grey scale values) or chromatic. Chromatic colours
consist of a hue (dominant spectral component),
saturation (the extent of dilution with white), and
lightness (intensity). A chromatic hue is composed
by naming and mixing the following primary hues:
Red, Orange/Brown, Yellow, Green, Blue, Purple
and secondary hues:
Reddish, Orangish/Brownish, Yellowish,
Greenish, Bluish, Purplish
To obtain one of the primary hues, just specify its
name, e.g. "Yellow". To obtain a hue halfway
between two primary hues, compose the two bounding
hues: for example "Yellow-green" (or "Green-yellow";
it doesn't matter which is specified first). To
obtain a hue one quarter the distance from one colour
to an adjacent colour, compose the "ish" form of the
adjacent colour with the primary colour. For
example, the hues between Yellow and Green are named:
Yellow
Greenish yellow
Yellow-green (or Green-yellow)
Green
Brown is a somewhat confusing special case. The
colour we perceive as brown has the same hue as
orange but when seen with reduced saturation and
intensity it appears as brown, a colour distinct from
orange (that's why there's no brown in the rainbow,
in case you've ever lost sleep pondering that fact).
To compensate for this perceptual quirk, "brown" and
"brownish" may be used as synonyms for "orange" and
"orangish" when specifying hues. If "brown" or
"brownish" are used, the default saturation and
lightness (see below) are set so the orange hue
appears brown; if explicit saturation and lightness
are given orange and brown are synonymous.
Lightness (brightness or intensity) may be specified
by adding one of the following adjectives to the
colour name:
very dark
dark
medium
light
very light
If no lightness adjective appears, a default is
assumed (unless a brown hue was named, in which case
the default will be "light"). This diverges from the
CNS specification in which omitted lightness defaults
to medium intensity.
Saturation (the degree of admixture of white light)
is specified by the following adjectives:
grayish (or greyish)
moderate
strong
vivid
"Vivid" denotes a fully saturated colour, and is the
default (unless a brown is specified, in which case
the default saturation is "strong").
Examples of chromatic colour specifications are:
red
blue-green
purplish red
very dark green
strong yellow-green
very light grayish greenish yellow
the last describing the colour of snow I don't
recommend you eat.
Achromatic specifications describe 7 shades of grey,
to wit:
black
very dark gray
dark gray
gray (or medium gray)
light gray
very light gray
white
in all cases "grey" may be used instead of "gray".
Although the word order used herein is as prescribed
by the formal specification of CNS, my implementation
is totally insensitive to word order. You can
specify "yellow greyish light greenish very" if you
like, silly seems how notwithstanding it.
AUTOLISP-CALLABLE FUNCTIONS
===========================
Three groups of AutoLisp-callable functions are implemented.
The first convert specifications in external colour systems to
AutoCAD's internal colour indices. These functions map the
specifications into either AutoCAD's standard 8 or 256 colour
palette. The palette is selected with the (COLSET) function:
(COLSET <gamut>)
where <gamut> is either 8 or 256 to choose the colour set
desired. (COLSET) returns the current colour gamut; if called
with no arguments, (COLSET) returns the current colour gamut
without changing it. The following functions return AutoCAD
colour indices between 1 and <gamut> - 1.
(CMY <cyan> <magenta> <yellow>)
(CMY '(<cyan> <magenta> <yellow>))
(CNS "CNS colour name description")
(CTEMP <temperature>)
(HLS <hue> <lightness> <saturation>)
(HLS '(<hue> <lightness> <saturation>))
(HSV <hue> <saturation> <value>)
(HSV '(<hue> <saturation> <value>))
(RGB <red> <green> <blue>)
(RGB '(<<red> <green> <blue>))
(YIQ <Y-value> <I-value> <Q-value>)
(YIQ '(<Y-value> <I-value> <Q-value>))
Except for the (CNS) function, which takes a string argument,
and (CTEMP) which takes a single numeric temperature, all
these conversion functions accept either three numerical
arguments (either integer or real), or a list of three
numbers. Representing colour triples as lists, in the same
manner as three-dimensional point co-ordinates, allows them to
be manipulated as units and operated upon with AutoLisp
functions. For example the (distance) function can be used to
determine distance in colour space as well as in physical
space. If invalid arguments are passed to these functions, an
error message is displayed and nil is returned. The (CNS)
function, which can generate a wide variety of error messages
resulting from syntax errors in the string passed to it,
indicates an error by returning nil. A string describing the
most recent error detected by the (CNS) function can be
obtained by calling (CNSERR). If no error has been detected
by (CNS), (CNSERR) returns nil.
When passed valid arguments, all of these functions return
AutoCAD colour numbers ranging from 1 to 255. They may
therefore be specified at any AutoCAD prompt which requests a
colour number.
A second group of functions converts external colour
specifications to lists of RGB intensities. Each of these
functions takes the same arguments as the functions which
return AutoCAD colour indices.
(CMY-RGB <cyan> <magenta> <yellow>)
(CMY-RGB '(<cyan> <magenta> <yellow>))
(CNS-RGB "CNS colour name description")
(CTEMP-RGB <temperature>)
(HLS-RGB <hue> <lightness> <saturation>)
(HLS-RGB '(<hue> <lightness> <saturation>))
(HSV-RGB <hue> <saturation> <value>)
(HSV-RGB '(<hue> <saturation> <value>))
(YIQ-RGB <Y-value> <I-value> <Q-value>)
(YIQ-RGB '(<Y-value> <I-value> <Q-value>))
There is no RGB-RGB function; it would be simply an identity
function.
The third family of functions converts AutoCAD colour indices
from 0 to 255 or RGB triples to their representation in each
of the external colour systems. AutoCAD colour index 0
(black), which cannot be specified as an entity colour, is
nonetheless a valid argument to these functions.
(TO-CMY <colour>)
(TO-CNS <colour>)
(TO-HLS <colour>)
(TO-HSV <colour>)
(TO-RGB <colour>)
(TO-YIQ <colour>)
With the exception of (TO-CNS), which returns a CNS colour
specification string, all of these functions return a list of
three real numbers specifying the values in its colour system
corresponding to the AutoCAD colour index or RGB triple. If
an RGB triple is specified for <colour> it may be given as
three arguments or as a list of three numbers.
INTERNAL FUNCTIONS
==================
Internal conversion functions implemented in this module are
as described below. Definitions are given as prototypes for
readability; for compatibility with older compilers, the
actual code is not prototyped. All conversions are to and
from RGB--to get between two non-RGB systems, you must convert
through RGB.
CMY
void rgb_cmy(double r, double g, double b,
double *c, double *m, double *y)
Converts r, g, b (0 to 1) to c, m, y (0 to 1).
void cmy_rgb(double c, double m, double y,
double *r, double *g, double *b)
Converts c, m, y (0 to 1) to r, g, b (0 to 1).
CTEMP
void ctemp_rgb(double temperature,
double *r, double *g, double *b)
Converts a colour temperature specified in degrees Kelvin,
to r, g, b (0 to 1).
YIQ
void rgb_yiq(double r, double g, double b,
double *y, double *i, double *q)
Converts r, g, b (0 to 1) to y (0 to 1), i (-0.6 to 0.6),
and q (-0.52 to 0.52).
void yiq_rgb(double y, double i, double q,
double *r, double *g, double *b)
Converts y (0 to 1), i (-0.6 to 0.6), and q (-0.52 to 0.52)
to r, g, b (0 to 1).
HSV
void rgb_hsv(double r, double g, double b,
double *h, double *s, double *v)
Converts r, g, b (0 to 1) to h (0 to 360), s (0 to 1), and
v (0 to 1). Note that rgb_hsv() returns hue in terms of
degrees, not as a fraction of circumference as do the
AutoLisp-callable functions.
void hsv_rgb(double h, double s, double v,
double *r, double *g, double *b)
Converts h (0 to 360), s (0 to 1), and v (0 to 1) to r, g,
b (0 to 1). Note that rgb_hsv() expects hue in terms of
degrees, not as a fraction of circumference as do the
AutoLisp-callable functions.
HLS
void rgb_hls(double r, double g, double b,
double *h, double *l, double *s)
Converts r, g, b (0 to 1) to h (0 to 360), l (0 to 1), and
s (0 to 1). Note that rgb_hls() returns hue in terms of
degrees, not as a fraction of circumference as do the
AutoLisp-callable functions.
void hls_rgb(double h, double l, double s,
double *r, double *g, double *b)
Converts h (0 to 360), l (0 to 1), and s (0 to 1) to r, g,
b (0 to 1). Note that rgb_hls() expects hue in terms of
degrees, not as a fraction of circumference as do the
AutoLisp-callable functions.
CNS
void rgb_cns(double r, double g, double b, char *cnstr)
Edits a zero-terminated CNS description of the colour
represented by r, g, and b (0 to 1), into the string cnstr.
The maximum length of the edited string is 36 characters,
so a buffer of at least 37 characters should be supplied
for cnstr. If the lightness of the colour is closest to
the CNS nomenclature for the default lightness stored in
the scaled integer variable defcnslit (initially 10000,
representing 1), no intensity is edited. The default
saturation of "vivid" is not edited.
bool cns_rgb(char *cns, double *r, double *g, double *b)
Scans a CNS specification in the string cns and stores RGB
intensities in r, g, and b which range from 0 to 1. If no
lightness is specified, the lightness (as defined for the
HSV routines) in the scaled integer defcnslit is used.
This value is initially set to 10000 for a default
intensity of very light (maximum). The function returns 1
if the specification is valid and 0 if an incorrect CNS
specification is supplied, in which case the character
pointer cnserr will point to an error message describing
the problem.
BIBLIOGRAPHY
============
Fundamentals of Interactive Computer Graphics
by J. D. Foley and A. van Dam, Reading Massachusetts:
Addison-Wesley, 1984.
Measuring Colour
by R. W. G. Hunt, West Sussex England: Ellis Horwood
Ltd., 1987. (Distributed by John Wiley & Sons).
A New Color-Naming System for Graphics Languages
by Toby Berk, Lee Brownston, and Arie Kaufman, Florida
International University, IEEE Computer Graphics and
Applications, May 1982, Page 37.
*/
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include "rocad.h"
#include "roconst.h"
#include "strutil.h"
#include "colext.h"
#ifdef COLEXT_STANDALONE
/* ADS Function Table structure */
typedef struct {
char *name;
void (*fptr)();
} ftblent;
int funcLoad (void);
int funcUnload (void);
int doFun (void);
extern "C" {
AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg,void * pkt);
}
/* Data types
*/
typedef enum {false = 0, true = 1} bool;
#endif // COLEXT_STANDALONE
#define V (void)
#define EOS '\0'
/* Set point variable from three co-ordinates
*/
#define Spoint(pt, x, y, z) pt[X] = (x); pt[Y] = (y); pt[Z] = (z)
/* Copy point
*/
#define Cpoint(d, s) d[X] = s[X]; d[Y] = s[Y]; d[Z] = s[Z]
/* Utility definition to get an array's element count (at compile
time). For example:
int arr[] = {1,2,3,4,5};
...
printf("%d", ELEMENTS(arr));
would print a five. ELEMENTS("abc") can also be used to tell how
many bytes are in a string constant INCLUDING THE TRAILING NULL.
*/
#define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))
/* Utility definitions
*/
#ifdef abs
#undef abs
#endif
#define abs(x) ((x)<0 ? -(x) : (x))
#ifdef min
#undef min
#endif
#define min(a,b) ((a)<(b) ? (a) : (b))
#ifdef max
#undef max
#endif
#define max(a,b) ((a)>(b) ? (a) : (b))
#ifndef M_E
#define M_E 2.7182818284590452354
#endif
#define Tbit(x) (tok & (1L << ((int) (x)))) /* Test token bit set */
#define Tb(x) (1L << ((int) (x))) /* Obtain bit to test */
/* AutoCAD standard color palette */
#define BLACK 0
#define RED 1
#define YELLOW 2
#define GREEN 3
#define CYAN 4
#define BLUE 5
#define MAGENTA 6
#define WHITE 7
#define SAT 1.0
struct r_g_b { /* RGB colour description */
double red, green, blue;
};
/* Colour naming system vocabulary definition. The vocabulary
is defined in this somewhat unusal fashion to facilitate
translation to languages other than English. */
typedef enum {
/* Chromatic colours */
Red, Orange, Brown, Yellow, Green, Blue, Purple,
/* "ish" forms of chromatic colours */
Reddish, Orangish, Brownish, Yellowish, Greenish, Bluish, Purplish,
/* Achromatic names */
Gray, Black, White,
/* Lightness specifications */
Very, Dark, Medium, Light,
/* Saturation specifications */
Grayish, Moderate, Strong, Vivid,
/* Punctuation */
Hyphen, Period, Huh
} colourvocab;
static struct {
char *cname;
colourvocab ccode;
} cvocab[] = {
{/*MSG3*/"red", Red},
{/*MSG4*/"orange", Orange},
{/*MSG5*/"brown", Brown},
{/*MSG6*/"yellow", Yellow},
{/*MSG7*/"green", Green},
{/*MSG8*/"blue", Blue},
{/*MSG9*/"purple", Purple},
{/*MSG10*/"reddish", Reddish},
{/*MSG11*/"orangish", Orangish},
{/*MSG12*/"brownish", Brownish},
{/*MSG13*/"yellowish", Yellowish},
{/*MSG14*/"greenish", Greenish},
{/*MSG15*/"bluish", Bluish},
{/*MSG16*/"purplish", Purplish},
{/*MSG17*/"gray", Gray},
{/*MSG18*/"grey", Gray},
{/*MSG19*/"black", Black},
{/*MSG20*/"white", White},
{/*MSG21*/"very", Very},
{/*MSG22*/"dark", Dark},
{/*MSG23*/"medium", Medium},
{/*MSG24*/"light", Light},
{/*MSG25*/"grayish", Grayish},
{/*MSG26*/"greyish", Grayish},
{/*MSG27*/"moderate", Moderate},
{/*MSG28*/"strong", Strong},
{/*MSG29*/"vivid", Vivid}
};
/* Table mapping generic hues to HSV hue indices. */
static struct {
long cbit;
int chue;
} colhue[] = {
{Tb(Red), 0}, /* red */
{Tb(Orange), 30}, /* orange */
{Tb(Brown), -30}, /* brown */
{Tb(Yellow), 60}, /* yellow */
{Tb(Green), 120}, /* green */
{Tb(Blue), 240}, /* blue */
{Tb(Purple), 300}, /* purple */
{0L, 360} /* red (other incarnation) */
};
/* Table mapping secondary hues to HSV hue indices. */
static struct {
long cbit;
int chue;
} ishhue[] = {
{Tb(Reddish), 0}, /* reddish */
{Tb(Orangish), 30}, /* orangish */
{Tb(Brownish), -30}, /* brownish */
{Tb(Yellowish), 60}, /* yellowish */
{Tb(Greenish), 120}, /* greenish */
{Tb(Bluish), 240}, /* bluish */
{Tb(Purplish), 300}, /* purplish */
{0L, 360} /* reddish (other incarnation) */
};
#define MAXTK 10 /* Maximum tokens in specification */
#define MAXTKS 20 /* Longest token in characters */
#define BROWNLIGHT 3 /* Brown lightness: Medium */
#define BROWNSAT 3 /* Brown saturation: Strong */
/* Modal variables */
static int defcnslit = 10000; /* Default lightness if none specified */
static int gamut = 256; /* Colour gamut available */
/* Local variables */
static char *cnserr = NULL; /* Error message string */
static char cnserb[80]; /* Error message edit buffer */
static char tokenb[MAXTKS]; /* Token buffer */
/* Forward functions */
#define _(x) x
static void hsv_rgb _((double,double,double,double *,double *,double *));
static void rgb_hsv _((double,double,double,double *,double *,double *));
static void rgb_hls _((double,double,double,double *,double *,double *));
static double hlsval _((double, double, double));
static void hls_rgb _((double,double,double,double *,double *,double *));
static void rgb_yiq _((double,double,double,double *,double *,double *));
static void yiq_rgb _((double,double,double,double *,double *,double *));
static void rgb_cmy _((double,double,double,double *,double *,double *));
static void ctemp_rgb _((double, double *,double *,double *));
#ifdef NEEDED
static void cmy_rgb _((double,double,double,double *,double *,double *));
#endif
static colourvocab token _((char **));
static bool cns_rgb _((char *, double *, double *, double *));
static char *cixname _((colourvocab));
static void rgb_cns _((double, double, double, char *));
static void acadrgb _((int, struct r_g_b *));
static int rgbacad _((double, double, double));
static void retrgb _((bool, double, double, double));
static bool triple _((double *, bool));
static void cmy _((bool));
static void cns _((bool));
static void cnser _((void));
static void ctemp _((bool));
static void hls _((bool));
static void hsv _((bool));
static void rgb _((bool));
static void yiq _((bool));
static void cmyac _((void));
static void ctempac _((void));
static void yiqac _((void));
static void hsvac _((void));
static void rgbac _((void));
static void hlsac _((void));
static void cnsac _((void));
static void cmyrgb _((void));
static void ctemprgb _((void));
static void yiqrgb _((void));
static void hsvrgb _((void));
static void hlsrgb _((void));
static void cnsrgb _((void));
static bool acadcol _((struct r_g_b *));
static void torgb _((void));
static void tocmy _((void));
static void toyiq _((void));
static void tohsv _((void));
static void tohls _((void));
static void tocns _((void));
static void colset _((void));
#ifdef COLEXT_STANDALONE
/* Colour system to AutoCAD colour functions. */
static void cmyac() { cmy(true); }
static void ctempac() { ctemp(true); }
static void yiqac() { yiq(true); }
static void hsvac() { hsv(true); }
static void rgbac() { rgb(true); }
static void hlsac() { hls(true); }
static void cnsac() { cns(true); }
/* Colour system to RGB functions. */
static void cmyrgb() { cmy(false); }
static void ctemprgb() { ctemp(false); }
static void yiqrgb() { yiq(false); }
static void hsvrgb() { hsv(false); }
static void hlsrgb() { hls(false); }
static void cnsrgb() { cns(false); }
/* Command definition and dispatch table. */
ftblent exfun[] = {
/* Name Function */
/* External colour system to AutoCAD colour functions */
{/*MSG0*/"CMY", cmyac},
{/*MSG0*/"CNS", cnsac},
{/*MSG0*/"CTEMP", ctempac},
{/*MSG0*/"HLS", hlsac},
{/*MSG0*/"HSV", hsvac},
{/*MSG0*/"RGB", rgbac},
{/*MSG0*/"YIQ", yiqac},
/* External colour system to RGB functions */
{/*MSG0*/"CMY-RGB", cmyrgb},
{/*MSG0*/"CNS-RGB", cnsrgb},
{/*MSG0*/"CTEMP-RGB", ctemprgb},
{/*MSG0*/"HLS-RGB", hlsrgb},
{/*MSG0*/"HSV-RGB", hsvrgb},
{/*MSG0*/"YIQ-RGB", yiqrgb},
/* AutoCAD colour index to external colour system functions */
{/*MSG0*/"TO-RGB", torgb},
{/*MSG0*/"TO-CMY", tocmy},
{/*MSG0*/"TO-YIQ", toyiq},
{/*MSG0*/"TO-HSV", tohsv},
{/*MSG0*/"TO-HLS", tohls},
{/*MSG0*/"TO-CNS", tocns},
/* Control and utility functions */
{/*MSG0*/"CNSERR", cnser},
{/*MSG0*/"COLSET", colset}
};
/******************************************************************************/
/*.doc funcLoad(internal) */
/*+
This function is called to define all function names in the ADS
function table. Each named function will be callable from lisp or
invokable from another ADS application.
-*/
/******************************************************************************/
int
/*FCN*/funcLoad()
{
int i;
for (i = 0; i < ELEMENTS(exfun); i++) {
if (!ads_defun(exfun[i].name, i))
return RTERROR;
}
return RTNORM;
}
/******************************************************************************/
/*.doc funclUnload(internal) */
/*+
This function is called to undefine all function names in the ADS
function table. Each named function will be removed from the
AutoLISP hash table.
-*/
/******************************************************************************/
int
/*FCN*/funcUnload()
{
int i;
/* Undefine each function we defined */
for (i = 0; i < ELEMENTS(exfun); i++) {
ads_undef(exfun[i].name,i);
}
return RTNORM;
}
/******************************************************************************/
/*.doc doFun(internal) */
/*+
This function is called to invoke the function which has the
registerd function code that is obtained from ads_getfuncode. The
function will return RTERROR if the function code is invalid, or
RSERR if the invoked function fails to return RTNORM. The value
RSRSLT will be returned if the function code is valid and the
invoked subroutine returns RTNORM.
-*/
/******************************************************************************/
int
/*FCN*/doFun()
{
int val;
ads_retvoid();
if ((val = ads_getfuncode()) < 0 || val > ELEMENTS(exfun))
return RTERROR;
(*exfun[val].fptr)();
return RTNORM;
}
AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* ptr)
{
if (ptr != NULL) {
// We have been handed some kind of object
// but we aren't going to do anything with it.
}
switch(msg) {
case AcRx::kInitAppMsg:
acrxUnlockApplication(ptr);
RO_SAY_WE_ARE_MDI_AWARE( ptr );
break;
case AcRx::kInvkSubrMsg:
doFun();
break;
case AcRx::kLoadDwgMsg:
funcLoad();
break;
case AcRx::kUnloadDwgMsg:
funcUnload();
ads_printf(/*MSG2*/"Unloading.\n");
break;
case AcRx::kUnloadAppMsg:
default:
break;
}
return AcRx::kRetOK;
}
#endif // COLEXT_STANDALONE
/* ***************************************************
** **
** Colour Interconversion Functions **
** **
***************************************************
*/
/* HSV_RGB -- Convert HSV colour specification to RGB intensities.
Hue is specified as a real value from 0 to 360,
Saturation and Intensity as reals from 0 to 1. The
RGB components are returned as reals from 0 to 1. */
static void hsv_rgb(double h, double s, double v,
double *r, double *g, double *b)
{
int i;
double f, p, q, t;
if (s == 0) {
*r = *g = *b = v;
} else {
if (h == 360.0)
h = 0;
h /= 60.0;
i = (int)h;
f = h - i;
p = v * (1.0 - s);
q = v * (1.0 - (s * f));
t = v * (1.0 - (s * (1.0 - f)));
// roassert(i >= 0 && i <= 5);
switch (i) {
case 0:
*r = v;
*g = t;
*b = p;
break;
case 1:
*r = q;
*g = v;
*b = p;
break;
case 2:
*r = p;
*g = v;
*b = t;
break;
case 3:
*r = p;
*g = q;
*b = v;
break;
case 4:
*r = t;
*g = p;
*b = v;
break;
case 5:
*r = v;
*g = p;
*b = q;
break;
}
}
}
/* RGB_HSV -- Map R, G, B intensities in the range from 0 to 1 into
Hue, Saturation, and Value: Hue from 0 to 360,
Saturation from 0 to 1, and Value from 0 to 1.
Special case: if Saturation is 0 (it's a grey scale
tone), Hue is undefined and is returned as -1.
This follows Foley & van Dam, section 17.4.4. */
static void rgb_hsv(double r, double g, double b,
double *h, double *s, double *v)
{
double imax = max(r, max(g, b)),
imin = min(r, min(g, b)),
rc, gc, bc;
*v = imax;
if (imax != 0)
*s = (imax - imin) / imax;
else
*s = 0;
if (*s == 0) {
*h = -1;
} else {
rc = (imax - r) / (imax - imin);
gc = (imax - g) / (imax - imin);
bc = (imax - b) / (imax - imin);
if (r == imax)
*h = bc - gc;
else if (g == imax)
*h = 2.0 + rc - bc;
else
*h = 4.0 + gc - rc;
*h *= 60.0;
if (*h < 0.0)
*h += 360.0;
}
}
/* RGB_HLS -- Map R, G, B intensities in the range from 0 to 1 into
Hue, Lightness, and Saturation: Hue from 0 to 360,
Lightness from 0 to 1, and Saturation from 0 to 1.
Special case: if Saturation is 0 (it's a grey scale
tone), Hue is undefined and is returned as -1.
This follows Foley & van Dam, section 17.4.5. */
static void rgb_hls(double r, double g, double b,
double *h, double *l, double *s)
{
double imax = max(r, max(g, b)),
imin = min(r, min(g, b)),
rc, gc, bc;
*l = (imax + imin) / 2;
if (imax == imin) {
*s = 0;
*h = -1;
} else {
if (*l <= 0.5)
*s = (imax - imin) / (imax + imin);
else
*s = (imax - imin) /
(2.0 - imax - imin);
rc = (imax - r) / (imax - imin);
gc = (imax - g) / (imax - imin);
bc = (imax - b) / (imax - imin);
if (r == imax)
*h = bc - gc;
else if (g == imax)
*h = 2.0 + rc - bc;
else
*h = 4.0 + gc - rc;
*h *= 60.0;
if (*h < 0)
*h += 360.0;
}
}
/* HLS_RGB -- Convert HLS colour specification to RGB intensities.
Hue is specified as a real value from 0 to 360;
Lightness and Saturation as reals from 0 to 1. The
RGB components are returned as reals from 0 to 1. */
static double hlsval(double n1, double n2, double hue)
{
if (hue > 360.0)
hue -= 360.0;
else if (hue < 0.0)
hue += 360.0;
if (hue < 60.0) {
return n1 + ((n2 - n1) * hue) / 60.0;
} else if (hue < 180.0) {
return n2;
} else if (hue < 240.0) {
return n1 + ((n2 - n1) * (240.0 - hue)) / 60.0;
} else {
return n1;
}
}
static void hls_rgb(double h, double l, double s,
double *r, double *g, double *b)
{
double m1, m2;
if (l <= 0.5)
m2 = l * (1.0 + s);
else
m2 = l + s - (l * s);
m1 = 2 * l - m2;
if (s == 0) {
*r = *g = *b = l;
} else {
*r = hlsval(m1, m2, h + 120.0);
*g = hlsval(m1, m2, h);
*b = hlsval(m1, m2, h - 120.0);
}
}
/* RGB_YIQ -- Convert RGB colour specification, R, G, B ranging
from 0 to 1, to Y, I, Q colour specification.
|Y| |0.30 0.59 0.11| |R|
|I| = |0.60 -0.28 -0.32| . |G|
|Q| |0.21 -0.52 0.31| |B|
*/
static void rgb_yiq(double r, double g, double b,
double *y, double *i, double *q)
{
double ay = (r * 0.30 + g * 0.59 + b * 0.11),
ai = (r * 0.60 + g * -0.28 + b * -0.32),
aq = (r * 0.21 + g * -0.52 + b * 0.31);
*y = ay;
if (ay == 1.0) { /* Prevent round-off on grey scale */
ai = aq = 0.0;
}
*i = ai;
*q = aq;
}
/* YIQ_RGB -- Convert YIQ colour specification, Y, I, Q given as
reals, Y from 0 to 1, I from -0.6 to 0.6, Q from
-0.52 to 0.52, to R, G, B intensities in the range
from 0 to 1. The matrix below is the inverse of the
RGB_YIQ matrix above.
|R| |1.00 0.948 0.624| |Y|
|G| = |1.00 -0.276 -0.640| . |I|
|B| |1.00 -1.105 1.730| |Q|
*/
static void yiq_rgb(double y, double i, double q,
double *r, double *g, double *b)
{
double ar = (y + i * 0.948 + q * 0.624),
ag = (y + i * -0.276 + q * -0.640),
ab = (y + i * -1.105 + q * 1.730);
*r = max(0, min(1.0, ar));
*g = max(0, min(1.0, ag));
*b = max(0, min(1.0, ab));
}
/* RGB_CMY -- Convert RGB colour specification, R, G, B ranging
from 0 to 1, to C, M, Y colour specification, also
ranging from 0 to 1.
|C| |1| |R|
|M| = |1| - |G|
|Y| |1| |B|
*/
static void rgb_cmy(double r, double g, double b,
double *c, double *m, double *y)
{
*c = 1.0 - r;
*m = 1.0 - g;
*y = 1.0 - b;
}
#ifdef NEEDED
/* CMY_RGB -- Convert CMY colour specification, C, M, Y ranging
from 0 to 1, to R, G, B colour specification, also
ranging from 0 to 1.
|R| |1| |C|
|G| = |1| - |M|
|B| |1| |Y|
*/
static void cmy_rgb(double c, double m, double y,
double *r, double *g, double *b)
{
*r = 1.0 - c;
*g = 1.0 - m;
*b = 1.0 - y;
}
#endif
/* CTEMP_RGB -- Calculate the relative R, G, and B components for a
black body emitting light at a given temperature.
The Planck radiation equation is solved directly
for the R, G, and B wavelengths defined for the CIE
1931 Standard Colorimetric Observer. The colour
temperature is specified in degrees Kelvin. */
static void ctemp_rgb(double temp, double *r, double *g, double *b)
{
double c1 = 3.74183e10,
c2 = 14388.0,
er, eg, eb, es;
/* Lambda is the wavelength in microns: 5500 angstroms is 0.55 microns. */
#define Planck(lambda) ((c1 * pow((double) lambda, -5.0)) / \
(pow(M_E, c2 / (lambda * temp)) - 1))
er = Planck(0.7000);
eg = Planck(0.5461);
eb = Planck(0.4358);
#undef Planck
es = 1.0 / max(er, max(eg, eb));
*r = er * es;
*g = eg * es;
*b = eb * es;
}
/* TOKEN -- Scan next token from the CNS string and update the
scan pointer. */
static colourvocab token(char **icp)
{
char ch;
char *cp = *icp, *tch;
int i, t = 0;
/* Ignore leading space */
while (true) {
ch = *cp++;
if (!isSpace(ch))
break;
}
if (ch == EOS)
return Period;
if (ch == '-') {
*icp = cp;
return Hyphen;
}
tch = cp - 1; /* Start of token pointer */
if (!isalpha(ch)) {
*cp = EOS;
*icp = tch;
return Huh;
}
while (isalpha(ch)) {
if (isupper(ch))
ch = tolower(ch);
if (t < ((sizeof tokenb) - 2))
tokenb[t++] = ch;
ch = *cp++;
}
tokenb[t] = EOS;
*icp = cp - 1;
for (i = 0; i < ELEMENTS(cvocab); i++) {
if (strcmp(tokenb, cvocab[i].cname) == 0) {
return cvocab[i].ccode;
}
}
**icp = EOS;
*icp = tch;
return Huh;
}
/* CNS_RGB -- Convert a CNS string to RGB intensities scaled from 0
to 1. If an invalid specification is made, 0
is returned and an error message is pointed to by the
global character pointer cnserr. Otherwise, 1 is
returned. */
static bool cns_rgb(char *cns, double *r, double *g, double *b)
{
int i, j, k = 0, lightness, saturation;
long tok = 0L, hue;
colourvocab t;
static char conflite[] = /*MSG31*/"Conflicting lightness specification.";
/* Grey scale table */
static int greyscale[] = {50, 17, 33, 50, 67, 83};
/* Saturation percentage table */
static int satab[] = {10000, 2500, 5000, 7500, 10000};
/* Chromatic lightness table */
static int litetab[] = {5000, 1300, 2500, 5000, 7500, 10000};
cnserr = NULL; /* Initially no error in CNS string */
j = strlen(cns);
if (j == 0) {
cnserr = /*MSG32*/"Void specification.";
return false;
}
/* Scan string and parse tokens */
while (true) {
t = token(&cns);
if (t == Huh) {
V sprintf(cnserb, /*MSG33*/"Unrecognised symbol: `%s'.", cns);
cnserr = cnserb;
return false;
}
if (Tbit(t)) {
V sprintf(cnserb, /*MSG34*/"Duplicate symbol: `%s'.", tokenb);
cnserr = cnserb;
return false;
}
if (t == Period)
break;
tok |= 1L << ((int) t);
}
/* Try to obtain lightness from specification */
if (tok & (Tb(Very) | Tb(Dark) | Tb(Medium) | Tb(Light))) {
if (Tbit(Medium)) {
if (Tbit(Very)) {
cnserr = /*MSG35*/"Very used with Medium.";
return false;
}
if (Tbit(Light) || Tbit(Dark)) {
cnserr = conflite;
return false;
}
lightness = 3;
} else if (Tbit(Dark)) {
lightness = Tbit(Very) ? 1 : 2;
if (Tbit(Light)) {
cnserr = conflite;
return false;
}
} else if (Tbit(Light)) {
lightness = Tbit(Very) ? 5 : 4;
} else {
cnserr = /*MSG36*/"Very used without Light or Dark.";
return false;
}
} else {
lightness = 0;
}
/* Test for achromatic colour specification. */
i = !!(Tbit(Black)) + !!(Tbit(Gray)) + !!(Tbit(White));
if (i > 0) {
/* Test for conflicting specification of more than
one achromatic colour. */
if (i != 1) {
cnserr = /*MSG37*/"Conflicting black/gray/white specification.";
return false;
}
/* Test for specification of chromatic colour with
achromatic colour. */
if (tok & (Tb(Red) | Tb(Orange) | Tb(Brown) | Tb(Yellow) |
Tb(Green) | Tb(Blue) | Tb(Purple))) {
cnserr = /*MSG38*/"Chromatic and achromatic shade mixed.";
return false;
}
/* Test for specification of chromatic colour ish form with
achromatic colour. */
if (tok & (Tb(Reddish) | Tb(Orangish) |
Tb(Brownish) | Tb(Yellowish) |
Tb(Greenish) | Tb(Bluish) | Tb(Purplish) |
Tb(Hyphen))) {
cnserr = /*MSG39*/"Chromatic modifier and achromatic shade mixed.";
return false;
}
/* Test for saturation specification with achromatic shade. */
if (tok & (Tb(Grayish) | Tb(Moderate) | Tb(Strong) | Tb(Vivid))) {
cnserr = /*MSG40*/"Saturation specified with achromatic shade.";
return false;
}
/* Test for lightness specified with White or Black. */
if (Tbit(White) || Tbit(Black)) {
if (tok & (Tb(Very) | Tb(Dark) | Tb(Medium) | Tb(Light))) {
cnserr = /*MSG41*/"Lightness specified with black or white.";
return false;
}
if (Tbit(White)) {
*r = *g = *b = 1.0; /* White */
} else {
*r = *g = *b = 0; /* Black */
}
return true;
}
/* Calculate grey scale value from lightness specification. */
*r = *g = *b = greyscale[lightness] / 100.0;
return true;
}
/* It isn't a grey scale, so it must be a chromatic colour
specification. Before we tear into the hue, let's try and
determine the saturation. */
i = (!!Tbit(Grayish)) + (!!Tbit(Moderate)) +
(!!Tbit(Strong)) + (!!Tbit(Vivid));
if (i > 0) {
if (i > 1) {
cnserr = /*MSG42*/"Conflicting saturation specification.";
return false;
}
saturation = Tbit(Grayish) ? 1 :
(Tbit(Moderate) ? 2 :
(Tbit(Strong) ? 3 : 4));
} else {
saturation = 0;
}
/* Count primary hue specifications. */
i = (!!Tbit(Red)) + (!!Tbit(Orange)) + (!!Tbit(Brown)) +
(!!Tbit(Yellow)) +
(!!Tbit(Green)) + (!!Tbit(Blue)) + (!!Tbit(Purple));
if (i == 0) {
cnserr = /*MSG43*/"No hue specified.";
return false;
}
if (i > 2) {
cnserr = /*MSG44*/"More than two hues specified.";
return false;
}
/* Count secondary hue specifications. */
j = (!!Tbit(Reddish)) + (!!Tbit(Orangish)) + (!!Tbit(Brownish)) +
(!!Tbit(Yellowish)) +
(!!Tbit(Greenish)) + (!!Tbit(Bluish)) + (!!Tbit(Purplish));
if (j > 1) {
cnserr = /*MSG45*/"More than one secondary hue specified.";
return false;
}
if (i == 2 && j > 0) {
cnserr = /*MSG46*/"Secondary hue specified with two primary hues.";
return false;
}
/* Obtain hue based on form of specification we've determined
is being made.
Case 1. Pure hue specified by a single primary hue. */
hue = -1;
if (i == 1 && j == 0) {
for (i = 0; i < ELEMENTS(colhue); i++) {
if (tok & colhue[i].cbit) {
hue = abs(colhue[i].chue) * 100L;
/* If it's brown, impute saturation and lightness
if none was explicitly specified. */
if (colhue[i].chue < 0) {
if (lightness == 0)
lightness = BROWNLIGHT;
if (saturation == 0)
saturation = BROWNSAT;
}
break;
}
}
} else if (i == 2) {
/* Case 2. Halfway hue specified by composing two adjacent
primary hues. */
j = k = -1;
for (i = 0; i < ELEMENTS(colhue); i++) {
if (tok & colhue[i].cbit) {
if (j < 0)
j = i;
else {
k = i;
break;
}
}
}
if ((colhue[j].chue == -colhue[k].chue) ||
(((j + 1) != k) &&
!(j == 0 && k == 2) && !(j == 1 && k == 3) &&
(!(j == 0 && k == (ELEMENTS(colhue) - 2))))) {
cnserr = /*MSG47*/"Two primary hues are not adjacent.";
return false;
}
if (Tbit(Red) && Tbit(Purple))
j = ELEMENTS(colhue) - 1;
hue = (abs(colhue[j].chue) + abs(colhue[k].chue)) * 50L;
/* If either is brown, impute saturation and lightness
if none was explicitly specified. */
if (colhue[j].chue < 0 || colhue[k].chue < 0) {
if (lightness == 0)
lightness = BROWNLIGHT;
if (saturation == 0)
saturation = BROWNSAT;
}
} else {
/* Case 3. Quarterway hue specified by one primary hue
and one secondary hue. */
for (i = 0; i < ELEMENTS(colhue); i++) {
if (tok & colhue[i].cbit) {
j = i;
break;
}
}
for (i = 0; i < ELEMENTS(ishhue); i++) {
if (tok & ishhue[i].cbit) {
k = i;
break;
}
}
if ((colhue[j].chue == -colhue[k].chue) || (
((j + 1) != k) && ((j - 1) != k) &&
!(j == 0 && k == 2) && !(j == 1 && k == 3) &&
!(k == 0 && j == 2) && !(k == 1 && j == 3) &&
(!(j == 0 && k == (ELEMENTS(ishhue) - 2))) &&
(!(k == 0 && j == (ELEMENTS(ishhue) - 2)))
)
) {
cnserr = /*MSG48*/"Primary and secondary hues are not adjacent.";
return false;
}
if (Tbit(Red) && Tbit(Purplish))
j = ELEMENTS(colhue) - 1;
else if (Tbit(Purple) && Tbit(Reddish))
k = ELEMENTS(ishhue) - 1;
hue = (abs(colhue[j].chue) * 3 + abs(ishhue[k].chue)) * 25L;
/* If either is brown, impute saturation and lightness
if none was explicitly specified. */
if (colhue[j].chue < 0 || ishhue[k].chue < 0) {
if (lightness == 0)
lightness = BROWNLIGHT;
if (saturation == 0)
saturation = BROWNSAT;
}
}
if (hue < 0) {
cnserr = /*MSG49*/"Internal error--cannot determine hue.";
return false;
}
if (lightness == 0)
k = defcnslit;
else
k = litetab[lightness];
hsv_rgb(hue / 100.0, satab[saturation] / 10000.0, k / 10000.0,
r, g, b);
return true;
}
/* CIXNAME -- Find name of colour vocabulary word from its index. */
static char *cixname( colourvocab cx)
{
int i;
for (i = 0; i < ELEMENTS(cvocab); i++)
if (cvocab[i].ccode == cx)
break;
return cvocab[i].cname;
}
/* RGB_CNS -- Find best CNS description for RGB colour expressed
in R, G, and B, from 0 to 1. */
static void rgb_cns(double r, double g, double b, char *cnstr)
{
int i, j = 0, k, d, s, v;
long lh, ld, hd;
double rh, rs, rv;
#define C(x) ((char) (x))
#define CVB(x) ((colourvocab) (x))
/* Grey scale name table */
static struct {
int intens;
char gname[3];
} gtab[] = {
{0, {C(Black), 0}},
{1700, {C(Very), C(Dark), C(Gray) }},
{3300, {C(Dark), C(Gray), 0}},
{5000, {C(Gray), 0}},
{6700, {C(Light), C(Gray), 0}},
{8300, {C(Very), C(Light), C(Gray) }},
{10000, {C(White), 0}}
};
/* Hue name table */
static struct {
long huecode;
char purename,
ishname;
} huetab[] = {
{0L, C(Red), C(Reddish)},
{3000L, C(Orange), C(Orangish)},
{6000L, C(Yellow), C(Yellowish)},
{12000L, C(Green), C(Greenish)},
{24000L, C(Blue), C(Bluish)},
{30000L, C(Purple), C(Purplish)},
{36000L, C(Red), C(Reddish)}
};
/* Chromatic lightness table */
static struct {
int intens;
char lname[2];
} ltab[] = {
{1250, {C(Very), C(Dark) }},
{2500, {C(Dark), 0}},
{5000, {C(Medium), 0}},
{7500, {C(Light), 0}},
{10000, {C(Very), C(Light) }}
};
/* Chromatic saturation table */
static struct {
int satper;
char sname;
} stab[] = {
{2500, C(Grayish) },
{5000, C(Moderate) },
{7500, C(Strong) },
{10000, C(Vivid) }
};
cnstr[0] = EOS;
rgb_hsv(r, g, b, &rh, &rs, &rv);
lh = (long)(rh * 100L);
s = (int)(rs * 10000);
v = (int)(rv * 10000);
if (s == 0) {
/* Achromatic */
d = 20000;
for (i = 0; i < ELEMENTS(gtab); i++) {
if (abs(gtab[i].intens - v) < d) {
d = abs(gtab[i].intens - v);
j = i;
}
}
for (i = 0; i < 3; i++) {
if (gtab[j].gname[i] == 0)
break;
if (strlen(cnstr))
V strcat(cnstr, " ");
V strcat(cnstr, cixname(CVB(gtab[j].gname[i])));
}
} else {
/* Chromatic. */
/* Locate intensity. If the closest intensity is the
default intensity in DEFCNSLIT, we don't edit any
intensity. You can disable this by setting DEFCNSLIT to
-1. */
d = 20000;
for (i = 0; i < ELEMENTS(ltab); i++) {
if (abs(ltab[i].intens - v) < d) {
d = abs(ltab[i].intens - v);
j = i;
}
}
if (ltab[j].intens != defcnslit) {
for (i = 0; i < 2; i++) {
if (ltab[j].lname[i] == 0)
break;
if (strlen(cnstr))
V strcat(cnstr, " ");
V strcat(cnstr, cixname(CVB(ltab[j].lname[i])));
}
}
/* Locate saturation. If the saturation is vivid, nothing
is edited. */
d = 20000;
for (i = 0; i < ELEMENTS(stab); i++) {
if (abs(stab[i].satper - s) <= d) {
d = abs(stab[i].satper - s);
j = i;
}
}
if (stab[j].satper != 10000) {
if (strlen(cnstr))
V strcat(cnstr, " ");
V strcat(cnstr, cixname(CVB(stab[j].sname)));
}
if (strlen(cnstr))
V strcat(cnstr, " ");
/* Find closest hue name. */
ld = 100000L;
if (lh == 36000L)
lh = 0;
for (i = 0; i < ELEMENTS(huetab); i++) {
if (abs(huetab[i].huecode - lh) < ld) {
ld = abs(huetab[i].huecode - lh);
j = i;
}
}
/* Now we'll find the next hue in the direction of the
actual hue from the specified hue. */
if (lh > huetab[j].huecode) {
if (j == (ELEMENTS(huetab) - 1))
k = 1;
else
k = j + 1;
} else {
if (j == 0)
k = ELEMENTS(huetab) - 2;
else
k = j - 1;
}
/* Next, compute the distance between the hue and the next
neighbour in the hue's direction. */
hd = abs(huetab[j].huecode - huetab[k].huecode);
/* The form of the hue then depends upon the relationship
between the actual distance, D, and the total distance,
HD, from the closest pure hue, J, and the next pure hue
in the direction of the hue supplied, K. We generate
the following based upon the relationship:
D / HD Name
------------ --------
0 - 0.125 J
0.125 - 0.375 Kish J
0.375 - 0.5 J-K
*/
hd = (ld * 10000L) / hd;
if (hd < 1250L) {
V strcat(cnstr, cixname(CVB(huetab[j].purename)));
} else if (hd < 3750L) {
V strcat(cnstr, cixname(CVB(huetab[k].ishname)));
V strcat(cnstr, " ");
V strcat(cnstr, cixname(CVB(huetab[j].purename)));
} else {
V strcat(cnstr, cixname(CVB(huetab[j].purename)));
V strcat(cnstr, "-");
V strcat(cnstr, cixname(CVB(huetab[k].purename)));
}
}
}
/* ACADRGB -- Takes an AutoCAD colour number in hsv and returns
red, green, and blue intensities in rgp in the range
0.0 to 1.0 */
static void acadrgb( int hsv, struct r_g_b *rgp)
{
static double brightfac[5] = { /* Brightness levels */
1.0, 0.65, 0.5, 0.3, 0.15
}, halfsat = .5; /* Halfway saturation */
int ih, vs;
double h, s, f, value;
roassert( 0 <= hsv );
roassert( 256 > hsv );
switch( hsv ) {
case BLACK:
rgp->red = 0.0;
rgp->blue = 0.0;
rgp->green = 0.0;
value = 0.0;
break;
case RED:
rgp->red = SAT;
rgp->green = 0.0;
rgp->blue = 0.0;
value = 1.0;
break;
case YELLOW:
rgp->red = SAT;
rgp->green = SAT;
rgp->blue = 0.0;
value = 1.0;
break;
case GREEN:
rgp->red = 0.0;
rgp->green = SAT;
rgp->blue = 0.0;
value = 1.0;
break;
case CYAN:
rgp->red = 0.0;
rgp->green = SAT;
rgp->blue = SAT;
value = 1.0;
break;
case BLUE:
rgp->red = 0.0;
rgp->green = 0.0;
rgp->blue = SAT;
value = 1.0;
break;
case MAGENTA:
rgp->red = SAT;
rgp->green = 0.0;
rgp->blue = SAT;
value = 1.0;
break;
case WHITE:
case 8:
case 9:
rgp->red = SAT;
rgp->green = SAT;
rgp->blue = SAT;
value = 1.0;
break;
default:
/* The chromatic colors. The hue resulting from an
AutoCAD color 10-249 will be determined by its first
two digits, and the saturation and value from the
last digit, as follows:
Hues:
10 -- Red
50 -- Yellow
90 -- Green
130 -- Cyan
170 -- Blue
210 -- Magenta
Between each of these are three intermediate hues,
e.g., between red and yellow, we have:
20 -- reddish orange
30 -- orange
40 -- yellowish orange
To each hue number, 0, 2, 4, 6, or 8 can be added to
give a different "value", or brightness, with 0 the
brightest and 8 the weakest. Finally, 1 can be
added to produce a "half-saturated", or pastel,
color. For example, color 18 is the dimmest red and
10 the brightest red. 19 is the dimmest pink and 11
the brightest pink.
*/
if (hsv > 9 && hsv < 250) {
/* Apply the algorithm from Foley & van Dam to turn
HSV into RGB values */
ih = (hsv - 10) / 10; /* Integer hue value. */
if (ih >= 24) /* Range is 0-23. */
ih -= 24;
vs = hsv % 10; /* Encoded value and saturation */
h = ih / 4.; /* Map into range [0.0,6.0) */
ih = (int)h; /* The integer part. */
f = h - ih; /* Fractional part. */
value = brightfac[vs >> 1]; /* Value in [0,1] */
s = vs & 1 ? halfsat : 1.0; /* Saturation */
switch (ih) {
case 0:
rgp->red = 1.0;
rgp->green = (double) (1.0 - s * (1.0 - f));
rgp->blue = (double) (1.0 - s);
break;
case 1:
rgp->red = (double) (1.0 - s * f);
rgp->green = 1.0;
rgp->blue = (double) (1 - s);
break;
case 2:
rgp->red = (double) (1.0 - s);
rgp->green = 1.0;
rgp->blue = (double) (1.0 - s *(1.0 - f));
break;
case 3:
rgp->red = (double) (1.0 - s);
rgp->green = (double) (1.0 - s * f);
rgp->blue = 1.0;
break;
case 4:
rgp->red = (double) (1.0 - s * (1.0 - f));
rgp->green = (double) (1.0 - s);
rgp->blue = 1.0;
break;
case 5:
rgp->red = 1.0;
rgp->green = (double) (1.0 - s);
rgp->blue = (double) (1.0 - s * f);
break;
}
} else {
/* Define some extra colours from dark grey to white
in the 250 to 255 slots */
value = 0.33 + (hsv - 250) * 0.134;
rgp->red = 1.0;
rgp->green = 1.0;
rgp->blue = 1.0;
}
break; /* Default */
}
rgp->red *= value; /* Apply lightness scale factor */
rgp->green *= value; /* to components resulting from */
rgp->blue *= value; /* hue and saturation. */
}
/* RGBACAD -- Find the AutoCAD colour closest to in RGB space to a
specified RGB triple. */
static int rgbacad(double r, double g, double b)
{
int i, low, ccol;
double closest = 1000.0;
struct r_g_b rc;
roassert(r >= 0.0 && r <= 1.0);
roassert(g >= 0.0 && g <= 1.0);
roassert(b >= 0.0 && b <= 1.0);
/* If we're mapping to the 8 colour gamut, turn all grey scale
colours into white and map the rest based on hue alone. */
if (gamut == {
double h, s, v;
rgb_hsv(r, g, b, &h, &s, &v);
return s == 0.0 ? WHITE :
(RED + ((((int) (h + 30.0)) % 360) / 60));
}
/* Note that we start with colour 1 since 0 (black) is not a
valid user-specified colour. If this is a grey scale tone,
map only to AutoCAD's grey scale indices. */
ccol = low = (r == g && r == b) ? 250 : 1;
for (i = low; i < 256; i++) {
double cdist;
acadrgb(i, &rc);
rc.red -= r;
rc.green -= g;
rc.blue -= b;
cdist = rc.red * rc.red + rc.green * rc.green +
rc.blue * rc.blue;
if (cdist < closest) {
ccol = i;
if ((closest = cdist) == 0.0)
break;
}
}
if (ccol == 255) /* If synonym for white... */
ccol = 7; /* ...make simple white. */
return ccol;
}
#ifdef COLEXT_STANDALONE
/* RETRGB -- Return an RGB triple as either an RGB point or
the closest AutoCAD standard colour. */
static void retrgb(bool acad, double r, double g, double b)
{
if (acad) {
ads_retint(rgbacad(r, g, b));
} else {
ads_point p;
Spoint(p, r, g, b);
ads_retpoint(p);
}
}
/* TRIPLE -- Scan a triple of real arguments into an array of
reals. Integers are accepted and converted to reals.
true is returned if valid arguments are obtained;
false otherwise. */
static bool triple(double cdesc[3], bool rangecheck)
{
int nargs;
struct resbuf *rb1 = ads_getargs();
ads_retnil();
for (nargs = 0; nargs < 3; nargs++) {
if (rb1 == NULL)
break;
if (rb1->restype == RTSHORT) {
cdesc[nargs] = rb1->resval.rint;
} else if (rb1->restype == RTREAL) {
cdesc[nargs] = rb1->resval.rreal;
} else if (nargs == 0 && rb1->restype == RT3DPOINT) {
Cpoint(cdesc, rb1->resval.rpoint);
nargs = 2;
} else {
ads_fail(/*MSG50*/"incorrect argument type");
return false;
}
rb1 = rb1->rbnext;
}
/* Make sure there were enough arguments. */
if (nargs < 3) {
ads_fail(/*MSG51*/"too few arguments");
return false;
}
/* Make sure there are no more arguments. */
if (rb1 != NULL) {
ads_fail(/*MSG52*/"too many arguments");
return false;
}
/* Range check arguments if requested. */
if (rangecheck) {
for (nargs = 0; nargs < 3; nargs++) {
if (rangecheck && (cdesc[nargs] < 0.0 || cdesc[nargs] > 1.0)) {
ads_fail(/*MSG53*/"argument out of range");
return false;
}
}
}
return true;
}
/* CMY -- Specify colour as CMY triple. */
static void cmy(bool acad)
{
double cdesc[3];
if (triple(cdesc, true)) {
retrgb(acad, 1.0 - cdesc[0], 1.0 - cdesc[1],
1.0 - cdesc[2]);
}
}
/* CTEMP -- Specify colour as a colour temperature. */
static void ctemp(bool acad)
{
struct resbuf *rb;
ads_retnil();
if ((rb = ads_getargs()) == NULL) {
ads_fail(/*MSG63*/"too few arguments");
} else {
double degrees, ir, ig, ib;
if (rb->restype == RTSHORT) {
degrees = rb->resval.rint;
} else if (rb->restype == RTREAL) {
degrees = rb->resval.rreal;
} else {
ads_fail(/*MSG64*/"incorrect argument type");
return;
}
/* Make sure there are no more arguments. */
if (rb->rbnext != NULL) {
ads_fail(/*MSG65*/"too many arguments");
return;
}
ctemp_rgb(degrees, &ir, &ig, &ib);
retrgb(acad, ir, ig, ib);
}
}
/* CNS -- Specify colour as a CNS string. */
static void cns(bool acad)
{
struct resbuf *rb;
ads_retnil();
if ((rb = ads_getargs()) == NULL) {
ads_fail(/*MSG54*/"too few arguments");
return;
} else {
struct resbuf *rb1 = rb;
double ir, ig, ib;
if (rb1->restype != RTSTR) {
ads_fail(/*MSG55*/"incorrect argument type");
return;
}
/* Make sure there are no more arguments. */
if (rb1->rbnext != NULL) {
ads_fail(/*MSG56*/"too many arguments");
return;
}
if (cns_rgb(rb1->resval.rstring, &ir, &ig, &ib)) {
retrgb(acad, ir, ig, ib);
}
}
}
/* CNSER -- Return string describing last CNS error, if any. */
static void cnser()
{
if (cnserr == NULL)
ads_retnil();
else
ads_retstr(cnserr);
}
/* HLS -- Specify colour as HLS triple. */
static void hls(bool acad)
{
double cdesc[3];
if (triple(cdesc, true)) {
double ir, ig, ib;
hls_rgb(cdesc[0] * 360.0, cdesc[1], cdesc[2], &ir, &ig, &ib);
retrgb(acad, ir, ig, ib);
}
}
/* HSV -- Specify colour as HSV triple. */
static void hsv(bool acad)
{
double cdesc[3];
if (triple(cdesc, true)) {
double ir, ig, ib;
hsv_rgb(cdesc[0] * 360.0, cdesc[1], cdesc[2], &ir, &ig, &ib);
retrgb(acad, ir, ig, ib);
}
}
/* RGB -- Specify colour as RGB triple. */
static void rgb( bool acad)
{
double cdesc[3];
if (triple(cdesc, true)) {
retrgb(acad, cdesc[0], cdesc[1], cdesc[2]);
}
}
/* YIQ -- Specify colour as YIQ triple. */
static void yiq(bool acad)
{
double cdesc[3];
if (triple(cdesc, false)) {
double ir, ig, ib;
if ((cdesc[0] < 0.0 || cdesc[0] > 1.0) &&
(cdesc[1] < -0.6 || cdesc[0] > 0.6) &&
(cdesc[2] < -0.52 || cdesc[2] > 0.52)) {
ads_fail(/*MSG57*/"argument out of range");
}
yiq_rgb(cdesc[0], cdesc[1], cdesc[2], &ir, &ig, &ib);
retrgb(acad, ir, ig, ib);
}
}
/* ACADCOL -- Obtain AutoCAD colour. We accept any of the following:
1. A single integer, representing an AutoCAD standard colour index.
2. A triple of reals and/or integers, representing RGB intensities.
3. A list of three reals and/or integers, representing RGB intensities.
*/
static bool acadcol(struct r_g_b *rp)
{
double crgb[3];
struct resbuf *rb = ads_getargs();
ads_retnil();
if (rb == NULL) {
ads_fail(/*MSG58*/"too few arguments");
return false;
}
if ((rb->restype == RTSHORT) && (rb->rbnext == NULL)) {
int cindex = rb->resval.rint;
if (cindex < 0 || cindex > 255) {
ads_fail(/*MSG59*/"argument out of range");
return false;
}
acadrgb(cindex, rp);
return true;
}
if (triple(crgb, true)) {
rp->red = crgb[0];
rp->green = crgb[1];
rp->blue = crgb[2];
} else {
return false;
}
return true;
}
/* TORGB -- Convert internal colour to RGB triple. */
static void torgb()
{
struct r_g_b rc;
if (acadcol(&rc)) {
ads_point p;
Spoint(p, rc.red, rc.green, rc.blue);
ads_retpoint(p);
}
}
/* TOCMY -- Convert internal colour to CMY triple. */
static void tocmy()
{
struct r_g_b rc;
if (acadcol(&rc)) {
ads_point p;
rgb_cmy(rc.red, rc.green, rc.blue, &p[X], &p[Y], &p[Z]);
ads_retpoint(p);
}
}
/* TOYIQ -- Convert internal colour to YIQ triple. */
static void toyiq()
{
struct r_g_b rc;
if (acadcol(&rc)) {
ads_point p;
rgb_yiq(rc.red, rc.green, rc.blue, &p[X], &p[Y], &p[Z]);
ads_retpoint(p);
}
}
/* TOHSV -- Convert internal colour to HSV triple. */
static void tohsv()
{
struct r_g_b rc;
if (acadcol(&rc)) {
ads_point p;
rgb_hsv(rc.red, rc.green, rc.blue, &p[X], &p[Y], &p[Z]);
p[X] = (p[X] < 0.0) ? 0.0 : (p[X] / 360.0);
ads_retpoint(p);
}
}
/* TOHLS -- Convert internal colour to HLS triple. */
static void tohls()
{
struct r_g_b rc;
if (acadcol(&rc)) {
ads_point p;
rgb_hls(rc.red, rc.green, rc.blue, &p[X], &p[Y], &p[Z]);
p[X] = (p[X] < 0.0) ? 0.0 : (p[X] / 360.0);
ads_retpoint(p);
}
}
/* TOCNS -- Convert internal colour to CNS string. */
static void tocns()
{
struct r_g_b rc;
if (acadcol(&rc)) {
char cnstr[40];
rgb_cns(rc.red, rc.green, rc.blue, cnstr);
ads_retstr(cnstr);
}
}
/* COLSET -- Set colour gamut available. */
static void colset()
{
struct resbuf *rb = ads_getargs();
ads_retnil();
if (rb == NULL) {
ads_retint(gamut);
return;
}
if (rb->rbnext != NULL) {
ads_fail(/*MSG60*/"too many arguments");
return;
}
if (rb->restype == RTSHORT) {
int colsys = rb->resval.rint;
switch (colsys) {
case 8:
case 256:
gamut = colsys;
ads_retint(gamut);
break;
default:
ads_fail(/*MSG61*/"argument out of range");
}
return;
}
ads_fail(/*MSG62*/"incorrect argument type");
}
#endif // COLEXT_STANDALONE
typedef unsigned char uchar;
typedef unsigned long ulong;
inline ulong makergb( int r, int g, int b )
{
roassert( 0 <= r && r < 256 );
roassert( 0 <= g && g < 256 );
roassert( 0 <= b && b < 256 );
return ((ulong) ((uchar) (r)))
| ((ulong) ((uchar) (g)) << 8)
| ((ulong) ((uchar) (b)) << 16);
}
inline uchar makebyte( double x )
{
roassert( 0.0 <= x && x <= 1.0 );
int a = int( x * 255 + 0.5 );
roassert( 0 <= a && a < 256 );
return (unsigned char) a;
}
inline double makereal( int a )
{
roassert( 0 <= a && a < 256 );
return a / 255;
}
inline uchar getrvalue( ulong rgb )
{
return uchar( rgb );
}
inline uchar getgvalue( ulong rgb )
{
return uchar( rgb >> 8 );
}
inline uchar getbvalue( ulong rgb )
{
return uchar( rgb >> 16 );
}
//
// exported functions:
//
//
// aci2colorref - convert AutoCAD colour index aci to Windows COLORREF:
//
// The COLORREF value is a 32-bit value used to specify an RGB color.
// When specifying an explicit RGB color, the COLORREF value has the
// following hexadecimal form:
//
// 0x00bbggrr
//
// The low-order byte contains a value for the relative intensity of red;
// the second byte contains a value for green; and the third byte contains
// a value for blue. The high-order byte must be zero. The maximum value
// for a single byte is 0xFF.
//
Ro::ulong aci2colorref( int color )
{
r_g_b rgb;
acadrgb( color, &rgb );
return makergb( makebyte( rgb.red ), makebyte( rgb.green ), makebyte( rgb.green ) );
}
int colorref2aci( Ro::ulong colorref )
{
return rgbacad( makereal( getrvalue( colorref ) ),
makereal( getgvalue( colorref ) ),
makereal( getbvalue( colorref ) ) );
}
bool aci2string( char * buf, int len, int color )
{
roassert( 39 < len );
bool rc = (0 <= color) && (256 > color);
roassert( rc );
if( rc ) {
r_g_b rgb;
acadrgb( color, &rgb );
rgb_cns( rgb.red, rgb.green, rgb.blue, buf );
}
return rc;
}
const char * aci2string( int color )
{
static char cnstr[40];
return aci2string( cnstr, sizeof( cnstr ), color ) ? cnstr : 0;
}
bool aci2hsv(
double & hue,
double & saturation,
double & value,
int color )
{
bool rc = (0 <= color) && (256 > color);
roassert( rc );
if( rc ) {
r_g_b rgb;
acadrgb( color, &rgb );
rgb_hsv( rgb.red, rgb.green, rgb.blue,
&hue, &saturation, &value );
}
return rc;
}
bool hsv2aci(
int & color,
double hue,
double saturation,
double value )
{
roassert( 0.0 <= hue );
roassert( 360.0 >= hue );
roassert( 0.0 <= saturation );
roassert( 1.0 >= saturation );
roassert( 0.0 <= value );
roassert( 1.0 >= value );
double r, g, b;
hsv_rgb( hue, saturation, value, &r, &g, &b );
color = rgbacad( r, g, b );
return true;
}
bool aci2rgb(
double & red,
double & green,
double & blue,
int color )
{
bool rc = (0 <= color) && (256 > color);
roassert( rc );
if( rc ) {
r_g_b rgb;
acadrgb( color, &rgb );
red = rgb.red;
green = rgb.green;
blue = rgb.blue;
}
return rc;
}
bool rgb2aci(
int & color,
double red,
double green,
double blue )
{
roassert( 0.0 <= red );
roassert( 1.0 >= red );
roassert( 0.0 <= green );
roassert( 1.0 >= green );
roassert( 0.0 <= blue );
roassert( 1.0 >= blue );
color = rgbacad( red, green, blue );
return true;
}