Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic setup for Python interface using SWIG #216

Merged
merged 18 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ if (SWIG_FOUND)
else()
message (STATUS "Searching for Ruby - found.")
endif()

########################################
# Include python
find_package(PythonLibs REQUIRED)
francocipollone marked this conversation as resolved.
Show resolved Hide resolved
if (NOT PYTHONLIBS_FOUND)
message (STATUS "Searching for Python - not found.")
else()
message (STATUS "Searching for Python - found.")
endif()
endif()

#============================================================================
Expand Down
39 changes: 39 additions & 0 deletions examples/angle_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (C) 2021 Open Source Robotics Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This example will only work if the Python interface library was compiled and
# installed.
#
# Modify the PYTHONPATH environment variable to include the ignition math
# library install path. For example, if you install to /user:
#
# $ export PYTHONPATH=/usr/lib/python/:$PYTHONPATH
#

import ignition.math

print("PI in degrees = {}\n".format(ignition.math.Angle.Pi.Degree()))

a1 = ignition.math.Angle(1.5707)
a2 = ignition.math.Angle(0.7854)
print("a1 = {} radians, {} degrees\n".format(a1.Radian(), a1.Degree()))
print("a2 = {} radians, {} degrees\n".format(a2.Radian(), a2.Degree()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit for the future (not this pull request): I find f"" strings are more readable than using .format()

for example:

print(f"a2 = {a2.Radian()} radians, {a2.Degree(} degrees\n")

print("a1 * a2 = {} radians, {} degrees\n".format((a1 * a2).Radian(), (a1 * a2).Degree()))
print("a1 + a2 = {} radians, {} degrees\n".format((a1 + a2).Radian(), (a1 + a2).Degree()))
print("a1 - a2 = {} radians, {} degrees\n".format((a1 - a2).Radian(), (a1 - a2).Degree()))

a3 = ignition.math.Angle(15.707)
print("a3 = {} radians, {} degrees\n".format(a3.Radian(), a3.Degree()))
a3.Normalize()
print("a3.Normalize = {} radians, {} degrees\n".format(a3.Radian(), a3.Degree()))
39 changes: 39 additions & 0 deletions examples/vector2_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (C) 2021 Open Source Robotics Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This example will only work if the Python interface library was compiled and
# installed.
#
# Modify the PYTHONPATH environment variable to include the ignition math
# library install path. For example, if you install to /user:
#
# $ export PYTHONPATH=/usr/lib/python/ignition:$PYTHONPATH
francocipollone marked this conversation as resolved.
Show resolved Hide resolved
#
import ignition.math

va = ignition.math.Vector2d(1, 2)
vb = ignition.math.Vector2d(3, 4)
vc = ignition.math.Vector2d(vb)

print("va = {} {}\n".format(va.X(), va.Y()))
print("vb = {} {}\n".format(vb.X(), vb.Y()))
print("vc = {} {}\n".format(vc.X(), vc.Y()))

vb += va
print("vb += va: {} {}\n".format(vb.X(), vb.Y()))

vb.Normalize()
print("vb.Normalize = {} {}\n".format(vb.X(), vb.Y()))

print("vb.Distance(va) = {}\n".format(vb.Distance(va)))
34 changes: 34 additions & 0 deletions examples/vector3_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (C) 2021 Open Source Robotics Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This example will only work if the Python interface library was compiled and
# installed.
#
# Modify the PYTHONPATH environment variable to include the ignition math
# library install path. For example, if you install to /user:
#
# $ export PYTHONPATH=/usr/lib/python/ignition:$PYTHONPATH
#
import ignition.math

v1 = ignition.math.Vector3d(0, 0, 3)
print("v =: {} {} {}\n".format(v1.X(), v1.Y(), v1.Z()))

v2 = ignition.math.Vector3d(4, 0, 0)
print("v2 = {} {} {}\n".format(v2.X(), v2.Y(), v2.Z()))

v3 = v1 + v2
print("v1 + v2 = {} {} {}\n".format(v3.X(), v3.Y(), v3.Z()))

print("v1.Distance(v2) = {}\n".format(v1.Distance(v2)))
93 changes: 93 additions & 0 deletions src/Angle_TEST.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#
# Copyright (C) 2021 Open Source Robotics Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#

import unittest
import math
from ignition.math import Angle


class TestAngle(unittest.TestCase):

def test_angle(self):

angle1 = Angle()
self.assertEqual(0.0, angle1.Radian())

angle1.SetDegree(90.0)
self.assertTrue(angle1 == Angle.HalfPi)

angle1.SetDegree(180.0)
self.assertTrue(angle1 == Angle.Pi)
self.assertFalse(angle1 == Angle.Pi + Angle(0.1))
self.assertTrue(angle1 == Angle.Pi + Angle(0.0001))
self.assertTrue(angle1 == Angle.Pi - Angle(0.0001))
self.assertTrue(Angle(0) == Angle(0))
self.assertTrue(Angle(0) == Angle(0.001))

angle1 = Angle(0.1) - Angle(0.3)
self.assertAlmostEqual(angle1.Radian(), -0.2)

angle = Angle(0.5)
self.assertEqual(0.5, angle.Radian())

angle.SetRadian(math.pi/2)
self.assertAlmostEqual(math.degrees(math.pi/2), angle.Degree())

angle.SetRadian(math.pi)
self.assertAlmostEqual(math.degrees(math.pi), angle.Degree())

def test_normalized_angles(self):

angle = Angle(Angle.Pi)
normalized = angle.Normalized()

angle.Normalized()
self.assertEqual(math.degrees(math.pi), angle.Degree())
self.assertEqual(normalized, angle)

def test_angle_operations(self):

angle = Angle(0.1) + Angle(0.2)
self.assertAlmostEqual(0.3, angle.Radian())

angle = Angle(0.1) * Angle(0.2)
self.assertAlmostEqual(0.02, angle.Radian())

angle = Angle(0.1) / Angle(0.2)
self.assertAlmostEqual(0.5, angle.Radian())

angle -= Angle(0.1)
self.assertAlmostEqual(0.4, angle.Radian())

angle += Angle(0.2)
self.assertAlmostEqual(0.6, angle.Radian())

angle *= Angle(0.5)
self.assertAlmostEqual(0.3, angle.Radian())

angle /= Angle(0.1)
self.assertAlmostEqual(3.0, angle.Radian())
self.assertTrue(angle == Angle(3))
self.assertTrue(angle != Angle(2))
self.assertTrue(angle < Angle(4))
self.assertTrue(angle > Angle(2))
self.assertTrue(angle >= Angle(3))
self.assertTrue(angle <= Angle(3))


if __name__ == '__main__':
unittest.main()
76 changes: 71 additions & 5 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ if (SWIG_FOUND)
set(CMAKE_SWIG_FLAGS "")

include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PYTHON_INCLUDE_PATH})

