<commit_before>/*
 * Copyright (c) 2011, Georgia Tech Research Corporation
 * All rights reserved.
 *
 * Author(s): Jeongseok Lee <jslee02@gmail.com>
 * Date: 05/07/2013
 *
 * Geoorgia Tech Graphics Lab and Humanoid Robotics Lab
 *
 * Directed by Prof. C. Karen Liu and Prof. Mike Stilman
 * <karenliu@cc.gatech.edu> <mstilman@cc.gatech.edu>
 *
 * This file is provided under the following "BSD-style" License:
 *   Redistribution and use in source and binary forms, with or
 *   without modification, are permitted provided that the following
 *   conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <gtest/gtest.h>

#include "fcl/collision.h"
#include "fcl/shape/geometric_shapes.h"
#include "fcl/narrowphase/narrowphase.h"

class COLLISION : public testing::Test
{
public:
	void unrotatedTest(fcl::CollisionGeometry* _coll1,
					   fcl::CollisionGeometry* _coll2,
					   double expectedContactPoint, int _idxAxis);
	void printResult(const fcl::CollisionResult& _result);
};

void COLLISION::unrotatedTest(fcl::CollisionGeometry* _coll1,
							  fcl::CollisionGeometry* _coll2,
							  double expectedContactPoint,
							  int _idxAxis)
{
	fcl::CollisionResult result;
	fcl::CollisionRequest request;
	request.enable_contact = true;
	request.num_max_contacts = 100;

	fcl::Vec3f position(0, 0, 0);

	fcl::Transform3f coll1_transform;
	fcl::Transform3f coll2_transform;

	//==========================================================================
	// Approaching test
	//==========================================================================
	result.clear();
	double dpos = -0.001;
	double pos = 10.0;

	coll1_transform.setIdentity();
	coll1_transform.setTranslation(fcl::Vec3f(0, 0, 0));
	coll2_transform.setIdentity();

	// Let's drop box2 until it collide with box1
	do {
		position[_idxAxis] = pos;
		coll2_transform.setTranslation(position);

		fcl::collide(_coll1, coll1_transform,
					 _coll2, coll2_transform,
					 request, result);

		pos += dpos;
	}
	while (result.numContacts() == 0);

	//
	if (_idxAxis == 0)
		std::cout << "The object is collided when its x-axis position is: " << (pos - dpos) << std::endl;
	if (_idxAxis == 1)
		std::cout << "The object is collided when its y-axis position is: " << (pos - dpos) << std::endl;
	if (_idxAxis == 2)
		std::cout << "The object is collided when its z-axis position is: " << (pos - dpos) << std::endl;

	//printResult(result);

	for (int i = 0; i < result.numContacts(); ++i)
	{
		EXPECT_GE(result.getContact(i).penetration_depth, 0.0);
//		EXPECT_NEAR(result.getContact(i).normal[_idxAxis], -1.0);
		EXPECT_EQ(result.getContact(i).normal.length(), 1.0);
		EXPECT_NEAR(result.getContact(i).pos[_idxAxis], expectedContactPoint, -dpos*2.0);
	}
}

void COLLISION::printResult(const fcl::CollisionResult& _result)
{
	std::cout << "====== [ RESULT ] ======" << std::endl;
	std::cout << "The number of contacts: " << _result.numContacts() << std::endl;

	for (int i = 0; i < _result.numContacts(); ++i)
	{
		std::cout << "----- CONTACT " << i << " --------" << std::endl;
		std::cout << "contact_points: " << _result.getContact(i).pos << std::endl;
		std::cout << "penetration_depth: " << _result.getContact(i).penetration_depth << std::endl;
		std::cout << "normal: " << _result.getContact(i).normal << std::endl;
		//std::cout << std::endl;
	}
	std::cout << std::endl;
}

/* ********************************************************************************************* */

TEST_F(COLLISION, BOX_BOX_X) {
	fcl::Box box1(2, 2, 2);
	fcl::Box box2(1, 1, 1);
	unrotatedTest(&box1, &box2, 1.0, 0); // x-axis
}

TEST_F(COLLISION, BOX_BOX_Y) {
	fcl::Box box1(2, 2, 2);
	fcl::Box box2(1, 1, 1);
	unrotatedTest(&box1, &box2, 1.0, 1); // y-axis
}

TEST_F(COLLISION, BOX_BOX_Z) {
	fcl::Box box1(2, 2, 2);
	fcl::Box box2(1, 1, 1);
	unrotatedTest(&box1, &box2, 1.0, 2); // z-axis
}

TEST_F(COLLISION, BOX_SPHERE_X) {
	fcl::Box box1(2, 2, 2);
	fcl::Sphere sphere(0.5);
	unrotatedTest(&box1, &sphere, 1.0, 0); // x-axis
}

TEST_F(COLLISION, BOX_SPHERE_Y) {
	fcl::Box box1(2, 2, 2);
	fcl::Sphere sphere(0.5);
	unrotatedTest(&box1, &sphere, 1.0, 1); // y-axis
}

TEST_F(COLLISION, BOX_SPHERE_Z) {
	fcl::Box box1(2, 2, 2);
	fcl::Sphere sphere(0.5);
	unrotatedTest(&box1, &sphere, 1.0, 2); // z-axis
}

//TEST_F(COLLISION, SPHERE_SPHERE_X) {
//	fcl::Sphere sphere1(1);
//	fcl::Sphere sphere2(0.5);
//	unrotatedTest(&sphere1, &sphere2, 1.0, 0); // x-axis
//}

//TEST_F(COLLISION, SPHERE_SPHERE_Y) {
//	fcl::Sphere sphere1(1);
//	fcl::Sphere sphere2(0.5);
//	unrotatedTest(&sphere1, &sphere2, 1.0, 1); // y-axis
//}

//TEST_F(COLLISION, SPHERE_SPHERE_Z) {
//	fcl::Sphere sphere1(1);
//	fcl::Sphere sphere2(0.5);
//	unrotatedTest(&sphere1, &sphere2, 1.0, 2); // z-axis
//}

//TEST_F(COLLISION, PLANE_BOX_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Box obj2(1, 1, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_BOX_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Box obj2(1, 1, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_BOX_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Box obj2(1, 1, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

//TEST_F(COLLISION, PLANE_SPHERE_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Sphere obj2(0.5);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_SPHERE_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Sphere obj2(0.5);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_SPHERE_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Sphere obj2(0.5);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

//TEST_F(COLLISION, PLANE_CYLINDER_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Cylinder obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_CYLINDER_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Cylinder obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_CYLINDER_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Cylinder obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

//TEST_F(COLLISION, PLANE_CAPSULE_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Capsule obj2(0.5, 2);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_CAPSULE_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Capsule obj2(0.5, 2);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_CAPSULE_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Capsule obj2(0.5, 2);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

//TEST_F(COLLISION, PLANE_CONE_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Cone obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_CONE_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Cone obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_CONE_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Cone obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

/* ********************************************************************************************* */
int main(int argc, char* argv[]) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
/* ********************************************************************************************* */

<commit_msg>Uncomment sphere-sphere collision tests since I fixed the bug in FCL and they run successfully now<commit_after>/*
 * Copyright (c) 2011, Georgia Tech Research Corporation
 * All rights reserved.
 *
 * Author(s): Jeongseok Lee <jslee02@gmail.com>
 * Date: 05/07/2013
 *
 * Geoorgia Tech Graphics Lab and Humanoid Robotics Lab
 *
 * Directed by Prof. C. Karen Liu and Prof. Mike Stilman
 * <karenliu@cc.gatech.edu> <mstilman@cc.gatech.edu>
 *
 * This file is provided under the following "BSD-style" License:
 *   Redistribution and use in source and binary forms, with or
 *   without modification, are permitted provided that the following
 *   conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <gtest/gtest.h>

#include "fcl/collision.h"
#include "fcl/shape/geometric_shapes.h"
#include "fcl/narrowphase/narrowphase.h"

class COLLISION : public testing::Test
{
public:
	void unrotatedTest(fcl::CollisionGeometry* _coll1,
					   fcl::CollisionGeometry* _coll2,
					   double expectedContactPoint, int _idxAxis);
	void printResult(const fcl::CollisionResult& _result);
};

void COLLISION::unrotatedTest(fcl::CollisionGeometry* _coll1,
							  fcl::CollisionGeometry* _coll2,
							  double expectedContactPoint,
							  int _idxAxis)
{
	fcl::CollisionResult result;
	fcl::CollisionRequest request;
	request.enable_contact = true;
	request.num_max_contacts = 100;

	fcl::Vec3f position(0, 0, 0);

	fcl::Transform3f coll1_transform;
	fcl::Transform3f coll2_transform;

	//==========================================================================
	// Approaching test
	//==========================================================================
	result.clear();
	double dpos = -0.001;
	double pos = 10.0;

	coll1_transform.setIdentity();
	coll1_transform.setTranslation(fcl::Vec3f(0, 0, 0));
	coll2_transform.setIdentity();

	// Let's drop box2 until it collide with box1
	do {
		position[_idxAxis] = pos;
		coll2_transform.setTranslation(position);

		fcl::collide(_coll1, coll1_transform,
					 _coll2, coll2_transform,
					 request, result);

		pos += dpos;
	}
	while (result.numContacts() == 0);

	//
	if (_idxAxis == 0)
		std::cout << "The object is collided when its x-axis position is: " << (pos - dpos) << std::endl;
	if (_idxAxis == 1)
		std::cout << "The object is collided when its y-axis position is: " << (pos - dpos) << std::endl;
	if (_idxAxis == 2)
		std::cout << "The object is collided when its z-axis position is: " << (pos - dpos) << std::endl;

	//printResult(result);

	for (int i = 0; i < result.numContacts(); ++i)
	{
		EXPECT_GE(result.getContact(i).penetration_depth, 0.0);
//		EXPECT_NEAR(result.getContact(i).normal[_idxAxis], -1.0);
		EXPECT_EQ(result.getContact(i).normal.length(), 1.0);
		EXPECT_NEAR(result.getContact(i).pos[_idxAxis], expectedContactPoint, -dpos*2.0);
	}
}

void COLLISION::printResult(const fcl::CollisionResult& _result)
{
	std::cout << "====== [ RESULT ] ======" << std::endl;
	std::cout << "The number of contacts: " << _result.numContacts() << std::endl;

	for (int i = 0; i < _result.numContacts(); ++i)
	{
		std::cout << "----- CONTACT " << i << " --------" << std::endl;
		std::cout << "contact_points: " << _result.getContact(i).pos << std::endl;
		std::cout << "penetration_depth: " << _result.getContact(i).penetration_depth << std::endl;
		std::cout << "normal: " << _result.getContact(i).normal << std::endl;
		//std::cout << std::endl;
	}
	std::cout << std::endl;
}

/* ********************************************************************************************* */

