1
2/* Lots of little arg checks. Global for convenience.
3 */
4
5check_any = [(const true), _ "any"];
6check_bool = [is_bool, _ "boolean"];
7check_real = [is_real, _ "real"];
8check_ureal = [is_ureal, _ "unsigned real"];
9check_preal = [is_preal, _ "positive real"];
10check_list = [is_list, _ "list"];
11check_real_list = [is_real_list, _ "list of real"];
12check_string = [is_string, _ "string"];
13check_string_list = [is_string_list, _ "list of string"];
14check_int = [is_int, _ "integer"];
15check_uint = [is_uint, _ "unsigned integer"];
16check_pint = [is_pint, _ "positive integer"];
17check_matrix = [is_matrix, _ "rectangular array of real"];
18check_matrix_display = [Matrix_display.is_display, _ "0, 1, 2 or 3"];
19check_image = [is_image, _ "image"];
20check_xy_list = [is_xy_list, _ "list of form [[1, 2], [3, 4], [5, 6], ...]"];
21check_instance name = [is_instanceof name, name];
22check_Image = check_instance "Image";
23check_Matrix = [is_Matrix, _ "Matrix"];
24check_Matrix_width w
25	= [is_Matrix_width, _ "Matrix, " ++ print w ++ _ " columns"]
26{
27	is_Matrix_width x = is_Matrix x && x.width == w;
28}
29check_colour_space = [is_colour_space, "colour_space"];
30check_rectangular = [is_rectangular, _ "rectangular [[*]]"];
31check_Guide = [is_Guide, _ "HGuide or VGuide"];
32check_Colour = check_instance (_ "Colour");
33check_Mark = check_instance (_ "Mark");
34
35/* Check a set of args to a class. Two members to look at: _check_args and
36 * _check_all.
37 *
38 * - each line in _check_args is [arg, "arg name", [test_fn, "arg type"]]
39 *   same number of lines as there are args
40 *
41 *   stuff like "arg 2 must be real"
42 *
43 * - each line in _check_all is [test, "description"]
44 *   any number of lines
45 *
46 *   stuff like "to must be greater than from"
47 *
48 * generate an error dialog with a helpful message on failure.
49 *
50 * Have as a separate function to try to keep the size of _Object down a bit.
51 */
52check_args x
53	= error message, badargs != [] || badalls != []
54 	= check_args x.super, x.super != []
55	= x
56{
57	argcheck = x._check_args;
58	allcheck = x._check_all;
59
60	// join two strings up with a separator string
61	join_sep j a b = a ++ j ++ b;
62
63	// indent string
64	indent = "    ";
65
66	// test for a condition in a check line fails
67	test_fail x = ! x?0;
68
69	// set of failed argcheck indexes
70	badargs = map (extract 1)
71		(filter test_fail (zip2 (map testarg argcheck) [0..]))
72	{
73		testarg x = x?2?0 x?0;
74	}
75
76	// set of failed allcheck indexes
77	badalls = map (extract 1)
78		(filter test_fail (zip2 (map hd allcheck) [0..]));
79
80	// the error message
81	message = _ "bad arguments to " ++ "\"" ++ x.name ++ "\"\n" ++
82		argmsg ++ allmsg ++ "\n" ++
83		_ "usage" ++ "\n" ++ indent ++ usage ++ "\n" ++ _ "where" ++ "\n" ++
84		arg_types ++ extra;
85
86	// make the failed argcheck messages ... eg.  ""value" should be
87	// real, you passed <function>" etc.
88	argmsg = concat (map fmt badargs)
89	{
90		fmt n = indent ++ "\"" ++ argcheck?n?1 ++ "\"" ++
91			_ " should be of type " ++ argcheck?n?2?1 ++ ", " ++
92			_ "you passed" ++ ":\n" ++
93			indent ++ indent ++ print argcheck?n?0 ++ "\n";
94	}
95
96	// make the failed allcheck messages ... eg "condition failed:
97	// x < y" ... don't make a message if any typechecks have
98	// failed, as we'll probably error horribly
99	allmsg
100		= [], badargs != []
101		= concat (map fmt badalls) ++
102			_ "you passed" ++ "\n" ++
103			concat (map fmt_arg argcheck)
104	{
105		fmt n = _ "condition failed" ++ ": " ++ allcheck?n?1 ++ "\n";
106		fmt_arg l = indent ++ l?1 ++ " = " ++ print l?0 ++ "\n";
107	}
108
109	// make usage note
110	usage = x.name ++ " " ++
111		foldr (join_sep " ") [] (map (extract 1) argcheck);
112
113	// make arg type notes
114	arg_types = foldr (join_sep "\n") [] (map fmt argcheck)
115	{
116		fmt l = indent ++ l?1 ++ " is of type " ++ l?2?1;
117	}
118
119	// extra bit at the bottom, if we have any conditions
120	extra
121		= [], allcheck == []
122		= _ "and" ++ "\n" ++ all_desc;
123
124	// make a list of all the allcheck descriptions, with a few
125	// spaces in front
126	all_desc_list = map (join indent @ extract 1) allcheck;
127
128	// join em up to make a set of condition notes
129	all_desc = foldr (join_sep "\n") [] all_desc_list;
130}
131
132/* Operator overloading stuff.
133 */
134
135Operator_type = class {
136	ARITHMETIC = 1;			// eg. add
137	RELATIONAL = 2;			// eg. less
138	COMPOUND = 3;			// eg. max/mean/etc.
139	COMPOUND_REWRAP = 4;	// eg. transpose
140}
141
142Operator op_name fn type symmetric = class {
143}
144
145/* Form the converse of an Operator.
146 */
147oo_converse op
148	= Operator (converse_name op.op_name)
149		(converse op.fn) op.type op.symmetric
150{
151	converse_name x
152		= init x, last x == last "'"
153		= x ++ "'";
154}
155
156/* Given an operator name, look up the definition.
157 */
158oo_binary_lookup op_name
159	= matches?0, matches != []
160	= error (_ "unknown binary operator" ++ ": " ++ print op_name)
161{
162	operator_table = [
163		Operator "add" add
164			Operator_type.ARITHMETIC true,
165		Operator "subtract" subtract
166			Operator_type.ARITHMETIC false,
167		Operator "remainder" remainder
168			Operator_type.ARITHMETIC false,
169		Operator "power" power
170			Operator_type.ARITHMETIC false,
171		Operator "subscript" subscript
172			Operator_type.ARITHMETIC false,
173		Operator "left_shift" left_shift
174			Operator_type.ARITHMETIC false,
175		Operator "right_shift" right_shift
176			Operator_type.ARITHMETIC false,
177		Operator "divide" divide
178			Operator_type.ARITHMETIC false,
179		Operator "join" join
180			Operator_type.ARITHMETIC false,
181		Operator "multiply" multiply
182			Operator_type.ARITHMETIC true,
183		Operator "logical_and" logical_and
184			Operator_type.ARITHMETIC true,
185		Operator "logical_or" logical_or
186			Operator_type.ARITHMETIC true,
187		Operator "bitwise_and" bitwise_and
188			Operator_type.ARITHMETIC true,
189		Operator "bitwise_or" bitwise_or
190			Operator_type.ARITHMETIC true,
191		Operator "eor" eor
192			Operator_type.ARITHMETIC true,
193		Operator "comma" comma
194			Operator_type.ARITHMETIC false,
195		Operator "if_then_else" if_then_else
196			Operator_type.ARITHMETIC false,
197		Operator "equal" equal
198			Operator_type.RELATIONAL true,
199		Operator "not_equal" not_equal
200			Operator_type.RELATIONAL true,
201		Operator "less" less
202			Operator_type.RELATIONAL false,
203		Operator "less_equal" less_equal
204			Operator_type.RELATIONAL false
205	];
206
207	matches = filter test_name operator_table;
208	test_name x = x.op_name == op_name;
209}
210
211/* Given an operator name, look up a function that implements that
212 * operator.
213 */
214oo_unary_lookup op_name
215	= matches?0, matches != []
216	= error (_ "unknown unary operator" ++ ": " ++ print op_name)
217{
218	operator_table = [
219		/* Operators.
220		 */
221		Operator "cast_signed_char" cast_signed_char
222		Operator_type.ARITHMETIC false,
223		Operator "cast_unsigned_char" cast_unsigned_char
224		Operator_type.ARITHMETIC false,
225		Operator "cast_signed_short" cast_signed_short
226		Operator_type.ARITHMETIC false,
227		Operator "cast_unsigned_short" cast_unsigned_short
228		Operator_type.ARITHMETIC false,
229		Operator "cast_signed_int" cast_signed_int
230		Operator_type.ARITHMETIC false,
231		Operator "cast_unsigned_int" cast_unsigned_int
232		Operator_type.ARITHMETIC false,
233		Operator "cast_float" cast_float
234		Operator_type.ARITHMETIC false,
235		Operator "cast_double" cast_double
236		Operator_type.ARITHMETIC false,
237		Operator "cast_complex" cast_complex
238		Operator_type.ARITHMETIC false,
239		Operator "cast_double_complex" cast_double_complex
240		Operator_type.ARITHMETIC false,
241		Operator "unary_minus" unary_minus
242		Operator_type.ARITHMETIC false,
243		Operator "negate" negate
244		Operator_type.RELATIONAL false,
245		Operator "complement" complement
246		Operator_type.ARITHMETIC false,
247		Operator "unary_plus" unary_plus
248		Operator_type.ARITHMETIC false,
249
250		/* Built in projections.
251 		 */
252		Operator "re" re Operator_type.ARITHMETIC false,
253		Operator "im" im Operator_type.ARITHMETIC false,
254		Operator "hd" hd Operator_type.ARITHMETIC false,
255		Operator "tl" tl Operator_type.ARITHMETIC false,
256
257		/* Maths builtins.
258		 */
259		Operator "sin" sin Operator_type.ARITHMETIC false,
260		Operator "cos" cos Operator_type.ARITHMETIC false,
261		Operator "tan" tan Operator_type.ARITHMETIC false,
262		Operator "asin" asin Operator_type.ARITHMETIC false,
263		Operator "acos" acos Operator_type.ARITHMETIC false,
264		Operator "atan" atan Operator_type.ARITHMETIC false,
265		Operator "log" log Operator_type.ARITHMETIC false,
266		Operator "log10" log10 Operator_type.ARITHMETIC false,
267		Operator "exp" exp Operator_type.ARITHMETIC false,
268		Operator "exp10" exp10 Operator_type.ARITHMETIC false,
269		Operator "ceil" ceil Operator_type.ARITHMETIC false,
270		Operator "floor" floor Operator_type.ARITHMETIC false
271	];
272
273	matches = filter test_name operator_table;
274	test_name x = x.op_name == op_name;
275}
276
277/* Find the matching methods in a method table.
278 */
279oo_method_lookup table = map (extract 0) (filter (extract 1) table);
280
281/* A binary op: a is a class, b may be a class ... eg. "add" a b
282
283   two obvious ways to find a method:
284
285   - a.oo_binary_search "add" (+) b
286   - b.oo_binary_search "add'" (converse (+)) a, is_class b
287
288   if these fail but op is a symmetric operator (eg. a + b == b + a), we can
289   also try reversing the args
290
291   - a.oo_binary_search "add'" (converse (+)) b
292   - b.oo_binary_search "add" (+) a, is_class b
293
294 */
295oo_binary_function op a b
296	= matches1?0,
297		matches1 != []
298	= matches2?0,
299		is_class b && matches2 != []
300	= matches3?0,
301		op.symmetric && matches3 != []
302	= matches4?0,
303		op.symmetric && is_class b && matches4 != []
304	= error (_ "No method found for binary operator." ++ "\n" ++
305		_ "left" ++ " = " ++ print a ++ "\n" ++
306		_ "operator" ++ " = " ++ op.op_name ++ "\n" ++
307		_ "right" ++ " = " ++ print b)
308{
309	matches1 = oo_method_lookup (a.oo_binary_table op b);
310	matches2 = oo_method_lookup (b.oo_binary_table (oo_converse op) a);
311	matches3 = oo_method_lookup (a.oo_binary_table (oo_converse op) b);
312	matches4 = oo_method_lookup (b.oo_binary_table op a);
313}
314
315/* A binary op: a is not a class, b is a class ... eg. "subtract" a b
316
317   only one way to find a method:
318
319   - b.oo_binary_search "subtract'" (converse (-)) a
320
321   if this fails but op is a symmetric operator (eg. a + b == b + a), we can
322   try reversing the args
323
324   - b.oo_binary_search "add" (+) a, is_class b
325
326 */
327oo_binary'_function op a b
328	= matches1?0,
329		matches1 != []
330	= matches2?0,
331		op.symmetric && matches2 != []
332	= error (_ "No method found for binary operator." ++ "\n" ++
333		_ "left" ++ " = " ++ print a ++ "\n" ++
334		_ "operator" ++ " = " ++ op.op_name ++ "\n" ++
335		_ "right" ++ " = " ++ print b)
336{
337	matches1 = oo_method_lookup (b.oo_binary_table (oo_converse op) a);
338	matches2 = oo_method_lookup (b.oo_binary_table op a);
339}
340
341oo_unary_function op x
342	= matches?0,
343		matches != []
344	= error (_ "No method found for unary operator." ++ "\n" ++
345		_ "operator" ++ " = " ++ op.op_name ++ "\n" ++
346		_ "argument" ++ " = " ++ print x)
347{
348	matches = oo_method_lookup (x.oo_unary_table op);
349}
350
351/* Base class for nip's built-in classes ... base check function, base
352 * operator overload functions.
353 */
354_Object = class {
355	check = check_args this;
356
357	_check_args = [];
358	_check_all = [];
359
360	/* Operator overloading stuff.
361	 */
362	oo_binary op x
363		= oo_binary_function (oo_binary_lookup op) this x;
364	oo_binary' op x
365		= oo_binary'_function (oo_binary_lookup op) x this;
366	oo_unary op
367		= oo_unary_function (oo_unary_lookup op) this;
368
369	/* Provide a fallback for class == thing ... just use pointer
370	 * equality.
371	 */
372	oo_binary_table op x = [
373		[pointer_equal this x,
374			op.op_name == "equal" || op.op_name == "equal'"],
375		[not_pointer_equal this x,
376			op.op_name == "not_equal" ||
377				op.op_name == "not_equal'"]
378	];
379	oo_unary_table op = [];
380}
381
382/* A list of things. Do automatic iteration of unary and binary operators on us.
383 */
384Group value = class
385	_Object {
386	_check_args = [
387		[value, "value", check_list]
388	];
389
390	// methods
391    oo_binary_table op x = [
392		[this.Group (map2 (apply2 op) value x.value),
393			is_Group x],
394		[this.Group (map (apply2 op' x) value),
395			true]
396	] ++ super.oo_binary_table op x
397	{
398		op' = oo_converse op;
399
400		apply2 op x1 x2
401			= oo_binary_function op x1 x2, is_class x1
402			= oo_binary'_function op x1 x2, is_class x2
403			= op.fn x1 x2;
404	};
405
406	oo_unary_table op = [
407		[this.Group (map apply value),
408			true]
409	] ++ super.oo_unary_table op
410	{
411		apply x
412			= oo_unary_function op x, is_class x
413			= op.fn x;
414	}
415}
416
417/* Single real number ... eg slider.
418 */
419Real value = class
420	_Object {
421	_check_args = [
422		[value, "value", check_real]
423	];
424
425	// methods
426	oo_binary_table op x = [
427		[this.Real (op.fn this.value x.value),
428			is_Real x &&
429			op.type == Operator_type.ARITHMETIC],
430		[this.Real (op.fn this.value x),
431			is_real x &&
432			op.type == Operator_type.ARITHMETIC],
433		[op.fn this.value x.value,
434			is_Real x &&
435			op.type == Operator_type.RELATIONAL],
436		[op.fn this.value x,
437			!is_class x]
438	] ++ super.oo_binary_table op x;
439
440	oo_unary_table op = [
441		[this.Real (op.fn this.value),
442			op.type == Operator_type.ARITHMETIC],
443		[op.fn this.value,
444			true]
445	] ++ super.oo_unary_table op;
446}
447
448/* Single bool ... eg Toggle.
449 */
450Bool value = class
451	_Object {
452	_check_args = [
453		[value, "value", check_bool]
454	];
455
456	// methods
457    oo_binary_table op x = [
458		[if value then x?0 else x?1,
459			op.op_name == "if_then_else"],
460		[this.Bool (op.fn this.value x.value),
461			is_Bool x],
462		[this.Bool (op.fn this.value x),
463			is_bool x]
464	] ++ super.oo_binary_table op x;
465
466    oo_unary_table op = [
467		[this.Bool (op.fn this.value),
468			op.type == Operator_type.ARITHMETIC ||
469			op.type == Operator_type.RELATIONAL]
470	] ++ super.oo_unary_table op;
471}
472
473/* An editable string.
474 */
475String caption value = class
476	_Object {
477	_check_args = [
478		[caption, "caption", check_string],
479		[value, "value", check_string]
480	];
481}
482
483/* An editable real number.
484 */
485Number caption value = class
486	scope.Real value {
487	_check_args = [
488		[caption, "caption", check_string]
489	];
490
491	Real x = this.Number caption x;
492}
493
494/* An editable expression.
495 */
496Expression caption expr = class
497	(if is_class expr then expr else _Object) {
498	_check_args = [
499		[caption, "caption", check_string],
500		[expr, "expr", check_any]
501	];
502}
503
504/* An editable filename.
505 */
506Pathname caption value = class
507	_Object {
508	_check_args = [
509		[caption, "caption", check_string],
510		[value, "value", check_string]
511	];
512}
513
514/* An editable fontname.
515 */
516Fontname caption value = class
517	_Object {
518	_check_args = [
519		[caption, "caption", check_string],
520		[value, "value", check_string]
521	];
522}
523
524/* Vector type ... just a finite list of real ... handy for wrapping an
525 * argument to eg. im_lintra_vec. Make it behave like a single pixel image.
526 */
527Vector value = class
528	_Object {
529	_check_args = [
530		[value, "value", check_real_list]
531	];
532
533	bands = len value;
534
535	// methods
536	oo_binary_table op x = [
537		// Vector ++ Vector means bandwise join
538		[this.Vector (op.fn this.value x.value),
539			is_Vector x &&
540			(op.op_name == "join" || op.op_name == "join'")],
541		[this.Vector (op.fn this.value [get_number x]),
542			has_number x &&
543			(op.op_name == "join" || op.op_name == "join'")],
544		// Vector ? number means extract element
545		[op.fn this.value (get_real x),
546			has_real x &&
547			(op.op_name == "subscript" ||
548				op.op_name == "subscript'")],
549		// extra check for lengths equal
550		[this.Vector (map_binary op.fn this.value x.value),
551			is_Vector x &&
552			len value == len x.value &&
553			op.type == Operator_type.ARITHMETIC],
554		[this.Vector (map_binary op.fn this.value (get_real x)),
555			has_real x &&
556			op.type == Operator_type.ARITHMETIC],
557
558		// need extra length check
559		[this.Vector (map bool_to_real
560			(map_binary op.fn this.value x.value)),
561			is_Vector x &&
562			len value == len x.value &&
563			op.type == Operator_type.RELATIONAL],
564		[this.Vector (map bool_to_real
565			(map_binary op.fn this.value (get_real x))),
566			has_real x &&
567			op.type == Operator_type.RELATIONAL],
568		[this.Vector (op.fn this.value x.value),
569			is_Vector x &&
570			len value == len x.value &&
571			op.type == Operator_type.COMPOUND_REWRAP],
572		[x.Image (vec op'.op_name x.value value),
573			is_Image x],
574		[vec op'.op_name x value,
575			is_image x],
576		[op.fn this.value x,
577			is_real x]
578	] ++ super.oo_binary_table op x
579	{
580		op' = oo_converse op;
581	};
582
583	oo_unary_table op = [
584		[this.Vector (map_unary op.fn this.value),
585			op.type == Operator_type.ARITHMETIC],
586		[this.Vector (map bool_to_real
587			(map_unary op.fn this.value)),
588			op.type == Operator_type.RELATIONAL],
589		[this.Vector (op.fn this.value),
590			op.type == Operator_type.COMPOUND_REWRAP],
591		[op.fn this.value,
592			true]
593	] ++ super.oo_unary_table op;
594
595	// turn an ip bool (or a number, for Vector) into VIPSs 255/0
596	bool_to_real x
597		= 255, is_bool x && x
598		= 255, is_number x && x != 0
599		= 0;
600}
601
602/* A rectangular array of real.
603 */
604Matrix_base value = class
605	_Object {
606	_check_args = [
607		[value, "value", check_matrix]
608	];
609
610	// calculate these from value
611	width = len value?0;
612	height = len value;
613
614	// methods
615	oo_binary_table op x = [
616		// mat multiply is special
617		[this.Matrix_base mul.value,
618			is_Matrix x &&
619			op.op_name == "multiply"],
620		[this.Matrix_base mul'.value,
621			is_Matrix x &&
622			op.op_name == "multiply'"],
623
624		// mat divide is also special
625		[this.Matrix_base div.value,
626			is_Matrix x &&
627			op.op_name == "divide"],
628		[this.Matrix_base div'.value,
629			is_Matrix x &&
630			op.op_name == "divide'"],
631
632		// power -1 means invert
633		[this.Matrix_base inv.value,
634			is_real x && x == -1 &&
635			op.op_name == "power"],
636		[this.Matrix_base sq.value,
637			is_real x && x == 2 &&
638			op.op_name == "power"],
639		[error "matrix **-1 and **2 only",
640			op.op_name == "power" ||
641			op.op_name == "power'"],
642
643		// matrix op vector ... treat a vector as a 1 row matrix
644		[this.Matrix_base (map (map_binary op'.fn x.value) this.value),
645			is_Vector x &&
646			op.type == Operator_type.ARITHMETIC],
647		[this.Matrix_base (map_binary op.fn this.value x.value),
648			(is_Matrix x || is_Real x) &&
649			op.type == Operator_type.ARITHMETIC],
650
651		[this.Matrix_base (map_binary op.fn this.value x),
652			is_real x &&
653			op.type == Operator_type.ARITHMETIC],
654
655		// compound ... don't do iteration
656		[this.Matrix_base (op.fn this.value x.value),
657			(is_Matrix x || is_Real x || is_Vector x) &&
658			op.type == Operator_type.COMPOUND_REWRAP]
659	] ++ super.oo_binary_table op x
660	{
661		mul = im_matmul this x;
662		mul' = im_matmul x this;
663		div = im_matmul this (im_matinv x);
664		div' = im_matmul x (im_matinv this);
665		inv = im_matinv this;
666		sq = im_matmul this this;
667		op' = oo_converse op;
668	}
669
670	oo_unary_table op = [
671		[this.Matrix_base (map_unary op.fn this.value),
672			op.type == Operator_type.ARITHMETIC],
673		[this.Matrix_base (op.fn this.value),
674			op.type == Operator_type.COMPOUND_REWRAP],
675		[op.fn this.value,
676			true]
677	] ++ super.oo_unary_table op;
678}
679
680/* How to display a matrix: text, sliders, toggles, or text plus scale/offset.
681 */
682Matrix_display = class {
683        text = 0;
684        slider = 1;
685        toggle = 2;
686        text_scale_offset = 3;
687
688        is_display = member [text, slider, toggle, text_scale_offset];
689}
690
691/* A matrix as VIPS sees them ... add scale, offset and filename. For nip, add
692 * a display type as well to control how the widget renders.
693 */
694Matrix_vips value scale offset filename display = class
695	scope.Matrix_base value {
696	_check_args = [
697		[scale, "scale", check_real],
698		[offset, "offset", check_real],
699		[filename, "filename", check_string],
700		[display, "display", check_matrix_display]
701	];
702
703	Matrix_base x = this.Matrix_vips x scale offset filename display;
704}
705
706/* A plain 'ol matrix which can be passed to VIPS.
707 */
708Matrix value = class
709	Matrix_vips value 1 0 "" Matrix_display.text {}
710
711/* Specialised constructors ... for convolutions, recombinations and
712 * morphologies.
713 */
714Matrix_con scale offset value = class
715	Matrix_vips value scale offset "" Matrix_display.text_scale_offset {};
716
717Matrix_rec value = class
718	Matrix_vips value 1 0 "" Matrix_display.slider {};
719
720Matrix_mor value = class
721	Matrix_vips value 1 0 "" Matrix_display.toggle {};
722
723Matrix_file filename = (im_read_dmask @ expand @ search) filename;
724
725/* A CIE colour ... a triple, plus a format (eg XYZ, Lab etc)
726 */
727Colour colour_space value = class
728	scope.Vector value {
729	_check_args = [
730		[colour_space, "colour_space", check_colour_space]
731	];
732	_check_all = [
733		[len value == 3, "len value == 3"]
734	];
735
736	Vector x = this.Colour colour_space x;
737
738	// make a colour-ish thing from an image
739	// back to Colour if we have another 3 band image
740	// to a vector if bands > 1
741	// to a number otherwise
742	itoc im
743		= this.Colour nip_type (to_matrix im).value?0,
744			bands == 3
745		= scope.Vector (map mean (bandsplit im)),
746			bands > 1
747		= mean im
748	{
749		type = im_header_int "Type" im;
750		bands = im_header_int "Bands" im;
751		nip_type = Image_type.colour_spaces.lookup 1 0 type;
752	}
753
754	// methods
755    oo_binary_table op x = [
756		[itoc (op.fn
757			((float) (to_image this).value)
758			((float) (to_image x).value)),
759			// here REWRAP means go via image
760			op.type == Operator_type.COMPOUND_REWRAP]
761	] ++ super.oo_binary_table op x;
762
763    oo_unary_table op = [
764		[itoc (op.fn ((float) (to_image this).value)),
765			op.type == Operator_type.COMPOUND_REWRAP]
766	] ++ super.oo_unary_table op;
767}
768
769// a subclass with widgets for picking a space and value
770Colour_picker default_colour default_value = class
771	Colour space.value_name colour.expr {
772	_vislevel = 3;
773
774	space = Option_enum Image_type.colour_spaces "Colour space" default_colour;
775	colour = Expression "Colour value" default_value;
776
777	Colour_edit colour_space value =
778		Colour_picker colour_space value;
779}
780
781/* Base scale type.
782 */
783Scale caption from to value = class
784	scope.Real value {
785	_check_args = [
786		[caption, "caption", check_string],
787		[from, "from", check_real],
788		[to, "to", check_real]
789	];
790	_check_all = [
791		[from < to, "from < to"]
792	];
793
794	Real x = this.Scale caption from to x;
795
796	// methods
797	oo_binary_table op x = [
798		[this.Scale caption (op.fn this.from x.from) (op.fn this.to x.to)
799			(op.fn this.value x.value),
800			is_Scale x &&
801			op.type == Operator_type.ARITHMETIC],
802		[this.Scale caption (op.fn this.from x) (op.fn this.to x)
803			(op.fn this.value x),
804			is_real x &&
805			op.type == Operator_type.ARITHMETIC]
806	] ++ super.oo_binary_table op x;
807}
808
809/* Compat. slider type.
810 */
811Slider = Scale "";
812
813/* Base toggle type.
814 */
815Toggle caption value = class
816	scope.Bool value {
817	_check_args = [
818		[caption, "caption", check_string],
819		[value, "value", check_bool]
820	];
821
822	Bool x = this.Toggle caption x;
823}
824
825/* Base option type.
826 */
827Option caption labels value = class
828	scope.Real value {
829	_check_args = [
830		[caption, "caption", check_string],
831		[labels, "labels", check_string_list],
832		[value, "value", check_uint]
833	];
834}
835
836Option_enum enum caption value_name = class
837	Option caption enum.names (index (equal value_name) enum.names) {
838	// corresponding thing
839	value_thing = enum.get_thing value_name;
840
841	Option_edit caption labels value
842		= this.Option_enum enum caption (enum.names ? value);
843}
844
845/* A rectangle. width and height can be -ve.
846 */
847Rect left top width height = class
848	_Object {
849	_check_args = [
850		[left, "left", check_real],
851		[top, "top", check_real],
852		[width, "width", check_real],
853		[height, "height", check_real]
854	];
855
856	// derived
857	right = left + width;
858	bottom = top + height;
859
860	oo_binary_table op x = [
861		[equal x,
862			is_Rect x &&
863			(op.op_name == "equal" || op.op_name == "equal'")],
864		[!equal x,
865			is_Rect x &&
866			(op.op_name == "not_equal" ||
867				op.op_name == "not_equal'")],
868
869		// binops with a complex are the same as (comp op comp)
870		[oo_binary_function op this (Rect (re x) (im x) 0 0),
871			is_complex x],
872
873		// all others are just pairwise
874		[this.Rect left' top' width' height',
875			is_Rect x &&
876			op.type == Operator_type.ARITHMETIC],
877		[this.Rect left'' top'' width'' height'',
878			has_number x &&
879			op.type == Operator_type.ARITHMETIC]
880	] ++ super.oo_binary_table op x
881	{
882		left' = op.fn left x.left;
883		top' = op.fn top x.top;
884		width' = op.fn width x.width;
885		height' = op.fn height x.height;
886
887		left'' = op.fn left x';
888		top'' = op.fn top x';
889		width'' = op.fn width x';
890		height'' = op.fn height x';
891		x' = get_number x;
892	}
893
894	oo_unary_table op = [
895		// arithmetic uops just map
896		[this.Rect left' top' width' height',
897			op.type == Operator_type.ARITHMETIC],
898
899		// compound uops are just like ops on complex
900		// do (width, height) so thing like abs(Arrow) work as you'd expect
901		[op.fn (width, height),
902			op.type == Operator_type.COMPOUND]
903	] ++ super.oo_unary_table op
904	{
905		left' = op.fn left;
906		top' = op.fn top;
907		width' = op.fn width;
908		height' = op.fn height;
909	}
910
911	// empty? ie. contains no pixels
912	is_empty = width == 0 || height == 0;
913
914	// normalised version, ie. make width/height +ve and flip the origin
915	nleft
916		= left + width, width < 0
917		= left;
918	ntop
919		= top + height, height < 0
920		= top;
921	nwidth = abs width;
922	nheight = abs height;
923	nright = nleft + nwidth;
924	nbottom = ntop + nheight;
925
926	equal x = left == x.left && top == x.top &&
927		  width == x.width && height == x.height;
928
929	// contains a point?
930	includes_point x y
931		= nleft <= x && x <= nright && ntop <= y && y <= nbottom;
932
933	// contains a rect? just test top left and bottom right points
934	includes_rect r
935		= includes_point r.nleft r.ntop &&
936			includes_point r.nright r.nbottom;
937
938	// bounding box of two rects
939	// if either is empty, can just return the other
940	union r
941		= r, is_empty
942		= this, r.is_empty
943		= Rect left' top' width' height'
944	{
945		left' = min_pair nleft r.nleft;
946		top' = min_pair ntop r.ntop;
947		width' = max_pair nright r.nright - left';
948		height' = max_pair nbottom r.nbottom - top';
949	}
950
951	// intersection of two rects ... empty rect if no intersection
952	intersect r
953		= Rect left' top' width'' height''
954	{
955		left' = max_pair nleft r.nleft;
956		top' = max_pair ntop r.ntop;
957		width' = min_pair nright r.nright - left';
958		height' = min_pair nbottom r.nbottom - top';
959		width''
960			= width', width > 0
961			= 0;
962		height''
963			= height', height > 0
964			= 0;
965	}
966
967	// expand/collapse by n pixels
968	margin_adjust n
969		= Rect (left - n) (top - n) (width + 2 * n) (height + 2 * n);
970}
971
972/* Values for Compression field in image.
973 */
974Image_compression = class {
975	NO_COMPRESSION = 0;
976	TCSF_COMPRESSION = 1;
977	JPEG_COMPRESSION = 2;
978	LABPACK_COMPRESSED = 3;
979	RGB_COMPRESSED = 4;
980	LUM_COMPRESSED = 5;
981}
982
983/* Values for Coding field in image.
984 */
985Image_coding = class {
986	NOCODING = 0;
987	COLQUANT = 1;
988	LABPACK = 2;
989}
990
991/* Values for BandFmt field in image.
992 */
993Image_format = class {
994	DPCOMPLEX = 9;
995	DOUBLE = 8;
996	COMPLEX = 7;
997	FLOAT = 6;
998	INT = 5;
999	UINT = 4;
1000	SHORT = 3;
1001	USHORT = 2;
1002	CHAR = 1;
1003	UCHAR = 0;
1004	NOTSET = -1;
1005
1006	maxval fmt
1007		= [
1008			255,		// UCHAR
1009			127,		// CHAR
1010			65535,		// USHORT
1011			32767,		// SHORT
1012			4294967295,	// UINT
1013			2147483647,	// INT
1014			255,		// FLOAT
1015			255,		// COMPLEX
1016			255,		// DOUBLE
1017			255		// DPCOMPLEX
1018		] ? fmt, fmt >= 0 && fmt <= DPCOMPLEX
1019		= error (_ "bad value for BandFmt");
1020}
1021
1022/* A lookup table.
1023 */
1024Table value = class
1025	_Object {
1026	_check_args = [
1027		[value, "value", check_rectangular]
1028	];
1029
1030	/* present col x: is there an x in column col
1031	 */
1032	present col x = member (map (extract col) value) x;
1033
1034	/* Look on column from, return matching item in column to.
1035	 */
1036	lookup from to x
1037		= value?n?to, n >= 0
1038		= error (_ "item" ++ " " ++ print x ++ " " ++ _ "not in table")
1039	{
1040		n = index (equal x) (map (extract from) value);
1041	}
1042}
1043
1044/* A two column lookup table with the first column a string and the second a
1045 * thing. Used for representing various enums. Option_enum makes a selector
1046 * from one of these.
1047 */
1048Enum value = class
1049	Table value {
1050	_check_args = [
1051		[value, "value", check_enum]
1052	]
1053	{
1054		check_enum = [is_enum, _ "is [[char, *]]"];
1055		is_enum x =
1056			is_rectangular x &&
1057			is_listof is_string (map (extract 0) x);
1058	}
1059
1060	// handy ... all the names and things as lists
1061	names = map (extract 0) value;
1062	things = map (extract 1) value;
1063
1064	// is a legal name or thing
1065	has_name x = this.present 1 x;
1066	has_thing x = this.present 0 x;
1067
1068	// map things to strings and back
1069	get_name x = this.lookup 1 0 x;
1070	get_thing x = this.lookup 0 1 x;
1071}
1072
1073/* Type field.
1074 */
1075Image_type = class {
1076	FOURIER = 24;
1077	YXY = 23;
1078	sRGB = 22;
1079	LABS = 21;
1080	LCH = 19;
1081	UCS = 18;
1082	RGB = 17;
1083	LABQ = 16;
1084	CMYK = 15;
1085	CMC = 14;
1086	LAB = 13;
1087	XYZ = 12;
1088	LUT = 11;
1089	HISTOGRAM = 10;
1090	POWER_SPECTRUM = 9;
1091	BLUE_ONLY = 8;
1092	GREEN_ONLY = 7;
1093	RED_ONLY = 6;
1094	YUV = 5;
1095	IR = 4;
1096	XRAY = 3;
1097	LUMINACE = 2;
1098	B_W = 1;
1099	MULTIBAND = 0;
1100
1101	/* Table to get names <-> numbers.
1102	 */
1103	type_names = Enum [
1104		["FOURIER", FOURIER],
1105		["YXY", YXY],
1106		["sRGB", sRGB],
1107		["LABS", LABS],
1108		["LCH", LCH],
1109		["UCS", UCS],
1110		["RGB", RGB],
1111		["LABQ", LABQ],
1112		["CMYK", CMYK],
1113		["CMC", CMC],
1114		["LAB", LAB],
1115		["XYZ", XYZ],
1116		["LUT", LUT],
1117		["HISTOGRAM", HISTOGRAM],
1118		["POWER_SPECTRUM", POWER_SPECTRUM],
1119		["BLUE_ONLY", BLUE_ONLY],
1120		["GREEN_ONLY", GREEN_ONLY],
1121		["RED_ONLY", RED_ONLY],
1122		["YUV", YUV],
1123		["IR", IR],
1124		["XRAY", XRAY],
1125		["LUMINACE", LUMINACE],
1126		["B_W", B_W],
1127		["MULTIBAND", MULTIBAND]
1128	];
1129
1130	/* Table relating nip's colour space names and VIPS's Type numbers.
1131	 * Options generated from this, so match the order to the order in the
1132	 * Colour menu.
1133	 */
1134	colour_spaces = Enum [
1135		["sRGB", sRGB],
1136		["Lab", LAB],
1137		["LCh", LCH],
1138		["XYZ", XYZ],
1139		["Yxy", YXY],
1140		["UCS", UCS]
1141	];
1142
1143	/* A slightly larger table ... the types of colorimetric image we can
1144	 * have. Add mono, and the S and Q forms of LAB.
1145	 */
1146	image_colour_spaces = Enum [
1147		["Mono", B_W],
1148		["sRGB", sRGB],
1149		["Lab", LAB],
1150		["LabQ", LABQ],
1151		["LabS", LABS],
1152		["LCh", LCH],
1153		["XYZ", XYZ],
1154		["Yxy", YXY],
1155		["UCS", UCS]
1156	];
1157}
1158
1159/* Base image type. Simple layer over vips_image.
1160 */
1161Image value = class
1162	_Object {
1163	_check_args = [
1164		[value, "value", check_image]
1165	];
1166
1167	// fields from VIPS header
1168	width = get_width value;
1169	height = get_height value;
1170	bands = get_bands value;
1171	format = get_format value;
1172	bits = get_bits value;
1173	coding = get_coding value;
1174	type = get_type value;
1175	xres = im_header_double "Xres" value;
1176	yres = im_header_double "Yres" value;
1177	xoffset = im_header_int "Xoffset" value;
1178	yoffset = im_header_int "Yoffset" value;
1179	filename = im_header_string "filename" value;
1180
1181	// convenience ... the area our pixels occupy, as a rect
1182	rect = Rect 0 0 width height;
1183
1184	// operator overloading
1185	// (op Image Vector) done in Vector class
1186	oo_binary_table op x = [
1187		// handle image ++ constant here
1188        [wrap join_result_image,
1189			(has_real x || is_Vector x) &&
1190			(op.op_name == "join" || op.op_name == "join'")],
1191		// image ++ image is slightly different ... we want to
1192		// sizealike, but we must not bandalike
1193        [wrap
1194			(op.fn (get_image resized?0) (get_image resized?1)),
1195			has_image x &&
1196			(op.op_name == "join" || op.op_name == "join'")],
1197        [wrap ite_result_image,
1198			op.op_name == "if_then_else"],
1199		// arithmetic and reational binops between image resize
1200		// and band_alike images to match
1201		[wrap
1202			(op.fn (get_image rebanded?0) (get_image rebanded?1)),
1203			has_image x &&
1204			(op.type == Operator_type.ARITHMETIC ||
1205				op.type == Operator_type.RELATIONAL)],
1206		// other op types don't resize
1207		[wrap (op.fn this.value (get_image x)),
1208			has_image x],
1209		[wrap (op.fn this.value (get_number x)),
1210			has_number x],
1211		[wrap (op.fn this.value x),
1212			true]
1213	] ++ super.oo_binary_table op x
1214	{
1215		// wrap the result with this ... only skip rewrap for COMPOUND
1216		wrap
1217			= id, op.type == Operator_type.COMPOUND
1218			= this.Image;
1219
1220		join_result_image
1221			= value ++ new_stuff, op.op_name == "join"
1222			= new_stuff ++ value
1223		{
1224			new_stuff = image_new width height new_bands
1225				format
1226				coding
1227				Image_type.B_W x xoffset yoffset;
1228			new_bands
1229				= get_bands x, has_bands x
1230				= 1;
1231		}
1232
1233		then_part = x?0;
1234		else_part = x?1;
1235
1236		// get things about our output from inputs in this order
1237		objects = [then_part, else_part, this];
1238
1239		// properties of our output image
1240		target_bands = get_member_list has_bands get_bands objects;
1241		target_format = get_member_list has_format get_format objects;
1242		target_type = get_member_list has_type get_type objects;
1243
1244		to_image x
1245			= x, is_image x
1246			= x.value, is_Image x
1247			= black + x
1248		{
1249			black = im_black width height target_bands;
1250		}
1251
1252		then_image = to_image then_part;
1253		else_image = to_image else_part;
1254
1255		then_image' = clip2fmt target_format then_image;
1256		else_image' = clip2fmt target_format else_image;
1257
1258		ite_resized = size_alike [value, then_image', else_image'];
1259
1260		ite_result_image = image_set_type target_type
1261			(if ite_resized?0 then ite_resized?1 else ite_resized?2);
1262
1263		resized = size_alike [this, x];
1264		rebanded = bands_alike resized;
1265	}
1266
1267	// FIXME ... yuk ... don't use operator hints, just always rewrap if
1268	// we have an image result
1269	// forced on us by things like abs:
1270	// 	abs Vector -> real
1271	//	abs Image -> Image
1272	// does not fit well with COMPOUND/whatever scheme
1273	oo_unary_table op = [
1274		[this.Image result,
1275			is_image result],
1276		[result,
1277			true]
1278	] ++ super.oo_unary_table op
1279	{
1280		result = op.fn this.value;
1281	}
1282}
1283
1284/* Construct an image from a file.
1285 */
1286Image_file filename = class
1287	Image value {
1288	_check_args = [
1289		[filename, "filename", check_string]
1290	];
1291
1292	value = vips_image filename;
1293}
1294
1295Region image left top width height = class
1296	Image value {
1297	_check_args = [
1298		[image, "Image", check_Image],
1299		[left, "left", check_real],
1300		[top, "top", check_real],
1301		[width, "width", check_preal],
1302		[height, "height", check_preal]
1303	];
1304
1305	// a rect for our coordinates
1306	// region.rect gets the rect for the extracted image
1307	region_rect = Rect left top width height;
1308
1309	// we need to always succeed ... value is our enclosing image if we're
1310	// out of bounds
1311	value
1312		= extract_area left top width height image.value,
1313			image.rect.includes_rect region_rect
1314		= image.value;
1315}
1316
1317Area image left top width height = class
1318	scope.Region image left top width height {
1319	Region image left top width height
1320		= this.Area image left top width height;
1321}
1322
1323Arrow image left top width height = class
1324	scope.Rect left top width height {
1325	_check_args = [
1326		[image, "Image", check_Image],
1327		[left, "left", check_real],
1328		[top, "top", check_real],
1329		[width, "width", check_real],
1330		[height, "height", check_real]
1331	];
1332
1333	Rect l t w h = this.Arrow image l t w h;
1334}
1335
1336HGuide image top = class
1337	scope.Arrow image image.rect.left top image.width 0 {
1338	Arrow image left top width height = this.HGuide image top;
1339}
1340
1341VGuide image left = class
1342	scope.Arrow image left image.rect.top 0 image.height {
1343	Arrow image left top width height = this.VGuide image left;
1344}
1345
1346Mark image left top = class
1347	scope.Arrow image left top 0 0 {
1348	Arrow image left top width height = this.Mark image left top;
1349}
1350
1351// convenience functions: ... specify position as [0 .. 1)
1352
1353Region_relative image u v w h
1354	= Region image
1355		(image.width * u)
1356		(image.height * v)
1357		(image.width * w)
1358		(image.height * h);
1359
1360Area_relative image u v w h
1361	= Area image
1362		(image.width * u)
1363		(image.height * v)
1364		(image.width * w)
1365		(image.height * h);
1366
1367Arrow_relative image u v w h
1368	= Arrow image
1369		(image.width * u)
1370		(image.height * v)
1371		(image.width * w)
1372		(image.height * h);
1373
1374VGuide_relative image v
1375	= VGuide image (image.height * v);
1376
1377HGuide_relative image u
1378	= HGuide image (image.width * u);
1379
1380Mark_relative image u v
1381	= Mark image
1382		(image.width * u)
1383		(image.height * v);
1384
1385Interpolate = class {
1386	NEAREST_NEIGHBOUR = 0;
1387	BILINEAR = 1;
1388	BICUBIC = 2;
1389
1390	/* Table to map interpol numbers to descriptive strings
1391	 */
1392	names = Enum [
1393		[_ "Nearest neighbour", NEAREST_NEIGHBOUR],
1394		[_ "Bilinear", BILINEAR],
1395		[_ "Bicubic", BICUBIC]
1396	];
1397}
1398
1399Render_intent = class {
1400	PERCEPTUAL = 0;
1401	RELATIVE = 1;
1402	SATURATION = 2;
1403	ABSOLUTE = 3;
1404
1405	/* Table to get names <-> numbers.
1406	 */
1407	names = Enum [
1408		[_ "Perceptual", PERCEPTUAL],
1409		[_ "Relative", RELATIVE],
1410		[_ "Saturation", SATURATION],
1411		[_ "Absolute", ABSOLUTE]
1412	];
1413}
1414
1415// abstract base class for toolkit menus
1416Menu = class {}
1417
1418// a "----" line in a menu
1419Menuseparator = class Menu {}
1420
1421// abstract base class for items in menus
1422Menuitem label tooltip = class Menu {}
1423
1424Menupullright label tooltip = class Menuitem label tooltip {}
1425
1426Menuaction label tooltip = class Menuitem label tooltip {}
1427
1428