set(swig_files
ruby
Angle
GaussMarkovProcess
Rand
Expand All @@ -50,20 +50,21 @@ if (RUBY_FOUND)
# Generate the list if .i files
list(APPEND swig_i_files ${swig_file}.i)
endforeach()
list(APPEND ruby_tests ruby_TEST)

# Turn on c++
set_source_files_properties(${swig_i_files} PROPERTIES CPLUSPLUS ON)
set_source_files_properties(${swig_i_files} ruby/ruby.i PROPERTIES CPLUSPLUS ON)

# Create the ruby library

if(CMAKE_VERSION VERSION_GREATER 3.8.0)
SWIG_ADD_LIBRARY(math LANGUAGE ruby SOURCES ruby.i ${swig_i_files} ${sources})
SWIG_ADD_LIBRARY(math LANGUAGE ruby SOURCES ruby/ruby.i ${swig_i_files} ${sources})
else()
SWIG_ADD_MODULE(math ruby ruby.i ${swig_i_files} ${sources})
SWIG_ADD_MODULE(math ruby ruby/ruby.i ${swig_i_files} ${sources})
endif()

# Suppress warnings on SWIG-generated files
target_compile_options(math PRIVATE
target_compile_options(math PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wno-pedantic -Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter>
$<$<CXX_COMPILER_ID:Clang>:-Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter>
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter>
Expand All @@ -81,3 +82,68 @@ if (RUBY_FOUND)
--gtest_output=xml:${CMAKE_BINARY_DIR}/test_results/${test}rb.xml)
endforeach()
endif()

#################################
# Create and install Python interfaces
# Example usage
# $ export PYTHONPATH=/ws/install/lib/python/:$PYTHONPATH
if (PYTHONLIBS_FOUND)
set_source_files_properties(python/python.i PROPERTIES CPLUSPLUS ON)
set_source_files_properties(python/python.i PROPERTIES SWIG_FLAGS "-includeall")
set_source_files_properties(python/python.i PROPERTIES SWIG_MODULE_NAME "math")
set(SWIG_PY_LIB pymath)
set(SWIG_PY_LIB_OUTPUT math)

set(CMAKE_SWIG_OUTDIR "${CMAKE_INSTALL_PREFIX}/lib/python/ignition")
scpeters marked this conversation as resolved.
Show resolved Hide resolved
if(CMAKE_VERSION VERSION_GREATER 3.8.0)
SWIG_ADD_LIBRARY(${SWIG_PY_LIB} LANGUAGE python SOURCES python/python.i ${sources})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are recompiling sources instead of just linking, for both python and ruby bindings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved at 0ce31c6

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It fails now, it's like now it can't find libignition-math6.so but I couldn't replicate this locally, locally it works. Will check

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can reproduce the problem on my machine. I'm looking into it

else()
SWIG_ADD_MODULE(${SWIG_PY_LIB} python python/python.i ${sources})
endif()

SWIG_LINK_LIBRARIES(${SWIG_PY_LIB} ${PYTHON_LIBRARIES})

# When using SWIG 3 to build a python interface there is an extra underscore between the name of
# the library given to swig_add_library macro and the generated .so
if(NOT ${SWIG_VERSION} VERSION_GREATER 4.0.0)
set(SWIG_PY_LIB "_${SWIG_PY_LIB}")
set(SWIG_PY_LIB_OUTPUT "_${SWIG_PY_LIB_OUTPUT}")
endif()

set_target_properties(${SWIG_PY_LIB}
PROPERTIES
OUTPUT_NAME ${SWIG_PY_LIB_OUTPUT}
)

# Suppress warnings on SWIG-generated files
target_compile_options(${SWIG_PY_LIB} PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wno-pedantic -Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers>
$<$<CXX_COMPILER_ID:Clang>:-Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers>
$<$<CXX_COMPILER_ID:AppleClang>:-Wno-shadow -Wno-maybe-uninitialized -Wno-unused-parameter -Wno-cast-function-type -Wno-missing-field-initializers>
)
install(TARGETS ${SWIG_PY_LIB} DESTINATION ${IGN_LIB_INSTALL_DIR}/python/ignition)

# Add the Python tests

set(python_files
python
Angle
GaussMarkovProcess
Rand
Vector2
Vector3
Vector4
)

foreach (python_file ${python_files})
list(APPEND python_tests ${python_file}_TEST)
endforeach()
francocipollone marked this conversation as resolved.
Show resolved Hide resolved

foreach (test ${python_tests})
add_test(NAME ${test}.py COMMAND
python3 ${CMAKE_SOURCE_DIR}/src/${test}.py)
set_tests_properties(${test}.py PROPERTIES
ENVIRONMENT "PYTHONPATH=${CMAKE_INSTALL_PREFIX}/lib/python/")
endforeach()

endif()
Loading