TEST_F(COLLISION, BOX_BOX_X) {
	fcl::Box box1(2, 2, 2);
	fcl::Box box2(1, 1, 1);
	unrotatedTest(&box1, &box2, 1.0, 0); // x-axis
}

TEST_F(COLLISION, BOX_BOX_Y) {
	fcl::Box box1(2, 2, 2);
	fcl::Box box2(1, 1, 1);
	unrotatedTest(&box1, &box2, 1.0, 1); // y-axis
}

TEST_F(COLLISION, BOX_BOX_Z) {
	fcl::Box box1(2, 2, 2);
	fcl::Box box2(1, 1, 1);
	unrotatedTest(&box1, &box2, 1.0, 2); // z-axis
}

TEST_F(COLLISION, BOX_SPHERE_X) {
	fcl::Box box1(2, 2, 2);
	fcl::Sphere sphere(0.5);
	unrotatedTest(&box1, &sphere, 1.0, 0); // x-axis
}

TEST_F(COLLISION, BOX_SPHERE_Y) {
	fcl::Box box1(2, 2, 2);
	fcl::Sphere sphere(0.5);
	unrotatedTest(&box1, &sphere, 1.0, 1); // y-axis
}

TEST_F(COLLISION, BOX_SPHERE_Z) {
	fcl::Box box1(2, 2, 2);
	fcl::Sphere sphere(0.5);
	unrotatedTest(&box1, &sphere, 1.0, 2); // z-axis
}

TEST_F(COLLISION, SPHERE_SPHERE_X) {
	fcl::Sphere sphere1(1);
	fcl::Sphere sphere2(0.5);
	unrotatedTest(&sphere1, &sphere2, 1.0, 0); // x-axis
}

TEST_F(COLLISION, SPHERE_SPHERE_Y) {
	fcl::Sphere sphere1(1);
	fcl::Sphere sphere2(0.5);
	unrotatedTest(&sphere1, &sphere2, 1.0, 1); // y-axis
}

TEST_F(COLLISION, SPHERE_SPHERE_Z) {
	fcl::Sphere sphere1(1);
	fcl::Sphere sphere2(0.5);
	unrotatedTest(&sphere1, &sphere2, 1.0, 2); // z-axis
}

//TEST_F(COLLISION, PLANE_BOX_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Box obj2(1, 1, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_BOX_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Box obj2(1, 1, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_BOX_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Box obj2(1, 1, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

//TEST_F(COLLISION, PLANE_SPHERE_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Sphere obj2(0.5);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_SPHERE_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Sphere obj2(0.5);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_SPHERE_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Sphere obj2(0.5);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

//TEST_F(COLLISION, PLANE_CYLINDER_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Cylinder obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_CYLINDER_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Cylinder obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_CYLINDER_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Cylinder obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

//TEST_F(COLLISION, PLANE_CAPSULE_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Capsule obj2(0.5, 2);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_CAPSULE_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Capsule obj2(0.5, 2);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_CAPSULE_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Capsule obj2(0.5, 2);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

//TEST_F(COLLISION, PLANE_CONE_X) {
//	fcl::Plane obj1(1, 0, 0, 0);
//	fcl::Cone obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 0); // x-axis
//}

//TEST_F(COLLISION, PLANE_CONE_Y) {
//	fcl::Plane obj1(0, 1, 0, 0);
//	fcl::Cone obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 1); // x-axis
//}

//TEST_F(COLLISION, PLANE_CONE_Z) {
//	fcl::Plane obj1(0, 0, 1, 0);
//	fcl::Cone obj2(0.5, 1);
//	unrotatedTest(&obj1, &obj2, 0.0, 2); // x-axis
//}

/* ********************************************************************************************* */
int main(int argc, char* argv[]) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
/* ********************************************************************************************* */

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "core/page/Settings.h"

#include <limits>
#include "core/dom/Document.h"
#include "core/fetch/ResourceFetcher.h"
#include "core/inspector/InspectorInstrumentation.h"
#include "core/page/Chrome.h"
#include "core/page/Frame.h"
#include "core/page/FrameTree.h"
#include "core/page/FrameView.h"
#include "core/page/Page.h"
#include "core/rendering/TextAutosizer.h"

using namespace std;

namespace WebCore {

static void setImageLoadingSettings(Page* page)
{
    for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        frame->document()->fetcher()->setImagesEnabled(page->settings().areImagesEnabled());
        frame->document()->fetcher()->setAutoLoadImages(page->settings().loadsImagesAutomatically());
    }
}

// Sets the entry in the font map for the given script. If family is the empty string, removes the entry instead.
static inline void setGenericFontFamilyMap(ScriptFontFamilyMap& fontMap, const AtomicString& family, UScriptCode script, Page* page)
{
    ScriptFontFamilyMap::iterator it = fontMap.find(static_cast<int>(script));
    if (family.isEmpty()) {
        if (it == fontMap.end())
            return;
        fontMap.remove(it);
    } else if (it != fontMap.end() && it->value == family)
        return;
    else
        fontMap.set(static_cast<int>(script), family);

    if (page)
        page->setNeedsRecalcStyleInAllFrames();
}

static inline const AtomicString& getGenericFontFamilyForScript(const ScriptFontFamilyMap& fontMap, UScriptCode script)
{
    ScriptFontFamilyMap::const_iterator it = fontMap.find(static_cast<int>(script));
    if (it != fontMap.end())
        return it->value;
    if (script != USCRIPT_COMMON)
        return getGenericFontFamilyForScript(fontMap, USCRIPT_COMMON);
    return emptyAtom;
}

bool Settings::gMockScrollbarsEnabled = false;
bool Settings::gUsesOverlayScrollbars = false;

// NOTEs
//  1) EditingMacBehavior comprises builds on Mac;
//  2) EditingWindowsBehavior comprises builds on Windows;
//  3) EditingUnixBehavior comprises all unix-based systems, but Darwin/MacOS/Android (and then abusing the terminology);
//  4) EditingAndroidBehavior comprises Android builds.
// 99) MacEditingBehavior is used a fallback.
static EditingBehaviorType editingBehaviorTypeForPlatform()
{
    return
#if OS(MACOSX)
    EditingMacBehavior
#elif OS(WIN)
    EditingWindowsBehavior
#elif OS(ANDROID)
    EditingAndroidBehavior
#elif OS(POSIX)
    EditingUnixBehavior
#else
    // Fallback
    EditingMacBehavior
#endif
    ;
}

static const bool defaultUnifiedTextCheckerEnabled = false;
#if OS(MACOSX)
static const bool defaultSmartInsertDeleteEnabled = true;
#else
static const bool defaultSmartInsertDeleteEnabled = false;
#endif
#if OS(WIN)
static const bool defaultSelectTrailingWhitespaceEnabled = true;
#else
static const bool defaultSelectTrailingWhitespaceEnabled = false;
#endif

Settings::Settings(Page* page)
    : m_page(0)
    , m_mediaTypeOverride("screen")
    , m_textAutosizingFontScaleFactor(1)
#if HACK_FORCE_TEXT_AUTOSIZING_ON_DESKTOP
    , m_textAutosizingWindowSizeOverride(320, 480)
    , m_textAutosizingEnabled(true)
#else
    , m_textAutosizingEnabled(false)
#endif
    , m_useWideViewport(true)
    , m_loadWithOverviewMode(true)
    SETTINGS_INITIALIZER_LIST
    , m_isJavaEnabled(false)
    , m_loadsImagesAutomatically(false)
    , m_areImagesEnabled(true)
    , m_arePluginsEnabled(false)
    , m_isScriptEnabled(false)
    , m_isCSSCustomFilterEnabled(false)
    , m_cssStickyPositionEnabled(true)
    , m_dnsPrefetchingEnabled(false)
    , m_touchEventEmulationEnabled(false)
    , m_viewportEnabled(false)
    , m_setImageLoadingSettingsTimer(this, &Settings::imageLoadingSettingsTimerFired)
    , m_compositorDrivenAcceleratedScrollingEnabled(false)
{
    m_page = page; // Page is not yet fully initialized wen constructing Settings, so keeping m_page null over initializeDefaultFontFamilies() call.
}

PassOwnPtr<Settings> Settings::create(Page* page)
{
    return adoptPtr(new Settings(page));
}

SETTINGS_SETTER_BODIES

const AtomicString& Settings::standardFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_standardFontFamilyMap, script);
}

void Settings::setStandardFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_standardFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::fixedFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_fixedFontFamilyMap, script);
}

void Settings::setFixedFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_fixedFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::serifFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_serifFontFamilyMap, script);
}

void Settings::setSerifFontFamily(const AtomicString& family, UScriptCode script)
{
     setGenericFontFamilyMap(m_serifFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::sansSerifFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_sansSerifFontFamilyMap, script);
}

void Settings::setSansSerifFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_sansSerifFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::cursiveFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_cursiveFontFamilyMap, script);
}

void Settings::setCursiveFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_cursiveFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::fantasyFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_fantasyFontFamilyMap, script);
}

void Settings::setFantasyFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_fantasyFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::pictographFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_pictographFontFamilyMap, script);
}

void Settings::setPictographFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_pictographFontFamilyMap, family, script, m_page);
}

void Settings::setTextAutosizingEnabled(bool textAutosizingEnabled)
{
    if (m_textAutosizingEnabled == textAutosizingEnabled)
        return;

    m_textAutosizingEnabled = textAutosizingEnabled;
    m_page->setNeedsRecalcStyleInAllFrames();
}

void Settings::setTextAutosizingWindowSizeOverride(const IntSize& textAutosizingWindowSizeOverride)
{
    if (m_textAutosizingWindowSizeOverride == textAutosizingWindowSizeOverride)
        return;

    m_textAutosizingWindowSizeOverride = textAutosizingWindowSizeOverride;
    m_page->setNeedsRecalcStyleInAllFrames();
}

void Settings::setUseWideViewport(bool useWideViewport)
{
    if (m_useWideViewport == useWideViewport)
        return;

    m_useWideViewport = useWideViewport;
    if (m_page->mainFrame())
        m_page->chrome().dispatchViewportPropertiesDidChange(m_page->mainFrame()->document()->viewportArguments());
}

