////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
Frame_item = class
	Menupullright "Picture _Frame" "working with images of frames"
	{
	////////////////////////////////////////////////////////////////////////////////////
	Build_frame_item = class
	Menupullright "_Build Frame From" "builds a new frame from image a and places it around image b"
		{
		////////////////////////////////////////////////////////////////////////////////////
		Frame_corner_item = class
			Menuaction "_Frame Corner"
			"copies and extends a frame corner, a, to produce a complete frame to fit round a given image, b"
			{
			prefs = Workspaces.Preferences;

			action a b = class
				_result
			{
			_check_args = [
				[a, "a", check_Image],
				[b, "b", check_Image]
			];
			_check_all = [
				[a.coding == b.coding && a.bands == b.bands,
					"a.coding == b.coding && a.bands == b.bands"]
			];
			_vislevel = 3;

			ppcm = Expression "Number of pixels per cm" 25;

			/* Given the value of ppcm, distance between the inner edge of the frame
			 * and the outer edge of the image.  +ve values mean the frame overlaps
			 * the image.
			 */
			overlap = Expression "Size of frame overlap in cm" 0;
			variables = Frame_variables 0;

			_type = Image_type.colour_spaces.get_name b.type;

			//If applied the count colour be seen for -ve values of overlap
			mount_options = Mount_options _type ppcm.expr;

			_cs = variables.corner_section.value;
			_ms = variables.middle_section.value;
			_ov = ppcm.expr * overlap.expr;
			_sf = variables.scale_factor.expr;
			_bf = variables.blend_fraction.value;

			//Scale frame image if required.
			_a = a, _sf == 1;
			   = a, _sf == 0;
			   = Image (resize Kernel_linear _sf _sf a.value);

			_im_w = b.width;
			_im_h = b.height + mount_options._los, mount_options.apply
				  = b.height;
			_os = mount_options._los, mount_options.apply
				= 0;
			_cl = Vector mount_options.mount_colour.value, mount_options.apply
				= 0;

			//Produce scaled and resized frame.
			frame = corner_frame _a _im_w _im_h _ov _cs _ms _bf;
			//Resize image canvas and applied mount colour as required.
			_pos_im = frame_position_image b frame _os _cl;
			//Wrap frame round image.
			_result = if (frame == 0) then _pos_im else frame;
			}
			}

		////////////////////////////////////////////////////////////////////////////////////
		Simple_frame_item = class
			Menuaction "_Simple Frame"
				"extends or shortens the central sections of a simple frame, a,  to fit round a given image, b"
			{
			prefs = Workspaces.Preferences;

			action a b = class
			_result
			{
			_check_args = [
				[a, "a", check_Image],
				[b, "b", check_Image]
				];
			_check_all = [
				[a.coding == b.coding && a.bands == b.bands,
					"a.coding == b.coding && a.bands == b.bands"]
				];
			_vislevel = 3;


			ppcm = Expression "Number of pixels per cm" 25;

			/* Given the value of ppcm, distance between the inner edge of the frame
			 * and the outer edge of the image.  +ve values mean the frame overlaps
			 * the image.
			 */
			overlap = Expression "Size of frame overlap in cm" 0;
			variables = Frame_variables 0;

			_type = Image_type.colour_spaces.get_name b.type;
			//If applied the count colour be seen for -ve values of overlap
			mount_options = Mount_options _type ppcm.expr;

			_cs = variables.corner_section.value;
			_ms = variables.middle_section.value;
			_ov = ppcm.expr * overlap.expr;
			_sf = variables.scale_factor.expr;
			_bf = variables.blend_fraction.value;

			//Scale frame image if required.
			_a = a, _sf == 1;
			   = a, _sf == 0;
			   = Image (resize Kernel_linear _sf _sf a.value);

			_im_w = b.width;
			_im_h = b.height + mount_options._los, mount_options.apply
				  = b.height;
			_os = mount_options._los, mount_options.apply
				= 0;
			_cl = Vector mount_options.mount_colour.value, mount_options.apply
				= 0;

			//Produce scaled and resized frame.
			frame = simple_frame _a _im_w _im_h _ov _cs _ms _bf variables.option;
			//Resize image canvas and applied mount colour as required.
			_pos_im = frame_position_image b frame _os _cl;
			//Wrap frame round image.
			_result = if (frame == 0) then _pos_im else frame;
			}
			}

		////////////////////////////////////////////////////////////////////////////////////
		Complex_frame_item = class
			Menuaction "_Complex Frame"
			"extends or shortens the central sections of a frame a, preserving any central edge details, to fit image b" {
		prefs = Workspaces.Preferences;

		action a b = class
			_result
			{
			_check_args = [
				[a, "a", check_Image],
				[b, "b", check_Image]
				];
			_check_all = [
				[a.coding == b.coding && a.bands == b.bands,
					"a.coding == b.coding && a.bands == b.bands"]
				];
			_vislevel = 3;

			ppcm = Expression "Number of pixels per cm" 25;

			/* Given the value of ppcm, distance between the inner edge of the frame
			 * and the outer edge of the image.  +ve values mean the frame overlaps
			 * the image.
			 */
			overlap = Expression "Size of frame overlap in cm" 0;
			variables = Frame_variables 1;

			_type = Image_type.colour_spaces.get_name b.type;

			//If applied the count colour be seen for -ve values of overlap
			mount_options = Mount_options _type ppcm.expr;

			_cs = variables.corner_section.value;
			_es = variables.edge_section.value;
			_ms = variables.middle_section.value;
			_ov = ppcm.expr * overlap.expr;
			_sf = variables.scale_factor.expr;
			_bf = variables.blend_fraction.value;

			_a = a, _sf == 1;
			   = a, _sf == 0;
			   = Image (resize Kernel_linear _sf _sf a.value);

			_im_w = b.width;
			_im_h = b.height + mount_options._los, mount_options.apply
				  = b.height;
			_os = mount_options._los, mount_options.apply
				= 0;
			_cl = Vector mount_options.colour.value, mount_options.apply
				= 0;


			//Produce scaled and resized frame.
			frame = complex_frame _a _im_w _im_h _ov _cs _es _ms _bf variables.option;
			//Resize image canvas and applied mount colour as required.
			_pos_im = frame_position_image b frame _os _cl;
			//Wrap frame round image.
			_result = if (frame == 0) then _pos_im else frame;
			}
		}
	}
////////////////////////////////////////////////////////////////////////////////////
	Straighten_frame_item = class
		Menuaction "_Straighten Frame" "uses four points to square up distorted images of frames" {
		action a = Perspective_item.action a;
		}
};

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
Select_item = class
	Menupullright "_Select"
		"select user defined areas of an image" {
	prefs = Workspaces.Preferences;

	/* Option toggle used to define whether the user is replacing a
	 * dark or a light area.
	 */
	_control = Option "Make" [
		"Selection Brighter",
	 	"Selection Darker",
	 	"Selection Black",
	 	"Selection White",
	 	"Background Black",
	 	"Background White",
		"Mask"
	] 4;

	control_selection mask im no
		= [
			if mask then im * 1.2 else im * 1,
			if mask then im * 0.8 else im * 1,
			if mask then 0 else im,
			if mask then 255 else im,
			if mask then im else 0,
			if mask then im else 255,
			mask
		]?no;

	Rectangle = class
        Menuaction "_Rectangle"
            "use an Arrow or Region x to define a rectangle"
        {
        action x = class
            _result {
            _vislevel = 3;

            control = _control;

            _result = control_selection mask im control
                  {
                	im = x.image;
                	mask = Image m
                    {
						rx
							= x.region_rect, is_Region x
							= x;
						b     = image_new im.width im.height 1 0 0 1 0 0 0;
						w     = image_new rx.nwidth rx.nheight 1 0 0 1 255 0 0;
						m     = insert_noexpand rx.nleft rx.ntop w b;
                     }
                   }
            }
        }

	Elipse = class
		Menuaction "_Ellipse"
			"use a line/arrow x to define the center point radius and direction of an ellipse"
		{
		action x = class
			_result {
			_vislevel = 3;

			control = _control;
			width = Scale "Width" 0.01 1 0.5;

			_result = control_selection mask im control
  				{
				mask = select_ellipse x width.value;
				im = x.image;
				}
			}
		}

	Tetragon = class
		Menuaction "_Tetragon"
			"selects the convex area defined by four points"
		{
		action a b c d = class
			_result {
			_vislevel = 3;

			control = _control;

			_result = control_selection mask im control
				{
				mask = select_tetragon a b c d;
				im = get_image a;
				}
			}

		}

	Polygon = class
		Menuaction "_Polygon"
			"selects a polygon from an ordered group of points"
		{
		action pt_list = class
			_result {
			_vislevel = 3;

			control = _control;

			_result = control_selection mask im control
				{
				mask = select_polygon pt_list;
				im = get_image ((pt_list.value)?0);
				}
			}
		}

	sep1 = Menuseparator;

	Threshold_item = class
		Menuaction "Thres_hold" "simple image threshold" {
		action x = class
			_result {
			_vislevel = 3;

			t
				= Scale "Threshold" 0 mx (mx / 2)
			{
				mx
					= Image_format.maxval x.format, is_Image x
					= 255;
			}

			_result = map_unary (more t.value) x;
		}
	}

	Threshold_percent_item = class
		Menuaction "Per_cent Threshold" "threshold at a percentage of pixels" {
		action x = class
			_result {
			_vislevel = 3;

			t = Scale "Percentage of pixels" 0 100 50;

			_result
				= map_unary (more (hist_thresh (t.value / 100) x)) x;
		}
	}

	sep2 = Menuseparator;

	Segment_item = class
		Menuaction "_Segment" "break image into disjoint regions" {
		action x = class
			_result {
			_vislevel = 3;

			segments
				= Expression "Number of disjoint regions"
					(map_unary (get_header "n-segments") _result);

			_result = map_unary segment x;
		}
	}

	Fill_item = class
		Menuaction "_Fill" "fill zero pixels with the nearest non-zero" {
		action x = class
			Image _result {
			_vislevel = 3;

			distance = Image _distance;

			[_result, _distance] = vips_call9 "fill_nearest" [x.value] [
				"distance" => true
			];
		}
	}

fill_nearest x
	= oo_unary_function nearest_op x, is_class x
	= near x, is_image x
	= error (_ "bad arguments to " ++ "fill_nearest")
{
	nearest_op = Operator "fill_nearest"
		fill_nearest Operator_type.COMPOUND_REWRAP false;

	near x
		= [out, distance]
	{
		[out, distance] = vips_call9 "fill_nearest" [x] [
			"distance" => true
		];
	}
}





	};

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

