void GuiDecalEditorCtrl::_renderDecalEdge( const Vector<Point3F> &verts, const ColorI &color )
{
   U32 vertCount = verts.size();

   GFXTransformSaver saver;

   PrimBuild::color( color );

   Point3F endPt( 0, 0, 0 );
   for ( U32 i = 0; i < vertCount; i++ )
   {
      const Point3F &vert = verts[i];
      if ( i + 1 < vertCount )
         endPt = verts[i + 1];
      else
         break;

      PrimBuild::begin( GFXLineList, 2 );

      PrimBuild::vertex3f( vert.x, vert.y, vert.z );
      PrimBuild::vertex3f( endPt.x, endPt.y, endPt.z );

      PrimBuild::end();
   }
}
Esempio n. 2
0
    std::string packLineData(LineData lineData)
    {
        rapidjson::Document document;

        document.SetObject();
        
        rapidjson::Value startPt(rapidjson::kObjectType);
        startPt.AddMember("x", lineData.startPoint.x, document.GetAllocator());
        startPt.AddMember("y", lineData.startPoint.y, document.GetAllocator());

        rapidjson::Value endPt(rapidjson::kObjectType);
        endPt.AddMember("x", lineData.endPoint.x, document.GetAllocator());
        endPt.AddMember("y", lineData.endPoint.y, document.GetAllocator());
        
        rapidjson::Value lineColor(rapidjson::kObjectType);
        lineColor.AddMember("r", lineData.color.r, document.GetAllocator());
        lineColor.AddMember("g", lineData.color.g, document.GetAllocator());
        lineColor.AddMember("b", lineData.color.b, document.GetAllocator());
        lineColor.AddMember("a", lineData.color.a, document.GetAllocator());
        
        document.AddMember("startPoint", startPt, document.GetAllocator());
        document.AddMember("endPoint", endPt, document.GetAllocator());
        document.AddMember("radius", lineData.radius, document.GetAllocator());
        document.AddMember("color", lineColor, document.GetAllocator());
        
        rapidjson::StringBuffer buffer;
        rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
        document.Accept(writer);
        
        return std::string(buffer.GetString(), buffer.GetSize());
    }