void Settings::setLoadWithOverviewMode(bool loadWithOverviewMode)
{
    if (m_loadWithOverviewMode == loadWithOverviewMode)
        return;

    m_loadWithOverviewMode = loadWithOverviewMode;
    if (m_page->mainFrame())
        m_page->chrome().dispatchViewportPropertiesDidChange(m_page->mainFrame()->document()->viewportArguments());
}

void Settings::setTextAutosizingFontScaleFactor(float fontScaleFactor)
{
    m_textAutosizingFontScaleFactor = fontScaleFactor;

    // FIXME: I wonder if this needs to traverse frames like in WebViewImpl::resize, or whether there is only one document per Settings instance?
    for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext())
        frame->document()->textAutosizer()->recalculateMultipliers();

    m_page->setNeedsRecalcStyleInAllFrames();
}

void Settings::setMediaTypeOverride(const String& mediaTypeOverride)
{
    if (m_mediaTypeOverride == mediaTypeOverride)
        return;

    m_mediaTypeOverride = mediaTypeOverride;

    Frame* mainFrame = m_page->mainFrame();
    ASSERT(mainFrame);
    FrameView* view = mainFrame->view();
    ASSERT(view);

    view->setMediaType(mediaTypeOverride);
    m_page->setNeedsRecalcStyleInAllFrames();
}

void Settings::setLoadsImagesAutomatically(bool loadsImagesAutomatically)
{
    m_loadsImagesAutomatically = loadsImagesAutomatically;

    // Changing this setting to true might immediately start new loads for images that had previously had loading disabled.
    // If this happens while a WebView is being dealloc'ed, and we don't know the WebView is being dealloc'ed, these new loads
    // can cause crashes downstream when the WebView memory has actually been free'd.
    // One example where this can happen is in Mac apps that subclass WebView then do work in their overridden dealloc methods.
    // Starting these loads synchronously is not important.  By putting it on a 0-delay, properly closing the Page cancels them
    // before they have a chance to really start.
    // See http://webkit.org/b/60572 for more discussion.
    m_setImageLoadingSettingsTimer.startOneShot(0);
}

void Settings::imageLoadingSettingsTimerFired(Timer<Settings>*)
{
    setImageLoadingSettings(m_page);
}

void Settings::setScriptEnabled(bool isScriptEnabled)
{
    m_isScriptEnabled = isScriptEnabled;
    InspectorInstrumentation::scriptsEnabled(m_page, m_isScriptEnabled);
}

void Settings::setJavaEnabled(bool isJavaEnabled)
{
    m_isJavaEnabled = isJavaEnabled;
}

void Settings::setImagesEnabled(bool areImagesEnabled)
{
    m_areImagesEnabled = areImagesEnabled;

    // See comment in setLoadsImagesAutomatically.
    m_setImageLoadingSettingsTimer.startOneShot(0);
}

void Settings::setPluginsEnabled(bool arePluginsEnabled)
{
    m_arePluginsEnabled = arePluginsEnabled;
}

void Settings::setUserStyleSheetLocation(const KURL& userStyleSheetLocation)
{
    if (m_userStyleSheetLocation == userStyleSheetLocation)
        return;

    m_userStyleSheetLocation = userStyleSheetLocation;

    m_page->userStyleSheetLocationChanged();
}

void Settings::setDNSPrefetchingEnabled(bool dnsPrefetchingEnabled)
{
    if (m_dnsPrefetchingEnabled == dnsPrefetchingEnabled)
        return;

    m_dnsPrefetchingEnabled = dnsPrefetchingEnabled;
    m_page->dnsPrefetchingStateChanged();
}

void Settings::setMockScrollbarsEnabled(bool flag)
{
    gMockScrollbarsEnabled = flag;
}

bool Settings::mockScrollbarsEnabled()
{
    return gMockScrollbarsEnabled;
}

void Settings::setUsesOverlayScrollbars(bool flag)
{
    gUsesOverlayScrollbars = flag;
}

bool Settings::usesOverlayScrollbars()
{
    return gUsesOverlayScrollbars;
}

void Settings::setOpenGLMultisamplingEnabled(bool flag)
{
    if (m_openGLMultisamplingEnabled == flag)
        return;

    m_openGLMultisamplingEnabled = flag;
    m_page->multisamplingChanged();
}

bool Settings::openGLMultisamplingEnabled()
{
    return m_openGLMultisamplingEnabled;
}

void Settings::setViewportEnabled(bool enabled)
{
    if (m_viewportEnabled == enabled)
        return;

    m_viewportEnabled = enabled;
    if (m_page->mainFrame())
        m_page->mainFrame()->document()->updateViewportArguments();
}

} // namespace WebCore
<commit_msg>Explicitly initialized GL Multisampling setting<commit_after>/*
 * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2012 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "core/page/Settings.h"

#include <limits>
#include "core/dom/Document.h"
#include "core/fetch/ResourceFetcher.h"
#include "core/inspector/InspectorInstrumentation.h"
#include "core/page/Chrome.h"
#include "core/page/Frame.h"
#include "core/page/FrameTree.h"
#include "core/page/FrameView.h"
#include "core/page/Page.h"
#include "core/rendering/TextAutosizer.h"

using namespace std;

namespace WebCore {

static void setImageLoadingSettings(Page* page)
{
    for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
        frame->document()->fetcher()->setImagesEnabled(page->settings().areImagesEnabled());
        frame->document()->fetcher()->setAutoLoadImages(page->settings().loadsImagesAutomatically());
    }
}

// Sets the entry in the font map for the given script. If family is the empty string, removes the entry instead.
static inline void setGenericFontFamilyMap(ScriptFontFamilyMap& fontMap, const AtomicString& family, UScriptCode script, Page* page)
{
    ScriptFontFamilyMap::iterator it = fontMap.find(static_cast<int>(script));
    if (family.isEmpty()) {
        if (it == fontMap.end())
            return;
        fontMap.remove(it);
    } else if (it != fontMap.end() && it->value == family)
        return;
    else
        fontMap.set(static_cast<int>(script), family);

    if (page)
        page->setNeedsRecalcStyleInAllFrames();
}

static inline const AtomicString& getGenericFontFamilyForScript(const ScriptFontFamilyMap& fontMap, UScriptCode script)
{
    ScriptFontFamilyMap::const_iterator it = fontMap.find(static_cast<int>(script));
    if (it != fontMap.end())
        return it->value;
    if (script != USCRIPT_COMMON)
        return getGenericFontFamilyForScript(fontMap, USCRIPT_COMMON);
    return emptyAtom;
}

bool Settings::gMockScrollbarsEnabled = false;
bool Settings::gUsesOverlayScrollbars = false;

// NOTEs
//  1) EditingMacBehavior comprises builds on Mac;
//  2) EditingWindowsBehavior comprises builds on Windows;
//  3) EditingUnixBehavior comprises all unix-based systems, but Darwin/MacOS/Android (and then abusing the terminology);
//  4) EditingAndroidBehavior comprises Android builds.
// 99) MacEditingBehavior is used a fallback.
static EditingBehaviorType editingBehaviorTypeForPlatform()
{
    return
#if OS(MACOSX)
    EditingMacBehavior
#elif OS(WIN)
    EditingWindowsBehavior
#elif OS(ANDROID)
    EditingAndroidBehavior
#elif OS(POSIX)
    EditingUnixBehavior
#else
    // Fallback
    EditingMacBehavior
#endif
    ;
}

static const bool defaultUnifiedTextCheckerEnabled = false;
#if OS(MACOSX)
static const bool defaultSmartInsertDeleteEnabled = true;
#else
static const bool defaultSmartInsertDeleteEnabled = false;
#endif
#if OS(WIN)
static const bool defaultSelectTrailingWhitespaceEnabled = true;
#else
static const bool defaultSelectTrailingWhitespaceEnabled = false;
#endif

Settings::Settings(Page* page)
    : m_page(0)
    , m_mediaTypeOverride("screen")
    , m_textAutosizingFontScaleFactor(1)
#if HACK_FORCE_TEXT_AUTOSIZING_ON_DESKTOP
    , m_textAutosizingWindowSizeOverride(320, 480)
    , m_textAutosizingEnabled(true)
#else
    , m_textAutosizingEnabled(false)
#endif
    , m_useWideViewport(true)
    , m_loadWithOverviewMode(true)
    SETTINGS_INITIALIZER_LIST
    , m_isJavaEnabled(false)
    , m_loadsImagesAutomatically(false)
    , m_areImagesEnabled(true)
    , m_arePluginsEnabled(false)
    , m_isScriptEnabled(false)
    , m_isCSSCustomFilterEnabled(false)
    , m_cssStickyPositionEnabled(true)
    , m_dnsPrefetchingEnabled(false)
    , m_touchEventEmulationEnabled(false)
    , m_openGLMultisamplingEnabled(false)
    , m_viewportEnabled(false)
    , m_setImageLoadingSettingsTimer(this, &Settings::imageLoadingSettingsTimerFired)
    , m_compositorDrivenAcceleratedScrollingEnabled(false)
{
    m_page = page; // Page is not yet fully initialized wen constructing Settings, so keeping m_page null over initializeDefaultFontFamilies() call.
}

PassOwnPtr<Settings> Settings::create(Page* page)
{
    return adoptPtr(new Settings(page));
}

SETTINGS_SETTER_BODIES

const AtomicString& Settings::standardFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_standardFontFamilyMap, script);
}

void Settings::setStandardFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_standardFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::fixedFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_fixedFontFamilyMap, script);
}

void Settings::setFixedFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_fixedFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::serifFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_serifFontFamilyMap, script);
}

void Settings::setSerifFontFamily(const AtomicString& family, UScriptCode script)
{
     setGenericFontFamilyMap(m_serifFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::sansSerifFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_sansSerifFontFamilyMap, script);
}

void Settings::setSansSerifFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_sansSerifFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::cursiveFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_cursiveFontFamilyMap, script);
}

void Settings::setCursiveFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_cursiveFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::fantasyFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_fantasyFontFamilyMap, script);
}

void Settings::setFantasyFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_fantasyFontFamilyMap, family, script, m_page);
}

const AtomicString& Settings::pictographFontFamily(UScriptCode script) const
{
    return getGenericFontFamilyForScript(m_pictographFontFamilyMap, script);
}

void Settings::setPictographFontFamily(const AtomicString& family, UScriptCode script)
{
    setGenericFontFamilyMap(m_pictographFontFamilyMap, family, script, m_page);
}

void Settings::setTextAutosizingEnabled(bool textAutosizingEnabled)
{
    if (m_textAutosizingEnabled == textAutosizingEnabled)
        return;

    m_textAutosizingEnabled = textAutosizingEnabled;
    m_page->setNeedsRecalcStyleInAllFrames();
}

void Settings::setTextAutosizingWindowSizeOverride(const IntSize& textAutosizingWindowSizeOverride)
{
    if (m_textAutosizingWindowSizeOverride == textAutosizingWindowSizeOverride)
        return;

    m_textAutosizingWindowSizeOverride = textAutosizingWindowSizeOverride;
    m_page->setNeedsRecalcStyleInAllFrames();
}

void Settings::setUseWideViewport(bool useWideViewport)
{
    if (m_useWideViewport == useWideViewport)
        return;

    m_useWideViewport = useWideViewport;
    if (m_page->mainFrame())
        m_page->chrome().dispatchViewportPropertiesDidChange(m_page->mainFrame()->document()->viewportArguments());
}

void Settings::setLoadWithOverviewMode(bool loadWithOverviewMode)
{
    if (m_loadWithOverviewMode == loadWithOverviewMode)
        return;

    m_loadWithOverviewMode = loadWithOverviewMode;
    if (m_page->mainFrame())
        m_page->chrome().dispatchViewportPropertiesDidChange(m_page->mainFrame()->document()->viewportArguments());
}

void Settings::setTextAutosizingFontScaleFactor(float fontScaleFactor)
{
    m_textAutosizingFontScaleFactor = fontScaleFactor;

    // FIXME: I wonder if this needs to traverse frames like in WebViewImpl::resize, or whether there is only one document per Settings instance?
    for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext())
        frame->document()->textAutosizer()->recalculateMultipliers();

    m_page->setNeedsRecalcStyleInAllFrames();
}

void Settings::setMediaTypeOverride(const String& mediaTypeOverride)
{
    if (m_mediaTypeOverride == mediaTypeOverride)
        return;

    m_mediaTypeOverride = mediaTypeOverride;

    Frame* mainFrame = m_page->mainFrame();
    ASSERT(mainFrame);
    FrameView* view = mainFrame->view();
    ASSERT(view);

    view->setMediaType(mediaTypeOverride);
    m_page->setNeedsRecalcStyleInAllFrames();
}

void Settings::setLoadsImagesAutomatically(bool loadsImagesAutomatically)
{
    m_loadsImagesAutomatically = loadsImagesAutomatically;

    // Changing this setting to true might immediately start new loads for images that had previously had loading disabled.
    // If this happens while a WebView is being dealloc'ed, and we don't know the WebView is being dealloc'ed, these new loads
    // can cause crashes downstream when the WebView memory has actually been free'd.
    // One example where this can happen is in Mac apps that subclass WebView then do work in their overridden dealloc methods.
    // Starting these loads synchronously is not important.  By putting it on a 0-delay, properly closing the Page cancels them
    // before they have a chance to really start.
    // See http://webkit.org/b/60572 for more discussion.
    m_setImageLoadingSettingsTimer.startOneShot(0);
}

void Settings::imageLoadingSettingsTimerFired(Timer<Settings>*)
{
    setImageLoadingSettings(m_page);
}

void Settings::setScriptEnabled(bool isScriptEnabled)
{
    m_isScriptEnabled = isScriptEnabled;
    InspectorInstrumentation::scriptsEnabled(m_page, m_isScriptEnabled);
}

void Settings::setJavaEnabled(bool isJavaEnabled)
{
    m_isJavaEnabled = isJavaEnabled;
}

void Settings::setImagesEnabled(bool areImagesEnabled)
{
    m_areImagesEnabled = areImagesEnabled;

    // See comment in setLoadsImagesAutomatically.
    m_setImageLoadingSettingsTimer.startOneShot(0);
}

void Settings::setPluginsEnabled(bool arePluginsEnabled)
{
    m_arePluginsEnabled = arePluginsEnabled;
}

void Settings::setUserStyleSheetLocation(const KURL& userStyleSheetLocation)
{
    if (m_userStyleSheetLocation == userStyleSheetLocation)
        return;

    m_userStyleSheetLocation = userStyleSheetLocation;

    m_page->userStyleSheetLocationChanged();
}

void Settings::setDNSPrefetchingEnabled(bool dnsPrefetchingEnabled)
{
    if (m_dnsPrefetchingEnabled == dnsPrefetchingEnabled)
        return;

    m_dnsPrefetchingEnabled = dnsPrefetchingEnabled;
    m_page->dnsPrefetchingStateChanged();
}

void Settings::setMockScrollbarsEnabled(bool flag)
{
    gMockScrollbarsEnabled = flag;
}

bool Settings::mockScrollbarsEnabled()
{
    return gMockScrollbarsEnabled;
}

void Settings::setUsesOverlayScrollbars(bool flag)
{
    gUsesOverlayScrollbars = flag;
}

bool Settings::usesOverlayScrollbars()
{
    return gUsesOverlayScrollbars;
}

void Settings::setOpenGLMultisamplingEnabled(bool flag)
{
    if (m_openGLMultisamplingEnabled == flag)
        return;

    m_openGLMultisamplingEnabled = flag;
    m_page->multisamplingChanged();
}

bool Settings::openGLMultisamplingEnabled()
{
    return m_openGLMultisamplingEnabled;
}

void Settings::setViewportEnabled(bool enabled)
{
    if (m_viewportEnabled == enabled)
        return;

    m_viewportEnabled = enabled;
    if (m_page->mainFrame())
        m_page->mainFrame()->document()->updateViewportArguments();
}

} // namespace WebCore
<|endoftext|>


<commit_before>/*
 * Worldvisions Weaver Software:
 *   Copyright (C) 1997-2000 Net Integration Technologies, Inc.
 *
 * Intelligent serial port scanner: try to find a port (or ports)
 * with a working modem, guess a basic startup init string, and find
 * the maximum baud rate.
 */