Perspective_match_item = class
	Menuaction "_Perspective Match"
		"rotate, scale and skew one image to match another" {
	action x y = class
		_result {
		_vislevel = 3;

		// try to find an image ... for a group, get the first item
		find_image x
			= x, is_Image x
			= find_image x?0, is_list x
			= find_image x.value, is_class x && has_value x
			= error "unable to find image";

		_a = find_image x;
		_b = find_image y;

		ap1 = Mark_relative _a 0.1 0.1;
		ap2 = Mark_relative _a 0.9 0.1;
		ap3 = Mark_relative _a 0.1 0.9;
		ap4 = Mark_relative _a 0.9 0.9;

		bp1 = Mark_relative _b 0.1 0.1;
		bp2 = Mark_relative _b 0.9 0.1;
		bp3 = Mark_relative _b 0.1 0.9;
		bp4 = Mark_relative _b 0.9 0.9;

		_result = map_binary process x y
			{
			f1 = _a.width / _b.width;
			f2 = _a.height / _b.height;

			rl = sort_pts_clockwise [ap1, ap2, ap3, ap4];
			pl = sort_pts_clockwise [bp1, bp2, bp3, bp4];

			to = [
				rl?0.left, rl?0.top,
				rl?1.left, rl?1.top,
				rl?2.left, rl?2.top,
				rl?3.left, rl?3.top
			];

			from = [
				pl?0.left * f1, pl?0.top * f2,
				pl?1.left * f1, pl?1.top * f2,
				pl?2.left * f1, pl?2.top * f2,
				pl?3.left * f1, pl?3.top * f2
			];

			trans = perspective_transform to from;

			process a b = transform 1 0 trans b2
				{
				b2 = resize Kernel_linear f1 f2 b, (f1 >= 1 && f2 >= 1) || (f1 >= 1 && f2 >= 1)
			    	= resize Kernel_linear f1 1 b1
					{b1 = resize Kernel_linear 1 f2 b;}
				}
			}
		}
	};

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
Perspective_item = class
	Menuaction "Pe_rspective Distort"
		"rotate, scale and skew an image with respect to defined points" {
	action x = class
		_result	{
		_vislevel = 3;

		// try to find an image ... for a group, get the first item
		find_image x
			= x, is_Image x
			= find_image x?0, is_list x
			= find_image x.value, is_class x && has_value x
			= error "unable to find image";

		_a = find_image x;

		dir = Option "Select distort direction" [ "Distort to points", "Distort to corners" ] 1;
		ap1 = Mark_relative _a 0.1 0.1;
		ap2 = Mark_relative _a 0.9 0.1;
		ap3 = Mark_relative _a 0.9 0.9;
		ap4 = Mark_relative _a 0.1 0.9;

		_result = map_unary process x
			{
			trans = [perspective_transform to from, perspective_transform from to]?(dir.value)
				{
				rl = sort_pts_clockwise [ap1, ap2, ap3, ap4];
				to = [(rl?0).left, (rl?0).top, (rl?1).left, (rl?1).top,
			    	  (rl?2).left, (rl?2).top, (rl?3).left, (rl?3).top];
				from=[0, 0, (_a.width - 1), 0,
					  (_a.width - 1), (_a.height - 1), 0, (_a.height - 1)];
				}

			process a = transform 1 0 trans a;
			}
		}
	};

