void ClipToPositive(const Vec3F& line,
										const Vec3F& clip,
										Vec3F& start,
										Vec3F& end) {
	const Vec3F isct = Cross3D(line, clip);
	if (PointSign(line, start) == -1) {
		start = isct;
	}
	if (PointSign(line, end) == -1) {
		end = isct;
	}
}
void ClipLineToPoly(const Vec3F& line,
										const vector<Vec3F>& poly,
										Vec3F& a,
										Vec3F& b) {
	a.Fill(0.0);
	b.Fill(0.0);
	const int n = poly.size();
	INDENTED for (int i = 1; i <= n; i++) {
		const Vec3F isct = Cross3D(line, poly[i%n]);
		const float dp1 = PointSign(isct, poly[ (i-1)%n ]);
		const float dp2 = PointSign(isct, poly[ (i+1)%n ]);
		if (dp1 >= 0 && dp2 >= 0) {
			b = a;
			a = isct;
		}
	}
}
void HehFeatureGen::Compute(const ImageRGB<byte>& imagergb,
														const ImageF& image,
														const MatI& seg) {
	// General notes:

	//   We must always be careful to deal with normalized image
	//   coordinates, which range from 0 to 1 in both dimensions. This
	//   includes pixel locations, vanishing point locations, and line
	//   intersections.

	//   Any homogeneous vector can represent a point at infinity, which
	//   will manifest as an INF if we Project() it. This is fine so
	//   long as we keep track of which coordinates might potentially be
	//   INF and only do sensible things with them -- e.g. no sqrt(),
	//   atan2(), division between two variables that can both be INF,
	//   etc.

	const int w = image.GetWidth();
	const int h = image.GetHeight();
	const int image_size = w*h;

	// Run the filter bank
	// TODO: implement LM filters. Here we just use 12 Gabor filters.
	vector<shared_ptr<ImageF> > responses;
	FilterBank filterbank(1);  // *** should be 3 scales
	MakeGaborFilters(4, &filterbank.filters);
	filterbank.Run(image, &responses);

	//
	// Find lines and compute vanishing points
	//
	vpts.Compute(image);
	const int num_lines = vpts.lines.segments.size();
	const int num_vpts = vpts.vanpts.size();

	//
	// Pre-process the line segments
	//

	// Whether each pair of lines is nearly parallel
	MatI is_nearly_parallel(num_lines, num_lines);
	// Whether the intersection of each pair is right of center
	MatI isct_is_right(num_lines, num_lines);
	// Whether the intersection of each pair is below center
	MatI isct_is_bottom(num_lines, num_lines);
	// Histogram bin index for intersection of each pair, or -1 if outside
	MatI isct_bin(num_lines, num_lines);

	// Compute line intersections
	for (int i = 0; i < num_lines; i++) {
		const LineSegment& segi = *vpts.lines.segments[i];
		is_nearly_parallel[i][i] = 0;
		for (int j = 1; j < num_lines; j++) {
			const LineSegment& segj = *vpts.lines.segments[j];

			// Parallel ?
			float dtheta = segi.theta - segj.theta;
			dtheta = min(dtheta, M_PI-dtheta);
			is_nearly_parallel[i][j] = (dtheta <= kParallelThresh ? 1 : 0);

			if (is_nearly_parallel[i][j]) {
				// Intersection position relative to center
				Vec3 isct = Cross3D(segi.line, segj.line);
				isct[0] /= w;  // can be +/- INF
				isct[1] /= h;  // can be +/- INF
				const float eucl_x = Project(isct)[0];
				const float eucl_y = Project(isct)[1];
				isct_is_right[i][j] = eucl_x > 0.5;
				isct_is_bottom[i][j] = eucl_y > 0.5;

				// Compute distance from centre
				// don't take sqrt(radius_sqr) because radius_sqr can be INF
				const float radius_sqr = eucl_x*eucl_x + eucl_y*eucl_y;
				int rad_bin = -1;
				if (radius_sqr > kFarRadius*kFarRadius) {
					rad_bin = 1;
				} else if (radius_sqr > kNearRadius*kNearRadius) {
					rad_bin = 0;
				}

				// Compute orientation from centre
				if (rad_bin == -1) {
					isct_bin[i][j] = -1;
				} else {
					// Must do atan2 with homogeneous coords. It will stay sane in
					// the case of points-at-infinity
					float phi = atan2(isct[1] - 0.5*isct[2],
															isct[0] - 0.5*isct[2]);
					int phi_bin = static_cast<int>(phi*M_PI/kNumOrientBins)%kNumOrientBins;
					int bin = rad_bin * kNumOrientBins + phi_bin;
					isct_bin[i][j] = bin;
				}
			}

			is_nearly_parallel[j][i] = is_nearly_parallel[i][j];
			isct_is_right[j][i] = isct_is_right[i][j];
			isct_is_bottom[j][i] = isct_is_bottom[i][j];
			isct_bin[j][i] = isct_bin[i][j];
		}
	}

	// 
	// Pre-process vanishing points
	//
	enum VanPointCla {
		kVptNone,
		kVptHoriz,
		kVptVert
	};
	const float h_vpt_min = 0.5-kHorizVptThresh;
	const float h_vpt_max = 0.5+kHorizVptThresh;
	const float v_vpt_low = 0.5-kVertVptThresh;
	const float v_vpt_high = 0.5+kVertVptThresh;
	int num_hvpts = 0;
	float sum_hvpts_y = 0;
	vector<VectorFixed<2,float> > norm_vpts;  // can contain INF
	VecI vanpt_cla(vpts.vanpts.size(), kVptNone);
	COUNTED_FOREACH(int i, const VecD& vpt, vpts.vanpts) {
		const float eucl_x = Project(vpt)[0] / w;  // can be +/-INF
		const float eucl_y = Project(vpt)[1] / h;  // can be +/-INF

		// Classify vanishing point as horizontal, vertical, or none
		norm_vpts.push_back(MakeVector<2,float>(eucl_x, eucl_y));
		if (eucl_y > h_vpt_min &&	eucl_y < h_vpt_max) {
			vanpt_cla[i] = kVptHoriz;
			sum_hvpts_y += eucl_y;
			num_hvpts++;
		} else if (eucl_y < v_vpt_low || eucl_y > v_vpt_high) {
			vanpt_cla[i] = kVptVert;
		}
	}

	// Assume horizon is horizontal with Y coord equal to average of the
	// "horizontal" vanishing points
	float horizon_y = 0.5;
	if (num_hvpts > 0) {
		horizon_y = sum_hvpts_y / num_hvpts;
	}

	// Print vanpt clas
	DREPORT(vanpt_cla);



	// Initialize features
	int num_segments = seg.MaxValue()+1;
	features.resize(num_segments);
	for (int i = 0; i < features.size(); i++) {
		features[i].seen = VecI(num_lines, 0);
	}

	//
	// Compute sums over the image
	//
	for (int r = 0; r < h; r++) {
		const PixelRGB<byte>* imrgbrow = imagergb[r];
		const PixelF* imrow = image[r];
		const int* segrow = seg[r];
		const int* linerow = vpts.lines.segment_map[r];
		for (int c = 0; c < w; c++) {
			HehFeature& ftr = features[segrow[c]];

			// Shape
			ftr.xs.push_back(1.0*c/w);
			ftr.ys.push_back(1.0*r/h);

			// Color
			PixelHSV<byte> hsv;
			const PixelRGB<byte>& rgb = imrgbrow[c];
			ImageConversions::RGB2HSV(rgb.r, rgb.g, rgb.b,
																hsv.h, hsv.s, hsv.v);
			ftr.r_mean += rgb.r;
			ftr.g_mean += rgb.g;
			ftr.b_mean += rgb.b;
			ftr.h_mean += hsv.h;
			ftr.s_mean += hsv.s;
			ftr.v_mean += hsv.v;

			const int hbin = static_cast<int>(kNumHueBins * hsv.h / 256);
			const int sbin = static_cast<int>(kNumSatBins * hsv.s / 256);
			ftr.hue_hist[hbin]++;
			ftr.sat_hist[sbin]++;
			
			// Texture
			ImageRef pyrpos(c, r);
			int maxi;
			float maxv = -INFINITY;
			for (int i = 0; i < responses.size(); i++) {
				if (i > 0 && responses[i]->GetWidth() < responses[i-1]->GetWidth()) {
					pyrpos /= 2;
				}
				const float v = (*responses[i])[pyrpos].y;
				ftr.filter_means[i] += v;
				if (v > maxv) {
					maxv = v;
					maxi = i;
				}
			}
			ftr.filter_max_hist[maxi]++;

			// Lines
			const int line = linerow[c];
			if (line != -1) {
				if (!ftr.seen[line]) {
					ftr.seen[line] = 1;
					ftr.lines.push_back(line);
				}
				ftr.num_line_pix++;
				const int line_cla = vanpt_cla[vpts.owners[line]];
				if (line_cla == kVptVert) {
					ftr.pct_vert_vpt++;
					ftr.pct_vert_vpt_pix++;
				} else if (line_cla == kVptHoriz) {
					ftr.pct_horiz_vpt_pix++;
				}
			}
		}
	}

	//
	// Normalize over each segment
	//
	for (int i = 0; i < features.size(); i++) {
		HehFeature& ftr = features[i];
		float size = ftr.xs.size();

		// Shape
		ftr.area = size/image_size;

		sort_all(ftr.xs);
		sort_all(ftr.ys);

		ftr.x_mean = accumulate_all(ftr.xs, 0.0) / size;
		ftr.x_10pct = ftr.xs[size/10];
		ftr.x_90pct = ftr.xs[size*9/10];

		ftr.y_mean = accumulate_all(ftr.ys, 0.0) / size;
		ftr.y_10pct = ftr.ys[size/10];
		ftr.y_90pct = ftr.ys[size*9/10];

		ftr.y_mean_wrt_horizon = ftr.y_mean - horizon_y;
		ftr.y_10pct_wrt_horizon = ftr.y_10pct - horizon_y;
		ftr.y_90pct_wrt_horizon = ftr.y_90pct - horizon_y;

		// Color
		ftr.r_mean /= size;
		ftr.g_mean /= size;
		ftr.b_mean /= size;
		ftr.h_mean /= size;
		ftr.s_mean /= size;
		ftr.v_mean /= size;
		ftr.hue_hist /= size;
		ftr.sat_hist /= size;

		// Texture
		ftr.filter_means /= size;
		ftr.filter_max_hist /= size;

		// Lines
		int num_nearly_parallel = 0;
		BOOST_FOREACH(int i, ftr.lines) {
			BOOST_FOREACH(int j, ftr.lines) {
				if (i == j) continue;
				if (is_nearly_parallel[i][j]) {
					num_nearly_parallel++;
					ftr.pct_parallel_lines++;
					if (isct_is_right[i][j]) {
						ftr.pct_isct_right++;
					}
					if (isct_is_bottom[i][j]) {
						ftr.pct_isct_bottom++;
					}
					if (isct_bin[i][j] != -1) {
						ftr.isct_hist[isct_bin[i][j]]++;
					}
				}
			}
		}
		const int num_line_pairs = ftr.lines.size() * (ftr.lines.size()-1);
		const float sqrt_area = sqrt(size);  // don't use normalized area!
		ftr.pct_line_pix = ftr.num_line_pix / sqrt_area;
		if (num_line_pairs > 0) {
			ftr.pct_parallel_lines /= num_line_pairs;
		}
		if (num_nearly_parallel > 0) {
			ftr.isct_hist /= num_nearly_parallel;
			ftr.isct_hist_entropy = GetEntropy(ftr.isct_hist);
			ftr.pct_isct_right /= num_nearly_parallel;
			ftr.pct_isct_bottom /= num_nearly_parallel;
		}

		// Vanishing points
		bool has_horiz_vpt = false;
		bool has_vert_vpt = false;
		float horiz_vpt_x;
		float vert_vpt_y;
		for (int i = 0; i < ftr.lines.size(); i++) {
			const int owner_vpt = vpts.owners[ftr.lines[i]];
			if (vanpt_cla[owner_vpt] == kVptHoriz) {
				horiz_vpt_x = Project(norm_vpts[owner_vpt])[0];  // can be +/- INF
				has_horiz_vpt = true;
			}
			if (vanpt_cla[owner_vpt] == kVptHoriz) {
				vert_vpt_y = Project(norm_vpts[owner_vpt])[1];  // can be +/- INF
				has_vert_vpt = true;
			}
		}
		ftr.pct_vert_vpt_pix /= sqrt_area;
		ftr.pct_horiz_vpt_pix /= sqrt_area;
		if (ftr.num_line_pix > 0) {
			ftr.pct_vert_vpt /= ftr.num_line_pix;
		}
		if (has_horiz_vpt) {
			ftr.x_mean_wrt_hvpt = ftr.x_mean - horiz_vpt_x;  // can be +/- INF
			ftr.x_10pct_wrt_hvpt = ftr.x_10pct - horiz_vpt_x;  // can be +/- INF
			ftr.x_90pct_wrt_hvpt = ftr.x_90pct - horiz_vpt_x;  // can be +/- INF
		} else {
			ftr.x_mean_wrt_hvpt = 0;
			ftr.x_10pct_wrt_hvpt = 0;
			ftr.x_90pct_wrt_hvpt = 0;
		}
		if (has_vert_vpt) {
			ftr.y_mean_wrt_vert_vpt = ftr.y_mean - vert_vpt_y;
		} else {
			ftr.y_mean_wrt_vert_vpt = 0;
		}
	}
}
//------------------------------------------------------------------------------
// Purpose : Draws a large crosshair at flCrossDistance from the debug player
//			 with tick marks
// Input   :
// Output  :
//------------------------------------------------------------------------------
void NDebugOverlay::DrawPositioningOverlay( float flCrossDistance )
{
	CBasePlayer* pPlayer = UTIL_PlayerByIndex(CBaseEntity::m_nDebugPlayer);

	if (!pPlayer) 
	{
		return;
	}

	Vector pRight;
	pPlayer->EyeVectors( NULL, &pRight, NULL );

#ifdef _WIN32
	Vector topPos		= NWCEdit::AirNodePlacementPosition();
#else
        Vector pForward;
        pPlayer->EyeVectors( &pForward );

        Vector  floorVec = pForward;
        floorVec.z = 0;
        VectorNormalize( floorVec );
        VectorNormalize( pForward );

        float cosAngle = DotProduct(floorVec,pForward);

        float lookDist = g_pAINetworkManager->GetEditOps()->m_flAirEditDistance/cosAngle;
        Vector topPos = pPlayer->EyePosition()+pForward * lookDist;
#endif

	Vector bottomPos	= topPos;
	bottomPos.z			= GetLongFloorZ(bottomPos);

	// Make sure we can see the target pos
	trace_t tr;
	Vector	endPos;
	UTIL_TraceLine(pPlayer->EyePosition(), topPos, MASK_NPCSOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr);
	if (tr.fraction == 1.0)
	{
		Vector rightTrace = topPos + pRight*400;
		float  traceLen	  = (topPos - rightTrace).Length();
		UTIL_TraceLine(topPos, rightTrace, MASK_NPCSOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr);
		endPos = topPos+(pRight*traceLen*tr.fraction);
		NDebugOverlay::DrawTickMarkedLine(topPos, endPos, 24.0, 5, 255,0,0,false,0);

		Vector leftTrace	= topPos - pRight*400;
		traceLen			= (topPos - leftTrace).Length();
		UTIL_TraceLine(topPos, leftTrace, MASK_NPCSOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr);
		endPos				= topPos-(pRight*traceLen*tr.fraction);
		NDebugOverlay::DrawTickMarkedLine(topPos, endPos, 24.0, 5, 255,0,0,false,0);

		Vector upTrace		= topPos + Vector(0,0,1)*400;
		traceLen			= (topPos - upTrace).Length();
		UTIL_TraceLine(topPos, upTrace, MASK_NPCSOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr);
		endPos				= topPos+(Vector(0,0,1)*traceLen*tr.fraction);
		NDebugOverlay::DrawTickMarkedLine(bottomPos, endPos, 24.0, 5, 255,0,0,false,0);

		// Draw white cross at center
		Cross3D(topPos, Vector(-2,-2,-2), Vector(2,2,2), 255,255,255, true, 0);
	}
	else
	{
		// Draw warning  cross at center
		Cross3D(topPos, Vector(-2,-2,-2), Vector(2,2,2), 255,100,100, true, 0 );
	}
	/*  Don't show distance for now.
	char text[25];
	Q_snprintf(text,sizeof(text),"%3.0f",flCrossDistance/12.0);
	Vector textPos = topPos - pRight*16 + Vector(0,0,10);
	NDebugOverlay::Text( textPos, text, true, 0 );
	*/
}