#include "wvmodemscan.h"
#include "wvmodem.h"
#include "strutils.h"
#include <time.h>
#include <assert.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/stat.h>

// startup at atz atq0 atv1 ate1 ats0 carrier dtr fastdial
// baudstep reinit done
static char *commands[WvModemScan::NUM_STAGES] = {
    NULL, "Q0 V1 E1", "Z", "S0=0",
    "&C1", "&D2", "S11=55", "+FCLASS=0", NULL,
    NULL, "", NULL
};

WvModemScan::WvModemScan(const char *devname)
	: debug(devname, WvLog::Debug)
{
    stage = Startup;
    memset(status, 0, sizeof(status));

    if (devname[0] == '/')
	file = devname;
    else
	file = WvString("/dev/%s", devname);

    baud = 1200;
    modem = NULL;
    tries = 0;
    broken = false;
}

WvModemScan::~WvModemScan()
{
    if (isok() && isdone())
	debug(WvLog::Info, "Speed %s; init \"%s\"\n", maxbaud(), initstr());

    if (modem)
	delete modem;
}

bool WvModemScan::isok() const
{
    return !broken;
}

WvString WvModemScan::initstr() const
{
    char s[200];

    strcpy(s, "AT");

    for (int i = 0; i < NUM_STAGES; i++)
    {
	if (status[i] != Worked && status[i] != Test)
	    continue;
	if (!commands[i] || !commands[i][0])
	    continue;
	if ((commands[i][0]=='Z' || commands[i][0]=='I') && status[i] != Test)
	    continue;

	strcat(s, commands[i]);
	strcat(s, " ");
    }

    return WvString(trim_string(s)).unique();
}

void WvModemScan::execute()
{
    if (isdone() || !isok()) return;

    switch ((Stage)stage)
    {
    case Startup:
	assert(!modem);
	modem = new WvModem(file, baud);
	modem->die_fast = true;
	if (!modem->isok())
	{
	    if (modem->geterr()
		&& modem->geterr() != EIO
		&& modem->geterr() != ENOENT
		&& modem->geterr() != ENODEV)
	    {
		debug(WvLog::Info, "%s\n", modem->errstr());
	    }
	    broken = true;
	}
	else
	    stage++;
	break;

    case AT:
    case ATZ:
    case ATS0:
    case Carrier:
    case DTR:
    case FastDial:
    case FCLASS:
    case Reinit:
	assert(modem);
	status[stage] = Test;
	if ( !doresult(WvString("%s\r", initstr()), stage==ATZ ? 3000 : 500)
	    || ((stage <= ATZ || stage == Reinit) && status[stage]==Fail) )
	{
	    tries++;

	    if (tries >= 3)
	    {
		debug("nothing.\n");
		broken = true;
	    }

	    // else try again shortly
	}
	else
	{
	    tries = 0;
	    stage++;
	}
	break;

    case GetIdent:
	assert(modem);
	status[stage] = Test;
	debug("Modem Identifier: ");
	if ( !doresult(WvString("ATI\r"), 500) || (status[stage]==Fail) )
	{
	    tries++;

	    if (tries >= 3)
	    	debug("nothing.\n");

	    // else try again shortly
	}
	else
	{
	    if (is_isdn())
	    	debug("Looks like an ISDN modem.\n");
	    tries = 0;
	    stage++;
	}

    case BaudStep:
	assert(modem);
	modem->drain();
	modem->speed(baud*2);

	// if we try 2*baud three times without success, or setting 2*baud
	// results in a lower setting than 1*baud, we have reached the
	// top speed of the modem or the serial port, respectively.
	if (tries >= 3 || modem->getspeed() <= baud)
	{
	    // using the absolute maximum baud rate confuses many slower modems
	    // in obscure ways; step down one.
	    baud = modem->speed(baud);
	    debug("Max speed is %s; ", baud);
	    if (is_isdn())
	    {
		modem->speed(115200);
	    	baud = modem->getspeed();
	    	debug("using %s for ISDN modem.\n", baud);
	    }
	    else
	    {
		if (modem->getspeed() < 115200)
		{
		    // (baud - 1) will resolve to next lower rate
		    baud = modem->speed(baud - 1);
		    debug("using %s to be safe.\n", baud);
		}
		else
		{
		    debug("that should be safe.\n");
		}
	    }

	    stage++;
	    status[stage] = Worked;
	    break;
	}

	debug("Speed %s: ", modem->getspeed());

	if (!doresult("AT\r", 500) || status[stage] == Fail)
	{
	    tries++;
	}
	else // got a response
	{
	    baud = modem->getspeed();
	    tries = 0;
	    // next time through we try a faster speed
	}
	break;

    case Done:
    case NUM_STAGES:
	assert(0);
	break; // should never happen
    }

    if (stage == Done) // we just incremented stage number to Done
    {
	if (modem)
	    delete modem;
	modem = NULL;
    }
}