Esempio n. 3
0
//----------------------------------------------------------						
void ofxVectorGraphics::endShape(bool bClose){
	if(bDraw){
		ofEndShape(bClose);
	}
	if(bRecord){
											
		//catmull roms - we need at least 4 points to draw
		if( whichShapeMode == 1 && curvePts.size() > 3){												
			
			//we go through and we calculate the bezier of each 
			//catmull rom curve - smart right? :)
			for (int i = 1; i< curvePts.size()-2; i++) {
				
				ofPoint3 prevPt(	curvePts[i-1][0],	curvePts[i-1][1]);
				ofPoint3 startPt(curvePts[i][0],		curvePts[i][1]);							
				ofPoint3 endPt(	curvePts[i+1][0],	curvePts[i+1][1]);
				ofPoint3 nextPt(	curvePts[i+2][0],	curvePts[i+2][1]);
				
				//SUPER WEIRD MAGIC CONSTANT = 1/6
				//Someone please explain this!!! 
				//It works and is 100% accurate but wtf!
				ofPoint3 cp1 = startPt + ( endPt - prevPt ) * (1.0/6);
				ofPoint3 cp2 = endPt + ( startPt - nextPt ) * (1.0/6);						
				
				//if this is the first line we are drawing 
				//we have to start the path at a location
				if( i == 1 ){
					creeps.startPath(startPt.x, startPt.y);
					bShouldClose = true;
				}
				
				creeps.addCurve( cp1.x, cp1.y, cp2.x, cp2.y, endPt.x, endPt.y);
			}						
		}
		
		if(bShouldClose){														
			//we close the path if requested
			if(bClose)creeps.closeSubpath();
		
			//render the stroke as either a fill or a stroke.
			if(bFill){
				creeps.endPath(CreEPS::FILL);
			}else{
				creeps.endPath(CreEPS::STROKE);
			}
			bShouldClose = false;
		}
		
		//we want to clear all the vertices
		//otherwise we keep adding points from
		//the previous file - cool but not what we want!
		clearAllVertices();
		
	}
}			
Esempio n. 4
0
void CXMLExporter::WriteEdge(CComPtr<ISkpEdge> pEdge)
{
    HResult hr;

    m_Stats.m_nEdges += 1;

    if (m_pOptions->GetExportLayers())
    {
        CComPtr<ISkpDrawingElement> pDE;
        hr = pEdge->QueryInterface(IID_ISkpDrawingElement, (void**) &pDE);

        CComPtr<ISkpLayer> pLayer;
        hr = pDE->get_Layer(&pLayer);

        long layerId = GetEntityId(pLayer);

        Write("<Edge layer=\"%d\">\n", layerId);
    }
    else
    {
        Write("<Edge>\n");
    }

    IncreaseIndent();

    CComPtr<ISkpPoint3d> pStartPoint;
    hr = pEdge->get_StartPoint(&pStartPoint);
    CPoint3d startPt(pStartPoint);
    CPoint3d startPoint = m_InheritanceManager.GetCurrentTransform() * startPt;
    Write("<Start x=\"%f\" y=\"%f\" z=\"%f\" />\n", startPoint.X(), startPoint.Y(), startPoint.Z());

    CComPtr<ISkpPoint3d> pEndPoint;
    hr = pEdge->get_EndPoint(&pEndPoint);
    CPoint3d endPt(pEndPoint);
    CPoint3d endPoint = m_InheritanceManager.GetCurrentTransform() * endPt;
    Write("<End x=\"%f\" y=\"%f\" z=\"%f\" />\n", endPoint.X(), endPoint.Y(), endPoint.Z());

    DecreaseIndent();

    Write("</Edge>\n");
}
Esempio n. 5
0
bool MgArc::_setHandlePoint2(int index, const Point2d& pt, float, int& data)
{
    static float lastSweepAngle;
    
    if (index == 1 || index == 2) {     // 起点、终点
        return setCenterRadius(getCenter(), pt.distanceTo(getCenter()), getStartAngle(), getSweepAngle());
    }
    if (index == 3) {                   // 弧线中点
        return setStartMidEnd(getStartPoint(), pt, getEndPoint());
    }
    if (index == 4) {                   // 改变起始角度
        if (data == 0) {
            lastSweepAngle = getSweepAngle();
            data++;
        }
        Point2d startPt(getCenter().polarPoint((pt - getCenter()).angle2(), getRadius()));
        bool ret = setCSE(getCenter(), startPt, getEndPoint(), lastSweepAngle);
        lastSweepAngle = getSweepAngle();
        return ret;
    }
    if (index == 5) {                   // 改变终止角度
        if (data == 0) {
            lastSweepAngle = getSweepAngle();
            data++;
        }
        Point2d endPt(getCenter().polarPoint((pt - getCenter()).angle2(), getRadius()));
        bool ret = setCSE(getCenter(), getStartPoint(), endPt, lastSweepAngle);
        lastSweepAngle = getSweepAngle();
        return ret;
    }
    if (index == 6) {
        return setTanStartEnd(pt - getStartPoint(), getStartPoint(), getEndPoint());
    }
    if (index == 7) {
        return (setTanStartEnd(getEndPoint() - pt, getEndPoint(), getStartPoint())
            && _reverse());
    }
    return offset(pt - getCenter(), -1);
}
Esempio n. 6
0
int
seg::
contains ( const seg& segIn ) const
{
	// TODO: Remove direction vector normalization??  User's responsibility???
	vec3 tDir ( segIn.dir );
	tDir.normalize ();
//	vec3 endPt = segIn.pos + tDir * segIn.length;
	vec3 endPt ( tDir );
	endPt *= segIn.length;
	endPt += segIn.pos;

	int c1 = contains ( segIn.pos );
	int c2 = contains ( endPt );

	if ( c1 )
	{
		if ( c2 )
			return containsResult::AllIn;

		return containsResult::SomeIn;
	}

	if ( c2 )
		return containsResult::SomeIn;


	// OPTIMIZE: Expensive...
	if ( segIn.contains ( pos ) )
		return containsResult::SomeIn;

	vec3 endPos = pos + dir * length;

	if ( segIn.contains ( endPos ) )
		return containsResult::SomeIn;

	return containsResult::NoneIn;
}
Esempio n. 7
0
// OPTIMIZE: NON-OPTIMAL
int
seg::
contains ( const vec3& pt ) const
{
	// TODO: There are some expensive calls in here.  Find a more optimal means of doing this.
	if ( pt.equal ( pos ) )
		return containsResult::AllIn;

	vec3 endPt ( dir );
	endPt *= length;
	endPt += pos;

	if ( pt.equal ( endPt ) )
		return containsResult::AllIn;

//	vec3 ptDir = pt - pos;
	vec3 ptDir ( pt );
	ptDir -= pos;
	vec3 a = ptDir;
	a.normalize ();

	// Direction vectors the same?
	if ( ! a.equal ( dir ) )
	{
		// Nope.  We're not in/on the segment.
		return containsResult::NoneIn;
	}

	// The direction vectors are the same, so what is our distance from the endpoint?
	if ( ptDir.length () <= length )
	{
		return containsResult::AllIn;
	}

	return containsResult::NoneIn;
}
Esempio n. 8
0
PointObject* calcPoint(const std::vector<int>& v)
{
	PointObject* pO = nullptr;

	int start_point = 0;
	int end_point = 0;

	auto it = std::find_if(v.begin(), v.end(), [](const int a) { return (a != 0); });
	auto it2 = std::find_if(v.rbegin(), v.rend(), [](int a) { return (a != 0); });

	if ((it != v.end()))
	{
		Point startPt(std::distance(v.begin(), it));
		Point endPt(std::distance(it2, v.rend()) - 1);

		pO = new Point(average(startPt, endPt));
	}
	else
	{
		pO = new NullPoint();
	}

	return pO;
}
Esempio n. 9
0
int testPoint() {
	int numErr = 0;

	logMessage(_T("TESTING  -  class GM_3dPoint ...\n\n"));

	// Default constructor, point must be invalid
	GM_3dPoint pt;
	if (pt.isValid()) {
		logMessage(_T("\tERROR - Default constructor creates valid points\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Default constructor creates invalid points\n"));
	}

	// Get/Set points coordinates
	double x = getRandomDouble();
	double y = getRandomDouble();
	double z = getRandomDouble();
	pt.x(x); pt.y(y); pt.z(z);
	if (pt.x() != x || pt.y() != y || pt.z() != z) {
		logMessage(_T("\tERROR - Get/Set not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Get/Set working\n"));
	}

	// Copy constructor
	GM_3dPoint pt1(pt);
	if (pt.isValid() != pt1.isValid() || pt1.x() != pt.x() || pt1.y() != pt.y() || pt1.z() != pt.z()) {
		logMessage(_T("\tERROR - Copy constructor not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Copy constructor working\n"));
	}

	// Constructor (coordinates)
	x = getRandomDouble();
	y = getRandomDouble();
	z = getRandomDouble();
	GM_3dPoint pt2(x, y, z);
	if (!pt2.isValid() || pt2.x() != x || pt2.y() != y || pt2.z() != z) {
		logMessage(_T("\tERROR - Coordinate constructor not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Coordinate constructor working\n"));
	}

	// Constructor (angle)
	double ang = getRandomAngle();
	GM_3dPoint pt3(ang);
	if (!pt2.isValid() || pt3.x() != cos(ang) || pt3.y() != sin(ang) || pt3.z() != 0.0) {
		logMessage(_T("\tERROR - XY angle constructor not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - XY angle constructor working\n"));
	}
	
	// Invalidation
	pt.invalidate();
	if (pt.isValid()) {
		logMessage(_T("\tERROR - Invalidation not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Invalidation working\n"));
	}

	// Inversion
	GM_3dPoint invPt(pt2);
	invPt.xyInvert();
	if (invPt.isValid() != pt2.isValid() || invPt.x() != -pt2.x() || invPt.y() != -pt2.y() || invPt.z() != pt2.z()) {
		logMessage(_T("\tERROR - Inversion not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Inversion working\n"));
	}

	// Origin check
	GM_3dPoint origPt(GM_NULL_TOLERANCE / 2.0, -GM_NULL_TOLERANCE / 2.0, 0.0);
	if (pt2.isOrigin() || !origPt.isOrigin()) {
		logMessage(_T("\tERROR - Origin check not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Origin check working\n"));
	}

	// Distance
	GM_3dPoint startPt(getRandomDouble(), getRandomDouble(), getRandomDouble());
	GM_3dPoint endPt(getRandomDouble(), getRandomDouble(), getRandomDouble());
	double dx = startPt.x() - endPt.x();
	double dy = startPt.y() - endPt.y();
	double dz = startPt.z() - endPt.z();
	double dist = sqrt(dx*dx + dy*dy + dz*dz);
	if (!startPt.isValid() || !endPt.isValid() || startPt.distFrom(endPt) != dist) {
		logMessage(_T("\tERROR - Distance computation not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Distance computation working\n"));
	}

	// Equality
	GM_3dPoint ptEq1(getRandomDouble(), getRandomDouble(), getRandomDouble());
	GM_3dPoint ptEq2(ptEq1);
	GM_3dPoint ptEq3(ptEq1.x() + getRandomDouble(), ptEq1.y() + getRandomDouble(), ptEq1.z() + getRandomDouble());
	if (ptEq1 != ptEq2 || ptEq1 == ptEq3) {
		logMessage(_T("\tERROR - Equality check not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Equality check working\n"));
	}

	return numErr;
}
Esempio n. 10
0
int testLine() {
	int numErr = 0;

	logMessage(_T("TESTING  -  class GM_3dLine ...\n\n"));

	// Default constructor, line must be invalid
	GM_3dLine ln;
	if (ln.isValid()) {
		logMessage(_T("\tERROR - Default constructor creates valid line\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Default constructor creates invalid line\n"));
	}

	// Get/Set line coordinates
	double xStart = getRandomDouble();
	double yStart = getRandomDouble();
	double zStart = getRandomDouble();
	double xEnd = getRandomDouble();
	double yEnd = getRandomDouble();
	double zEnd = getRandomDouble();
	ln.begin().x(xStart);
	ln.begin().y(yStart);
	ln.begin().z(zStart);
	ln.end().x(xEnd);
	ln.end().y(yEnd);
	ln.end().z(zEnd);
	if (!ln.isValid() || ln.begin().x() != xStart || ln.begin().y() != yStart || ln.begin().z() != zStart ||
				ln.end().x() != xEnd || ln.end().y() != yEnd || ln.end().z() != zEnd) {
		logMessage(_T("\tERROR - Get/Set not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Get/Set working\n"));
	}

	// Copy constructor
	GM_3dLine ln1(ln);
	if (ln1.isValid() != ln.isValid() || ln1.begin().x() != ln.begin().x() || ln1.begin().y() != ln.begin().y() || ln1.begin().z() != ln.begin().z() ||
				ln1.end().x() != ln.end().x() || ln1.end().y() != ln.end().y() || ln1.end().z() != ln.end().z()) {
		logMessage(_T("\tERROR - Copy constructor not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Copy constructor working\n"));
	}

	// Constructor (points)
	GM_3dPoint startPt(getRandomDouble(), getRandomDouble(), getRandomDouble());
	GM_3dPoint endPt(getRandomDouble(), getRandomDouble(), getRandomDouble());
	GM_3dLine ln2(startPt, endPt);
	if (!ln2.isValid() || ln2.begin().x() != startPt.x() || ln2.begin().y() != startPt.y() || ln2.begin().z() != startPt.z() ||
			ln2.end().x() != endPt.x() || ln2.end().y() != endPt.y() || ln2.end().z() != endPt.z()) {
		logMessage(_T("\tERROR - Points constructor not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Points constructor working\n"));
	}

	// Constructor (coordinates)
	xStart = getRandomDouble();
	yStart = getRandomDouble();
	zStart = getRandomDouble();
	xEnd = getRandomDouble();
	yEnd = getRandomDouble();
	zEnd = getRandomDouble();
	GM_3dLine ln3(xStart, yStart, zStart, xEnd, yEnd, zEnd);
	if (!ln3.isValid() || ln3.begin().x() != xStart || ln3.begin().y() != yStart || ln3.begin().z() != zStart ||
			ln3.end().x() != xEnd || ln3.end().y() != yEnd || ln3.end().z() != zEnd) {
		logMessage(_T("\tERROR - Coordinate constructor not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Coordinate constructor working\n"));
	}

	// dx, dy, dz
	double dx = ln2.dx();
	double dy = ln2.dy();
	double dz = ln2.dz();
	if (dx != ln2.end().x()-ln2.begin().x() || dy != ln2.end().y()-ln2.begin().y() || dz != ln2.end().z()-ln2.begin().z()) {
		logMessage(_T("\tERROR - dx, dy or dz not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - dx, dy or dz working\n"));
	}

	// Dot product
	double dotProduct = ln2 * ln3;
	double dxLn2 = ln2.dx();
	double dyLn2 = ln2.dy();
	double dzLn2 = ln2.dz();
	double dxLn3 = ln3.dx();
	double dyLn3 = ln3.dy();
	double dzLn3 = ln3.dz();
	double checkDotProduct = dxLn2*dxLn3 + dyLn2*dyLn3 + dzLn2*dzLn3;
	if (dotProduct != checkDotProduct) {
		logMessage(_T("\tERROR - Dot product not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Dot product working\n"));
	}

	// Cross product
	GM_3dLine crossProdLn = ln2 ^ ln3;
	double checkCrossProdX = ln2.begin().x() + (dyLn2*dzLn3 - dzLn2*dyLn3);
	double checkCrossProdY = ln2.begin().y() + (dzLn2*dxLn3 - dxLn2*dzLn3);
	double checkCrossProdZ = ln2.begin().z() + (dxLn2*dyLn3 - dyLn2*dxLn3);
	if (!crossProdLn.isValid() || crossProdLn.end().x() != checkCrossProdX || crossProdLn.end().y() != checkCrossProdY || crossProdLn.end().z() != checkCrossProdZ) {
		logMessage(_T("\tERROR - Cross product not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Cross product working\n"));
	}

	// Center
	GM_3dPoint ln2Center = ln2.center();
	if (!ln2Center.isValid() || ln2Center.x() != ln2.begin().x() + (ln2.end().x()-ln2.begin().x())/2.0 ||
								ln2Center.y() != ln2.begin().y() + (ln2.end().y()-ln2.begin().y())/2.0 ||
								ln2Center.z() != ln2.begin().z() + (ln2.end().z()-ln2.begin().z())/2.0) {
		logMessage(_T("\tERROR - Center point computation not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Center point computation working\n"));
	}

	// Invert
	GM_3dLine ln2Copy(ln2);
	ln2.invert();
	if (!ln2Copy.isValid() || ln2Copy.begin().x() != ln2.end().x() || ln2Copy.begin().y() != ln2.end().y() || ln2Copy.begin().z() != ln2.end().z() ||
			ln2Copy.end().x() != ln2.begin().x() || ln2Copy.end().y() != ln2.begin().y() || ln2Copy.end().z() != ln2.begin().z()) {
		logMessage(_T("\tERROR - Inversion not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Inversion working\n"));
	}

	// Length
	double checkLen = sqrt(ln2.dx()*ln2.dx() + ln2.dy()*ln2.dy() + ln2.dz()*ln2.dz());
	if (ln2.length() != checkLen) {
		logMessage(_T("\tERROR - Length computation not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Length computation working\n"));
	}

	// Null check
	GM_3dLine nullLn(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
	bool checkNull1 = nullLn.isNull();
	nullLn.begin().x(10.0);
	bool checkNull2 = nullLn.isNull();
	if (!nullLn.isValid() || !checkNull1 || checkNull2) {
		logMessage(_T("\tERROR - Null line check not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Null line check working\n"));
	}

	// Vertical line check
	double xCoord = getRandomDouble();
	double yCoord = getRandomDouble();
	GM_3dLine vertLn(xCoord, yCoord, getRandomDouble(), xCoord, yCoord, getRandomDouble());
	bool checkVert1 = vertLn.isVertical();
	vertLn.begin().x(vertLn.begin().x() + 10.0);
	bool checkVert2 = vertLn.isVertical();
	if (!vertLn.isValid() || !checkVert1 || checkVert2) {
		logMessage(_T("\tERROR - Vertical line check not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Vertical line check working\n"));
	}

	// Horizontal line check
	double zCoord = getRandomDouble();
	GM_3dLine horLn(getRandomDouble(), getRandomDouble(), zCoord, getRandomDouble(), getRandomDouble(), zCoord);
	bool checkHor1 = horLn.isHorizontal();
	horLn.begin().z(horLn.begin().z() + 10.0);
	bool checkHor2 = horLn.isHorizontal();
	if (!horLn.isValid() || !checkHor1 || checkVert2) {
		logMessage(_T("\tERROR - Horizontal line check not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Horizontal line check working\n"));
	}

	// Z min/max
	double minZ = ln2.minZ();
	double maxZ = ln2.maxZ();
	double checkMin = ln2.begin().z() < ln2.end().z() ? ln2.begin().z() : ln2.end().z();
	double checkMax = ln2.begin().z() > ln2.end().z() ? ln2.begin().z() : ln2.end().z();
	if (minZ != checkMin || maxZ != checkMax) {
		logMessage(_T("\tERROR - min/max Z not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - min/max Z working\n"));
	}

	// Point on line at Z
	double sec = getRandom01Double();
	double dX = ln2.dx();
	double dY = ln2.dy();
	double dZ = ln2.dz();
	double xCheck = ln2.begin().x() + (sec * dX);
	double yCheck = ln2.begin().y() + (sec * dY);
	double zCheck = ln2.begin().z() + (sec * dZ);
	GM_3dPoint pointAtZ = ln2.pointAtZ(zCheck);
	if (!pointAtZ.isValid() || fabs(pointAtZ.x()-xCheck) > GM_NULL_TOLERANCE || fabs(pointAtZ.y()-yCheck) > GM_NULL_TOLERANCE) {
		logMessage(_T("\tERROR - Point on line at Z not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Point on line at Z working\n"));
	}

	// Nomal vector on XY
	GM_3dVector normalVectorR = ln2.normalXYVector(true);
	GM_3dVector normalVectorL = ln2.normalXYVector(false);
	GM_3dVector ln2Dir(ln2);
	double dotPR = normalVectorR * ln2Dir;
	double dotPL = normalVectorL * ln2Dir;
	bool RSide = ln2Dir.isAtLeftOnXY(normalVectorR);
	bool LSide = ln2Dir.isAtLeftOnXY(normalVectorL);
	if (!normalVectorR.isValid() || !normalVectorL.isValid() || fabs(dotPR) > GM_NULL_TOLERANCE || fabs(dotPL) > GM_NULL_TOLERANCE || !RSide || LSide) {
		logMessage(_T("\tERROR - XY normal vector computation not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - XY normal vector computation working\n"));
	}

	// Point from section
	double section = getRandom01Double();
	GM_3dPoint sectionPoint = ln2.pointFromSection(section);
	double ln2Len = ln2.length();
	double pointSectionLen = ln2.begin().distFrom(sectionPoint);
	if (fabs((pointSectionLen / ln2Len) - section) > GM_NULL_TOLERANCE) {
		logMessage(_T("\tERROR - Point from section not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Point from section working\n"));
	}

	// Section from point
	double checkSection = ln2.sectionFromPoint(sectionPoint);
	if (fabs(checkSection - section) > GM_NULL_TOLERANCE) {
		logMessage(_T("\tERROR - Section from point not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Section from point working\n"));
	}

	// Point distance
	ln2Dir.normalize();
	GM_3dVector ln2NormDir(ln2Dir);
	section = getRandom01Double();
	sectionPoint = ln2.pointFromSection(section);
	if (fabs(ln2Dir.x()) > GM_NULL_TOLERANCE) {
		ln2NormDir.x(getRandomDouble());
		ln2NormDir.y(getRandomDouble());
		ln2NormDir.z(getRandomDouble());
		ln2NormDir.normalize();
		ln2NormDir.x(-(ln2Dir.y()*ln2NormDir.y() + ln2Dir.z()*ln2NormDir.z()) / ln2Dir.x());
	}
	else if (fabs(ln2Dir.y()) > GM_NULL_TOLERANCE) {
		ln2NormDir.x(getRandomDouble());
		ln2NormDir.y(getRandomDouble());
		ln2NormDir.z(getRandomDouble());
		ln2NormDir.normalize();
		ln2NormDir.y(-(ln2Dir.x()*ln2NormDir.x() + ln2Dir.z()*ln2NormDir.z()) / ln2Dir.y());
	}
	else if (fabs(ln2Dir.z()) > GM_NULL_TOLERANCE) {
		ln2NormDir.x(getRandomDouble());
		ln2NormDir.y(getRandomDouble());
		ln2NormDir.z(getRandomDouble());
		ln2NormDir.normalize();
		ln2NormDir.z(-(ln2Dir.x()*ln2NormDir.x() + ln2Dir.y()*ln2NormDir.y()) / ln2Dir.z());
	}
	else {
		ln2NormDir.invalidate();
	}
	double dist = 0.0;
	double checkDist = 0.0;
	GM_3dPoint pointOnLine;
	if (ln2NormDir.isValid()) {
		ln2NormDir.normalize();

		double dotProd = ln2NormDir * ln2Dir;

		dist = fabs(getRandomDouble());
		GM_3dPoint externPoint(sectionPoint);
		externPoint = (GM_3dVector)externPoint + (ln2NormDir * dist);
		checkDist = ln2.pointDistance(externPoint, pointOnLine);
	}
	if (!pointOnLine.isValid() || fabs(dist - checkDist) > GM_NULL_TOLERANCE) {
		logMessage(_T("\tERROR - Point distance not working\n"));
		numErr++;
	}
	else {
		logMessage(_T("\tOK - Point distance working\n"));
	}


	return numErr;
}