bool WvModemScan::doresult(const WvString &_s, int msec)
{
    char buf[1024], *cptr;
    size_t len;
    WvString s(_s);

    modem->drain();
    usleep(50 * 1000); // delay a bit after emptying the buffer
    modem->write(s);

    debug("%s -- ", trim_string(s.edit()));

    len = coagulate(buf, sizeof(buf), msec);

    if (!len)
    {
	// debug("(no answer yet)\n");
	return false;
    }

    buf[len] = 0;

    cptr = trim_string(buf);
    while (strchr(cptr, '\r'))
    {
	cptr = trim_string(strchr(cptr, '\r'));
	if (stage == GetIdent && status[stage] == Test)
	{
	    char *p = strpbrk(cptr, "\n\r");
	    if (p) *p=0;
	    identifier = cptr;
	    identifier.unique();
	    status[stage] = Worked;
	    debug("%s\n", identifier);
	    return true;
	}
    }
    while (strchr(cptr, '\n'))
	cptr = trim_string(strchr(cptr, '\n'));

    debug("%s\n", cptr);

    if (!strncmp(cptr, "OK", 2))
	status[stage] = Worked;
    else
	status[stage] = Fail;

    return true;
}

size_t WvModemScan::coagulate(char *buf, size_t size, int msec)
{
    size_t len = 0, amt;
    char *cptr = buf;

    assert(modem);

    if (!modem->isok())
    {
	broken = true;
	return 0;
    }

    while (modem->select(msec))
    {
	amt = modem->read(cptr, size-1);
	cptr[amt] = 0;

	len += amt;
	size -= amt;
	cptr += amt;

	if (strstr(buf, "OK") || strstr(buf, "ERROR"))
	    break;
    }

    return len;
}

const char *WvModemScan::is_isdn() const
{
    if (!identifier)
    	return NULL;

    if (identifier == "3C882")		// 3Com Impact IQ
    	return identifier;
    if (identifier == "346800")		// USR ISDN TA
    	return identifier;
    if (identifier == "960")		// Motorola BitSurfr
    	return identifier;

    return NULL;
}

static int fileselect(const struct dirent *e)
{
    return !strncmp(e->d_name, "ttyS", 4);     // serial
       // (no internal ISDN support)   || !strncmp(e->d_name, "ttyI", 4);
}

static int filesort(const void *_e1, const void *_e2)
{
    dirent const * const *e1 = (dirent const * const *)_e1;
    dirent const * const *e2 = (dirent const * const *)_e2;
    const char *p1, *p2;
    int diff;

    for (p1=(*e1)->d_name, p2=(*e2)->d_name; *p1 || *p2; p1++, p2++)
    {
	if (!isdigit(*p1) || !isdigit(*p2))
	{
	    diff = *p1 - *p2;
	    if (diff) return diff;
	}
	else // both are digits
	{
	    return atoi(p1) - atoi(p2);
	}
    }

    return 0;
}

void WvModemScanList::setup()
{
    struct dirent **namelist;
    struct stat mouse, modem;
    int num, count, mousestat, modemstat;

    log = new WvLog("Port Scan", WvLog::Debug);
    thisline = -1;
    printed = false;

    mousestat = stat("/dev/mouse", &mouse);
    modemstat = stat("/dev/modem", &modem);
    num = scandir("/dev", &namelist, fileselect, filesort);

    if (num < 0)
	return;

    for (count = 0; count < num; count++)
    {
	// never search the device assigned to /dev/mouse; most mouse-using
	// programs neglect to lock the device, so we could mess up the
	// mouse response!  (We are careful to put things back when done,
	// but X seems to still get confused.)  Anyway the mouse is seldom
	// a modem.
	if (mousestat==0 && mouse.st_ino == (ino_t)namelist[count]->d_ino)
	{
	    log->print("\nIgnoring %s because /dev/mouse is a link to it.\n",
		       namelist[count]->d_name);
	    continue;
	}

	// bump /dev/modem to the top of the list, if it exists
	if (modemstat==0 && modem.st_ino == (ino_t)namelist[count]->d_ino)
	    prepend(new WvModemScan(WvString("%s", namelist[count]->d_name)),
		    true);
	else
	    append(new WvModemScan(WvString("%s", namelist[count]->d_name)),
		   true);
    }

    while (--num >= 0)
	free(namelist[num]);
    free(namelist);
}

void WvModemScanList::shutdown()
{
    delete log;
}

// we used to try to scan all ports simultaneously; unfortunately, this
// caused problems when people had "noncritical" IRQ conflicts (ie. two
// serial ports with the same IRQ, but they work as long as only one port
// is used at a time).  Also, the log messages looked really confused.
//
// So now we do the scanning sequentially, which is slower.  The port
// being scanned is at the head of the list.  If the probe fails, we
// unlink the element.  If it succeeds, we have found a modem -- so,
// isdone() knows we are done when the _first_ element is done.
//
void WvModemScanList::execute()
{
    assert (!isdone());

    WvModemScanList::Iter i(*this);

    for (i.rewind(); i.next(); )
	if (!i().isdone()) break;

    if (!i.cur()) return;

    WvModemScan &s(i);

    if (!s.isok())
    {
	if (!printed)
	{
	    const WvString &f = s.filename();
	    const char *cptr = strrchr(f, '/');
	    if (cptr)
		cptr++;
	    else
		cptr = f;

	    if (!strncmp(cptr, "tty", 3))
		cptr += 3;

	    ++thisline %= 8;
	    if (!thisline)
		log->print("\n");

	    log->print("%-4s ", cptr);
	}

	i.unlink();
	printed = false;
    }
    else
    {
	s.execute();

	if (s.isok())
	    printed = true;
    }

    if (isdone())
	log->write("\n", 1);
}

bool WvModemScanList::isdone()
{
    WvModemScanList::Iter i(*this);

    for (i.rewind(); i.next(); )
	if (!i().isdone()) return false;

    return true;
}
<commit_msg><commit_after>/*
 * Worldvisions Weaver Software:
 *   Copyright (C) 1997-2000 Net Integration Technologies, Inc.
 *
 * Intelligent serial port scanner: try to find a port (or ports)
 * with a working modem, guess a basic startup init string, and find
 * the maximum baud rate.
 */
#include "wvmodemscan.h"
#include "wvmodem.h"
#include "strutils.h"
#include <time.h>
#include <assert.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/stat.h>

// startup at atz atq0 atv1 ate1 ats0 carrier dtr fastdial
// baudstep reinit done
static char *commands[WvModemScan::NUM_STAGES] = {
    NULL, "Q0 V1 E1", "Z", "S0=0",
    "&C1", "&D2", "S11=55", "+FCLASS=0", NULL,
    NULL, "", NULL
};

WvModemScan::WvModemScan(const char *devname)
	: debug(devname, WvLog::Debug)
{
    stage = Startup;
    memset(status, 0, sizeof(status));

    if (devname[0] == '/')
	file = devname;
    else
	file = WvString("/dev/%s", devname);

    baud = 1200;
    modem = NULL;
    tries = 0;
    broken = false;
}

WvModemScan::~WvModemScan()
{
    if (isok() && isdone())
	debug(WvLog::Info, "Speed %s; init \"%s\"\n", maxbaud(), initstr());

    if (modem)
	delete modem;
}

bool WvModemScan::isok() const
{
    return !broken;
}

WvString WvModemScan::initstr() const
{
    char s[200];

    strcpy(s, "AT");

    for (int i = 0; i < NUM_STAGES; i++)
    {
	if (status[i] != Worked && status[i] != Test)
	    continue;
	if (!commands[i] || !commands[i][0])
	    continue;
	if ((commands[i][0]=='Z' || commands[i][0]=='I') && status[i] != Test)
	    continue;

	strcat(s, commands[i]);
	strcat(s, " ");
    }

    return WvString(trim_string(s)).unique();
}

void WvModemScan::execute()
{
    if (isdone() || !isok()) return;

    switch ((Stage)stage)
    {
    case Startup:
	assert(!modem);
	modem = new WvModem(file, baud);
	modem->die_fast = true;
	if (!modem->isok())
	{
	    if (modem->geterr()
		&& modem->geterr() != EIO
		&& modem->geterr() != ENOENT
		&& modem->geterr() != ENODEV)
	    {
		debug(WvLog::Info, "%s\n", modem->errstr());
	    }
	    broken = true;
	}
	else
	    stage++;
	break;

    case AT:
    case ATZ:
    case ATS0:
    case Carrier:
    case DTR:
    case FastDial:
    case FCLASS:
    case Reinit:
	assert(modem);
	status[stage] = Test;
	if ( !doresult(WvString("%s\r", initstr()), stage==ATZ ? 3000 : 500)
	    || ((stage <= ATZ || stage == Reinit) && status[stage]==Fail) )
	{
	    tries++;

	    if (tries >= 3)
	    {
		debug("nothing.\n");
		broken = true;
	    }

	    // else try again shortly
	}
	else
	{
	    tries = 0;
	    stage++;
	}
	break;

    case GetIdent:
	assert(modem);
	status[stage] = Test;
	debug("Modem Identifier: ");
	if ( !doresult(WvString("ATI\r"), 500) || (status[stage]==Fail) )
	{
	    tries++;

	    if (tries >= 3)
	    	debug("nothing.\n");

	    // else try again shortly
	}
	else
	{
	    if (is_isdn())
	    	debug("Looks like an ISDN modem.\n");
	    tries = 0;
	    stage++;
	}

    case BaudStep:
	assert(modem);
	modem->drain();
	modem->speed(baud*2);

	// if we try 2*baud three times without success, or setting 2*baud
	// results in a lower setting than 1*baud, we have reached the
	// top speed of the modem or the serial port, respectively.
	if (tries >= 3 || modem->getspeed() <= baud)
	{
	    // using the absolute maximum baud rate confuses many slower modems
	    // in obscure ways; step down one.
	    baud = modem->speed(baud);
	    debug("Max speed is %s; that should be safe.\n", baud);

	    stage++;
	    status[stage] = Worked;
	    break;
	}

	debug("Speed %s: ", modem->getspeed());

	if (!doresult("AT\r", 500) || status[stage] == Fail)
	{
	    tries++;
	}
	else // got a response
	{
	    baud = modem->getspeed();
	    tries = 0;
	    // next time through we try a faster speed
	}
	break;

    case Done:
    case NUM_STAGES:
	assert(0);
	break; // should never happen
    }

    if (stage == Done) // we just incremented stage number to Done
    {
	if (modem)
	    delete modem;
	modem = NULL;
    }
}

bool WvModemScan::doresult(const WvString &_s, int msec)
{
    char buf[1024], *cptr;
    size_t len;
    WvString s(_s);

    modem->drain();
    usleep(50 * 1000); // delay a bit after emptying the buffer
    modem->write(s);

    debug("%s -- ", trim_string(s.edit()));

    len = coagulate(buf, sizeof(buf), msec);

    if (!len)
    {
	// debug("(no answer yet)\n");
	return false;
    }

    buf[len] = 0;

    cptr = trim_string(buf);
    while (strchr(cptr, '\r'))
    {
	cptr = trim_string(strchr(cptr, '\r'));
	if (stage == GetIdent && status[stage] == Test)
	{
	    char *p = strpbrk(cptr, "\n\r");
	    if (p) *p=0;
	    identifier = cptr;
	    identifier.unique();
	    status[stage] = Worked;
	    debug("%s\n", identifier);
	    return true;
	}
    }
    while (strchr(cptr, '\n'))
	cptr = trim_string(strchr(cptr, '\n'));

    debug("%s\n", cptr);

    if (!strncmp(cptr, "OK", 2))
	status[stage] = Worked;
    else
	status[stage] = Fail;

    return true;
}

size_t WvModemScan::coagulate(char *buf, size_t size, int msec)
{
    size_t len = 0, amt;
    char *cptr = buf;

    assert(modem);

    if (!modem->isok())
    {
	broken = true;
	return 0;
    }

    while (modem->select(msec))
    {
	amt = modem->read(cptr, size-1);
	cptr[amt] = 0;

	len += amt;
	size -= amt;
	cptr += amt;

	if (strstr(buf, "OK") || strstr(buf, "ERROR"))
	    break;
    }

    return len;
}

const char *WvModemScan::is_isdn() const
{
    if (!identifier)
    	return NULL;

    if (identifier == "3C882")		// 3Com Impact IQ
    	return identifier;
    if (identifier == "346800")		// USR ISDN TA
    	return identifier;
    if (identifier == "960")		// Motorola BitSurfr
    	return identifier;

    return NULL;
}

static int fileselect(const struct dirent *e)
{
    return !strncmp(e->d_name, "ttyS", 4);     // serial
       // (no internal ISDN support)   || !strncmp(e->d_name, "ttyI", 4);
}

static int filesort(const void *_e1, const void *_e2)
{
    dirent const * const *e1 = (dirent const * const *)_e1;
    dirent const * const *e2 = (dirent const * const *)_e2;
    const char *p1, *p2;
    int diff;

    for (p1=(*e1)->d_name, p2=(*e2)->d_name; *p1 || *p2; p1++, p2++)
    {
	if (!isdigit(*p1) || !isdigit(*p2))
	{
	    diff = *p1 - *p2;
	    if (diff) return diff;
	}
	else // both are digits
	{
	    return atoi(p1) - atoi(p2);
	}
    }

    return 0;
}

void WvModemScanList::setup()
{
    struct dirent **namelist;
    struct stat mouse, modem;
    int num, count, mousestat, modemstat;

    log = new WvLog("Port Scan", WvLog::Debug);
    thisline = -1;
    printed = false;

    mousestat = stat("/dev/mouse", &mouse);
    modemstat = stat("/dev/modem", &modem);
    num = scandir("/dev", &namelist, fileselect, filesort);

    if (num < 0)
	return;

    for (count = 0; count < num; count++)
    {
	// never search the device assigned to /dev/mouse; most mouse-using
	// programs neglect to lock the device, so we could mess up the
	// mouse response!  (We are careful to put things back when done,
	// but X seems to still get confused.)  Anyway the mouse is seldom
	// a modem.
	if (mousestat==0 && mouse.st_ino == (ino_t)namelist[count]->d_ino)
	{
	    log->print("\nIgnoring %s because /dev/mouse is a link to it.\n",
		       namelist[count]->d_name);
	    continue;
	}

	// bump /dev/modem to the top of the list, if it exists
	if (modemstat==0 && modem.st_ino == (ino_t)namelist[count]->d_ino)
	    prepend(new WvModemScan(WvString("%s", namelist[count]->d_name)),
		    true);
	else
	    append(new WvModemScan(WvString("%s", namelist[count]->d_name)),
		   true);
    }

    while (--num >= 0)
	free(namelist[num]);
    free(namelist);
}

void WvModemScanList::shutdown()
{
    delete log;
}

// we used to try to scan all ports simultaneously; unfortunately, this
// caused problems when people had "noncritical" IRQ conflicts (ie. two
// serial ports with the same IRQ, but they work as long as only one port
// is used at a time).  Also, the log messages looked really confused.
//
// So now we do the scanning sequentially, which is slower.  The port
// being scanned is at the head of the list.  If the probe fails, we
// unlink the element.  If it succeeds, we have found a modem -- so,
// isdone() knows we are done when the _first_ element is done.
//
void WvModemScanList::execute()
{
    assert (!isdone());

    WvModemScanList::Iter i(*this);

    for (i.rewind(); i.next(); )
	if (!i().isdone()) break;

    if (!i.cur()) return;

    WvModemScan &s(i);

    if (!s.isok())
    {
	if (!printed)
	{
	    const WvString &f = s.filename();
	    const char *cptr = strrchr(f, '/');
	    if (cptr)
		cptr++;
	    else
		cptr = f;

	    if (!strncmp(cptr, "tty", 3))
		cptr += 3;

	    ++thisline %= 8;
	    if (!thisline)
		log->print("\n");

	    log->print("%-4s ", cptr);
	}

	i.unlink();
	printed = false;
    }
    else
    {
	s.execute();

	if (s.isok())
	    printed = true;
    }

    if (isdone())
	log->write("\n", 1);
}

bool WvModemScanList::isdone()
{
    WvModemScanList::Iter i(*this);

    for (i.rewind(); i.next(); )
	if (!i().isdone()) return false;

    return true;
}
<|endoftext|>


<commit_before><commit_msg>Add two more CG_TRACEs<commit_after><|endoftext|>


<commit_before>/*************************************************************************/
/*  path.cpp                                                             */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "path.h"

#include "core/engine.h"
#include "scene/scene_string_names.h"

void Path::_notification(int p_what) {
}

void Path::_curve_changed() {

	if (is_inside_tree() && Engine::get_singleton()->is_editor_hint())
		update_gizmo();
	if (is_inside_tree()) {
		emit_signal("curve_changed");
	}

	// update the configuration warnings of all children of type PathFollow
	// previously used for PathFollowOriented (now enforced orientation is done in PathFollow)
	if (is_inside_tree()) {
		for (int i = 0; i < get_child_count(); i++) {
			PathFollow *child = Object::cast_to<PathFollow>(get_child(i));
			if (child) {
				child->update_configuration_warning();
			}
		}
	}
}

void Path::set_curve(const Ref<Curve3D> &p_curve) {

	if (curve.is_valid()) {
		curve->disconnect("changed", this, "_curve_changed");
	}

	curve = p_curve;

	if (curve.is_valid()) {
		curve->connect("changed", this, "_curve_changed");
	}
	_curve_changed();
}

Ref<Curve3D> Path::get_curve() const {

	return curve;
}

void Path::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Path::set_curve);
	ClassDB::bind_method(D_METHOD("get_curve"), &Path::get_curve);
	ClassDB::bind_method(D_METHOD("_curve_changed"), &Path::_curve_changed);

	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), "set_curve", "get_curve");

	ADD_SIGNAL(MethodInfo("curve_changed"));
}

Path::Path() {

	set_curve(Ref<Curve3D>(memnew(Curve3D))); //create one by default
}

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

void PathFollow::_update_transform() {

	if (!path)
		return;

	Ref<Curve3D> c = path->get_curve();
	if (!c.is_valid())
		return;

	if (delta_offset == 0) {
		return;
	}

	float bl = c->get_baked_length();
	if (bl == 0.0) {
		return;
	}
	float bi = c->get_bake_interval();
	float o = offset;
	float o_next = offset + bi;

	if (loop) {
		o = Math::fposmod(o, bl);
		o_next = Math::fposmod(o_next, bl);
	} else if (rotation_mode == ROTATION_ORIENTED && o_next >= bl) {
		o = bl - bi;
		o_next = bl;
	}

	Vector3 pos = c->interpolate_baked(o, cubic);
	Transform t = get_transform();
	// Vector3 pos_offset = Vector3(h_offset, v_offset, 0); not used in all cases
	// will be replaced by "Vector3(h_offset, v_offset, 0)" where it was formerly used

	if (rotation_mode == ROTATION_ORIENTED) {

		Vector3 forward = c->interpolate_baked(o_next, cubic) - pos;

		if (forward.length_squared() < CMP_EPSILON2)
			forward = Vector3(0, 0, 1);
		else
			forward.normalize();

		Vector3 up = c->interpolate_baked_up_vector(o, true);

		if (o_next < o) {
			Vector3 up1 = c->interpolate_baked_up_vector(o_next, true);
			Vector3 axis = up.cross(up1);

			if (axis.length_squared() < CMP_EPSILON2)
				axis = forward;
			else
				axis.normalize();

			up.rotate(axis, up.angle_to(up1) * 0.5f);
		}

		Vector3 scale = t.basis.get_scale();
		Vector3 sideways = up.cross(forward).normalized();
		up = forward.cross(sideways).normalized();

		t.basis.set(sideways, up, forward);
		t.basis.scale_local(scale);

		t.origin = pos + sideways * h_offset + up * v_offset;
	} else if (rotation_mode != ROTATION_NONE) {
		// perform parallel transport
		//
		// see C. Dougan, The Parallel Transport Frame, Game Programming Gems 2 for example
		// for a discussion about why not Frenet frame.

		t.origin = pos;

		Vector3 t_prev = (pos - c->interpolate_baked(o - delta_offset, cubic)).normalized();
		Vector3 t_cur = (c->interpolate_baked(o + delta_offset, cubic) - pos).normalized();

		Vector3 axis = t_prev.cross(t_cur);
		float dot = t_prev.dot(t_cur);
		float angle = Math::acos(CLAMP(dot, -1, 1));

		if (likely(!Math::is_zero_approx(angle))) {
			if (rotation_mode == ROTATION_Y) {
				// assuming we're referring to global Y-axis. is this correct?
				axis.x = 0;
				axis.z = 0;
			} else if (rotation_mode == ROTATION_XY) {
				axis.z = 0;
			} else if (rotation_mode == ROTATION_XYZ) {
				// all components are allowed
			}

			if (likely(!Math::is_zero_approx(axis.length()))) {
				t.rotate_basis(axis.normalized(), angle);
			}
		}

		// do the additional tilting
		float tilt_angle = c->interpolate_baked_tilt(o);
		Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct??

		if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) {
			if (rotation_mode == ROTATION_Y) {
				tilt_axis.x = 0;
				tilt_axis.z = 0;
			} else if (rotation_mode == ROTATION_XY) {
				tilt_axis.z = 0;
			} else if (rotation_mode == ROTATION_XYZ) {
				// all components are allowed
			}

			if (likely(!Math::is_zero_approx(tilt_axis.length()))) {
				t.rotate_basis(tilt_axis.normalized(), tilt_angle);
			}
		}

		t.translate(Vector3(h_offset, v_offset, 0));
	} else {
		t.origin = pos + Vector3(h_offset, v_offset, 0);
	}

	set_transform(t);
}

void PathFollow::_notification(int p_what) {

	switch (p_what) {

		case NOTIFICATION_ENTER_TREE: {

			Node *parent = get_parent();
			if (parent) {
				path = Object::cast_to<Path>(parent);
				if (path) {
					_update_transform();
				}
			}

		} break;
		case NOTIFICATION_EXIT_TREE: {

			path = NULL;
		} break;
	}
}

void PathFollow::set_cubic_interpolation(bool p_enable) {

	cubic = p_enable;
}

bool PathFollow::get_cubic_interpolation() const {

	return cubic;
}

void PathFollow::_validate_property(PropertyInfo &property) const {

	if (property.name == "offset") {

		float max = 10000;
		if (path && path->get_curve().is_valid())
			max = path->get_curve()->get_baked_length();

		property.hint_string = "0," + rtos(max) + ",0.01,or_greater";
	}
}

String PathFollow::get_configuration_warning() const {

	if (!is_visible_in_tree() || !is_inside_tree())
		return String();

	if (!Object::cast_to<Path>(get_parent())) {
		return TTR("PathFollow only works when set as a child of a Path node.");
	} else {
		Path *path = Object::cast_to<Path>(get_parent());
		if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) {
			return TTR("PathFollow's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path's Curve resource.");
		}
	}

	return String();
}

void PathFollow::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_offset", "offset"), &PathFollow::set_offset);
	ClassDB::bind_method(D_METHOD("get_offset"), &PathFollow::get_offset);

	ClassDB::bind_method(D_METHOD("set_h_offset", "h_offset"), &PathFollow::set_h_offset);
	ClassDB::bind_method(D_METHOD("get_h_offset"), &PathFollow::get_h_offset);

	ClassDB::bind_method(D_METHOD("set_v_offset", "v_offset"), &PathFollow::set_v_offset);
	ClassDB::bind_method(D_METHOD("get_v_offset"), &PathFollow::get_v_offset);

	ClassDB::bind_method(D_METHOD("set_unit_offset", "unit_offset"), &PathFollow::set_unit_offset);
	ClassDB::bind_method(D_METHOD("get_unit_offset"), &PathFollow::get_unit_offset);

	ClassDB::bind_method(D_METHOD("set_rotation_mode", "rotation_mode"), &PathFollow::set_rotation_mode);
	ClassDB::bind_method(D_METHOD("get_rotation_mode"), &PathFollow::get_rotation_mode);

	ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enable"), &PathFollow::set_cubic_interpolation);
	ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow::get_cubic_interpolation);

	ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow::set_loop);
	ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow::has_loop);

	ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_greater"), "set_offset", "get_offset");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "None,Y,XY,XYZ,Oriented"), "set_rotation_mode", "get_rotation_mode");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");

	BIND_ENUM_CONSTANT(ROTATION_NONE);
	BIND_ENUM_CONSTANT(ROTATION_Y);
	BIND_ENUM_CONSTANT(ROTATION_XY);
	BIND_ENUM_CONSTANT(ROTATION_XYZ);
	BIND_ENUM_CONSTANT(ROTATION_ORIENTED);
}

void PathFollow::set_offset(float p_offset) {
	delta_offset = p_offset - offset;
	offset = p_offset;

	if (path)
		_update_transform();
	_change_notify("offset");
	_change_notify("unit_offset");
}

void PathFollow::set_h_offset(float p_h_offset) {

	h_offset = p_h_offset;
	if (path)
		_update_transform();
}

float PathFollow::get_h_offset() const {

	return h_offset;
}

void PathFollow::set_v_offset(float p_v_offset) {

	v_offset = p_v_offset;
	if (path)
		_update_transform();
}

float PathFollow::get_v_offset() const {

	return v_offset;
}

float PathFollow::get_offset() const {

	return offset;
}

void PathFollow::set_unit_offset(float p_unit_offset) {

	if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length())
		set_offset(p_unit_offset * path->get_curve()->get_baked_length());
}

float PathFollow::get_unit_offset() const {

	if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length())
		return get_offset() / path->get_curve()->get_baked_length();
	else
		return 0;
}

void PathFollow::set_rotation_mode(RotationMode p_rotation_mode) {

	rotation_mode = p_rotation_mode;

	update_configuration_warning();
	_update_transform();
}

PathFollow::RotationMode PathFollow::get_rotation_mode() const {

	return rotation_mode;
}

void PathFollow::set_loop(bool p_loop) {

	loop = p_loop;
}

bool PathFollow::has_loop() const {

	return loop;
}

PathFollow::PathFollow() {

	offset = 0;
	delta_offset = 0;
	h_offset = 0;
	v_offset = 0;
	path = NULL;
	rotation_mode = ROTATION_XYZ;
	cubic = true;
	loop = true;
}
<commit_msg>Port Path2D changes to 3D<commit_after>/*************************************************************************/
/*  path.cpp                                                             */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "path.h"

#include "core/engine.h"
#include "scene/scene_string_names.h"

void Path::_notification(int p_what) {
}

void Path::_curve_changed() {

	if (is_inside_tree() && Engine::get_singleton()->is_editor_hint())
		update_gizmo();
	if (is_inside_tree()) {
		emit_signal("curve_changed");
	}

	// update the configuration warnings of all children of type PathFollow
	// previously used for PathFollowOriented (now enforced orientation is done in PathFollow)
	if (is_inside_tree()) {
		for (int i = 0; i < get_child_count(); i++) {
			PathFollow *child = Object::cast_to<PathFollow>(get_child(i));
			if (child) {
				child->update_configuration_warning();
			}
		}
	}
}

void Path::set_curve(const Ref<Curve3D> &p_curve) {

	if (curve.is_valid()) {
		curve->disconnect("changed", this, "_curve_changed");
	}

	curve = p_curve;

	if (curve.is_valid()) {
		curve->connect("changed", this, "_curve_changed");
	}
	_curve_changed();
}

Ref<Curve3D> Path::get_curve() const {

	return curve;
}

void Path::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Path::set_curve);
	ClassDB::bind_method(D_METHOD("get_curve"), &Path::get_curve);
	ClassDB::bind_method(D_METHOD("_curve_changed"), &Path::_curve_changed);

	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), "set_curve", "get_curve");

	ADD_SIGNAL(MethodInfo("curve_changed"));
}

Path::Path() {

	set_curve(Ref<Curve3D>(memnew(Curve3D))); //create one by default
}

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

void PathFollow::_update_transform() {

	if (!path)
		return;

	Ref<Curve3D> c = path->get_curve();
	if (!c.is_valid())
		return;

	if (delta_offset == 0) {
		return;
	}

	float bl = c->get_baked_length();
	if (bl == 0.0) {
		return;
	}
	float bi = c->get_bake_interval();
	float o_next = offset + bi;

	if (loop) {
		o_next = Math::fposmod(o_next, bl);
	} else if (rotation_mode == ROTATION_ORIENTED && o_next >= bl) {
		o_next = bl;
	}

	Vector3 pos = c->interpolate_baked(offset, cubic);
	Transform t = get_transform();
	// Vector3 pos_offset = Vector3(h_offset, v_offset, 0); not used in all cases
	// will be replaced by "Vector3(h_offset, v_offset, 0)" where it was formerly used

	if (rotation_mode == ROTATION_ORIENTED) {

		Vector3 forward = c->interpolate_baked(o_next, cubic) - pos;

		if (forward.length_squared() < CMP_EPSILON2)
			forward = Vector3(0, 0, 1);
		else
			forward.normalize();

		Vector3 up = c->interpolate_baked_up_vector(offset, true);

		if (o_next < offset) {
			Vector3 up1 = c->interpolate_baked_up_vector(o_next, true);
			Vector3 axis = up.cross(up1);

			if (axis.length_squared() < CMP_EPSILON2)
				axis = forward;
			else
				axis.normalize();

			up.rotate(axis, up.angle_to(up1) * 0.5f);
		}

		Vector3 scale = t.basis.get_scale();
		Vector3 sideways = up.cross(forward).normalized();
		up = forward.cross(sideways).normalized();

		t.basis.set(sideways, up, forward);
		t.basis.scale_local(scale);

		t.origin = pos + sideways * h_offset + up * v_offset;
	} else if (rotation_mode != ROTATION_NONE) {
		// perform parallel transport
		//
		// see C. Dougan, The Parallel Transport Frame, Game Programming Gems 2 for example
		// for a discussion about why not Frenet frame.

		t.origin = pos;

		Vector3 t_prev = (pos - c->interpolate_baked(offset - delta_offset, cubic)).normalized();
		Vector3 t_cur = (c->interpolate_baked(offset + delta_offset, cubic) - pos).normalized();

		Vector3 axis = t_prev.cross(t_cur);
		float dot = t_prev.dot(t_cur);
		float angle = Math::acos(CLAMP(dot, -1, 1));

		if (likely(!Math::is_zero_approx(angle))) {
			if (rotation_mode == ROTATION_Y) {
				// assuming we're referring to global Y-axis. is this correct?
				axis.x = 0;
				axis.z = 0;
			} else if (rotation_mode == ROTATION_XY) {
				axis.z = 0;
			} else if (rotation_mode == ROTATION_XYZ) {
				// all components are allowed
			}

			if (likely(!Math::is_zero_approx(axis.length()))) {
				t.rotate_basis(axis.normalized(), angle);
			}
		}

		// do the additional tilting
		float tilt_angle = c->interpolate_baked_tilt(offset);
		Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct??

		if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) {
			if (rotation_mode == ROTATION_Y) {
				tilt_axis.x = 0;
				tilt_axis.z = 0;
			} else if (rotation_mode == ROTATION_XY) {
				tilt_axis.z = 0;
			} else if (rotation_mode == ROTATION_XYZ) {
				// all components are allowed
			}

			if (likely(!Math::is_zero_approx(tilt_axis.length()))) {
				t.rotate_basis(tilt_axis.normalized(), tilt_angle);
			}
		}

		t.translate(Vector3(h_offset, v_offset, 0));
	} else {
		t.origin = pos + Vector3(h_offset, v_offset, 0);
	}

	set_transform(t);
}

void PathFollow::_notification(int p_what) {

	switch (p_what) {

		case NOTIFICATION_ENTER_TREE: {

			Node *parent = get_parent();
			if (parent) {
				path = Object::cast_to<Path>(parent);
				if (path) {
					_update_transform();
				}
			}

		} break;
		case NOTIFICATION_EXIT_TREE: {

			path = NULL;
		} break;
	}
}

void PathFollow::set_cubic_interpolation(bool p_enable) {

	cubic = p_enable;
}

bool PathFollow::get_cubic_interpolation() const {

	return cubic;
}

void PathFollow::_validate_property(PropertyInfo &property) const {

	if (property.name == "offset") {

		float max = 10000;
		if (path && path->get_curve().is_valid())
			max = path->get_curve()->get_baked_length();

		property.hint_string = "0," + rtos(max) + ",0.01,or_lesser";
	}
}

String PathFollow::get_configuration_warning() const {

	if (!is_visible_in_tree() || !is_inside_tree())
		return String();

	if (!Object::cast_to<Path>(get_parent())) {
		return TTR("PathFollow only works when set as a child of a Path node.");
	} else {
		Path *path = Object::cast_to<Path>(get_parent());
		if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) {
			return TTR("PathFollow's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path's Curve resource.");
		}
	}

	return String();
}

void PathFollow::_bind_methods() {

	ClassDB::bind_method(D_METHOD("set_offset", "offset"), &PathFollow::set_offset);
	ClassDB::bind_method(D_METHOD("get_offset"), &PathFollow::get_offset);

	ClassDB::bind_method(D_METHOD("set_h_offset", "h_offset"), &PathFollow::set_h_offset);
	ClassDB::bind_method(D_METHOD("get_h_offset"), &PathFollow::get_h_offset);

	ClassDB::bind_method(D_METHOD("set_v_offset", "v_offset"), &PathFollow::set_v_offset);
	ClassDB::bind_method(D_METHOD("get_v_offset"), &PathFollow::get_v_offset);

	ClassDB::bind_method(D_METHOD("set_unit_offset", "unit_offset"), &PathFollow::set_unit_offset);
	ClassDB::bind_method(D_METHOD("get_unit_offset"), &PathFollow::get_unit_offset);

	ClassDB::bind_method(D_METHOD("set_rotation_mode", "rotation_mode"), &PathFollow::set_rotation_mode);
	ClassDB::bind_method(D_METHOD("get_rotation_mode"), &PathFollow::get_rotation_mode);

	ClassDB::bind_method(D_METHOD("set_cubic_interpolation", "enable"), &PathFollow::set_cubic_interpolation);
	ClassDB::bind_method(D_METHOD("get_cubic_interpolation"), &PathFollow::get_cubic_interpolation);

	ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow::set_loop);
	ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow::has_loop);

	ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser"), "set_offset", "get_offset");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
	ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
	ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM, "None,Y,XY,XYZ,Oriented"), "set_rotation_mode", "get_rotation_mode");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cubic_interp"), "set_cubic_interpolation", "get_cubic_interpolation");
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");

	BIND_ENUM_CONSTANT(ROTATION_NONE);
	BIND_ENUM_CONSTANT(ROTATION_Y);
	BIND_ENUM_CONSTANT(ROTATION_XY);
	BIND_ENUM_CONSTANT(ROTATION_XYZ);
	BIND_ENUM_CONSTANT(ROTATION_ORIENTED);
}

void PathFollow::set_offset(float p_offset) {
	delta_offset = p_offset - offset;
	offset = p_offset;

	if (path) {
		if (path->get_curve().is_valid() && path->get_curve()->get_baked_length()) {
			float path_length = path->get_curve()->get_baked_length();

			if (loop) {
				while (offset > path_length)
					offset -= path_length;

				while (offset < 0)
					offset += path_length;

			} else {
				offset = CLAMP(offset, 0, path_length);
			}
		}

		_update_transform();
	}
	_change_notify("offset");
	_change_notify("unit_offset");
}

void PathFollow::set_h_offset(float p_h_offset) {

	h_offset = p_h_offset;
	if (path)
		_update_transform();
}

float PathFollow::get_h_offset() const {

	return h_offset;
}

void PathFollow::set_v_offset(float p_v_offset) {

	v_offset = p_v_offset;
	if (path)
		_update_transform();
}

float PathFollow::get_v_offset() const {

	return v_offset;
}

float PathFollow::get_offset() const {

	return offset;
}

void PathFollow::set_unit_offset(float p_unit_offset) {

	if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length())
		set_offset(p_unit_offset * path->get_curve()->get_baked_length());
}

float PathFollow::get_unit_offset() const {

	if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length())
		return get_offset() / path->get_curve()->get_baked_length();
	else
		return 0;
}

void PathFollow::set_rotation_mode(RotationMode p_rotation_mode) {

	rotation_mode = p_rotation_mode;

	update_configuration_warning();
	_update_transform();
}

PathFollow::RotationMode PathFollow::get_rotation_mode() const {

	return rotation_mode;
}

void PathFollow::set_loop(bool p_loop) {

	loop = p_loop;
}

bool PathFollow::has_loop() const {

	return loop;
}

PathFollow::PathFollow() {

	offset = 0;
	delta_offset = 0;
	h_offset = 0;
	v_offset = 0;
	path = NULL;
	rotation_mode = ROTATION_XYZ;
	cubic = true;
	loop = true;
}
<|endoftext|>


<commit_before>#include "linkedlist.hpp"

class TestLinkedList : public CxxTest::TestSuite {
public:

  class MyNode : public LinkedList::Node {

  };

  void test_create() {
    LinkedList ll;
    TS_ASSERT_EQUALS(ll.size(), 0);
  }

  void test_add() {
    LinkedList ll;
    MyNode node;

    ll.add(&node);

    TS_ASSERT(!node.next());
    TS_ASSERT(!node.prev());

    TS_ASSERT_EQUALS(ll.size(), 1);

    MyNode node2;

    ll.add(&node2);

    TS_ASSERT_EQUALS(node.prev(), &node2);
    TS_ASSERT(!node.next());

    TS_ASSERT_EQUALS(node2.next(), &node);
    TS_ASSERT(!node2.prev());

    TS_ASSERT_EQUALS(ll.size(), 2);
  }

  void test_remove() {
    LinkedList ll;
    MyNode node;
    MyNode node2;

    ll.add(&node);
    ll.add(&node2);

    TS_ASSERT_EQUALS(ll.size(), 2);

    ll.remove(&node);
    TS_ASSERT(!node.next());
    TS_ASSERT(!node.prev());

    TS_ASSERT_EQUALS(ll.size(), 1);

    TS_ASSERT_EQUALS(ll.head(), &node2);
    TS_ASSERT(!node2.next());
    TS_ASSERT(!node2.prev());
  }

  void test_add_remove_3() {
    LinkedList ll;
    MyNode node;
    MyNode node2;
    MyNode node3;

    ll.add(&node);
    ll.add(&node2);
    ll.add(&node3);

    TS_ASSERT_EQUALS(&node2, node3.next());
    TS_ASSERT_EQUALS(&node3, node2.prev());

    TS_ASSERT_EQUALS(&node,  node2.next());
    TS_ASSERT_EQUALS(&node2, node.prev());

    TS_ASSERT(!node.next());
    TS_ASSERT(!node3.prev());

    ll.remove(&node2);

    TS_ASSERT_EQUALS(&node, node3.next());
    TS_ASSERT_EQUALS(&node3, node.prev());

  }
};
<commit_msg>fixed unsigned comparisons to make gcc 4.3.2 happy<commit_after>#include "linkedlist.hpp"

class TestLinkedList : public CxxTest::TestSuite {
public:

  class MyNode : public LinkedList::Node {

  };

  void test_create() {
    LinkedList ll;
    TS_ASSERT_EQUALS(ll.size(), (unsigned)0);
  }

  void test_add() {
    LinkedList ll;
    MyNode node;

    ll.add(&node);

    TS_ASSERT(!node.next());
    TS_ASSERT(!node.prev());

    TS_ASSERT_EQUALS(ll.size(), (unsigned)1);

    MyNode node2;

    ll.add(&node2);

    TS_ASSERT_EQUALS(node.prev(), &node2);
    TS_ASSERT(!node.next());

    TS_ASSERT_EQUALS(node2.next(), &node);
    TS_ASSERT(!node2.prev());

    TS_ASSERT_EQUALS(ll.size(), (unsigned)2);
  }

  void test_remove() {
    LinkedList ll;
    MyNode node;
    MyNode node2;

    ll.add(&node);
    ll.add(&node2);

    TS_ASSERT_EQUALS(ll.size(), (unsigned)2);

    ll.remove(&node);
    TS_ASSERT(!node.next());
    TS_ASSERT(!node.prev());

    TS_ASSERT_EQUALS(ll.size(), (unsigned)1);

    TS_ASSERT_EQUALS(ll.head(), &node2);
    TS_ASSERT(!node2.next());
    TS_ASSERT(!node2.prev());
  }

  void test_add_remove_3() {
    LinkedList ll;
    MyNode node;
    MyNode node2;
    MyNode node3;

    ll.add(&node);
    ll.add(&node2);
    ll.add(&node3);

    TS_ASSERT_EQUALS(&node2, node3.next());
    TS_ASSERT_EQUALS(&node3, node2.prev());

    TS_ASSERT_EQUALS(&node,  node2.next());
    TS_ASSERT_EQUALS(&node2, node.prev());

    TS_ASSERT(!node.next());
    TS_ASSERT(!node3.prev());

    ll.remove(&node2);

    TS_ASSERT_EQUALS(&node, node3.next());
    TS_ASSERT_EQUALS(&node3, node.prev());

  }
};
<|endoftext|>