🐳chore(库):增加 碰撞 库
parent
130af114b3
commit
724b25fcf0
@ -0,0 +1,14 @@
|
||||
Makefile.in
|
||||
autom4te.cache/*
|
||||
aclocal.m4
|
||||
config.guess
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
ltmain.sh
|
||||
missing
|
||||
*~
|
||||
src/gjk/config.h.in
|
||||
build/*
|
||||
ccd.pc
|
||||
@ -0,0 +1,23 @@
|
||||
dist: trusty
|
||||
sudo: required
|
||||
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
env:
|
||||
global:
|
||||
- PREFIX="$TRAVIS_BUILD_DIR/build/install"
|
||||
matrix:
|
||||
- USE_AUTOTOOLS=yes
|
||||
- USE_CMAKE=yes
|
||||
- USE_MAKEFILE=yes USE_DOUBLE=yes
|
||||
- USE_MAKEFILE=yes USE_SINGLE=yes
|
||||
|
||||
script:
|
||||
- mkdir -p "$PREFIX"
|
||||
- if [[ "$USE_AUTOTOOLS" == "yes" ]]; then ./bootstrap && cd build && ../configure --prefix "$PREFIX"; fi
|
||||
- if [[ "$USE_CMAKE" == "yes" ]]; then cd build && cmake "-DCMAKE_INSTALL_PREFIX=$PREFIX" ..; fi
|
||||
- if [[ "$USE_MAKEFILE" == "yes" ]]; then cd src; fi
|
||||
- make && make install
|
||||
@ -0,0 +1,75 @@
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
# Can not explicitly declared the software as C in project command due to bug:
|
||||
# https://gitlab.kitware.com/cmake/cmake/issues/16967
|
||||
project(libccd)
|
||||
|
||||
set(CCD_VERSION_MAJOR 2)
|
||||
set(CCD_VERSION_MINOR 0)
|
||||
set(CCD_VERSION ${CCD_VERSION_MAJOR}.${CCD_VERSION_MINOR})
|
||||
|
||||
set(CCD_SOVERSION 2)
|
||||
|
||||
# Include GNUInstallDirs to get canonical paths
|
||||
include(GNUInstallDirs)
|
||||
include(CTest)
|
||||
|
||||
option(BUILD_DOCUMENTATION "Build the documentation" OFF)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build libccd as a shared library" ON)
|
||||
|
||||
option(ENABLE_DOUBLE_PRECISION
|
||||
"Enable double precision computations instead of single precision" OFF)
|
||||
|
||||
# Option for some bundle-like build system in order not to expose
|
||||
# any FCL binary symbols in their public ABI
|
||||
option(CCD_HIDE_ALL_SYMBOLS "Hide all binary symbols" OFF)
|
||||
if (CCD_HIDE_ALL_SYMBOLS)
|
||||
add_definitions("-DCCD_STATIC_DEFINE")
|
||||
endif()
|
||||
|
||||
# set the default build type
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING
|
||||
"Choose the type of build; options are Debug Release RelWithDebInfo MinSizeRel"
|
||||
FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
|
||||
STRINGS
|
||||
Debug
|
||||
Release
|
||||
RelWithDebInfo
|
||||
MinSizeRel)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
if(BUILD_DOCUMENTATION)
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
configure_package_config_file(ccd-config.cmake.in ccd-config.cmake
|
||||
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/ccd"
|
||||
PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO)
|
||||
|
||||
write_basic_package_version_file(ccd-config-version.cmake
|
||||
VERSION ${CCD_VERSION} COMPATIBILITY AnyNewerVersion)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_BINARY_DIR}/ccd-config.cmake"
|
||||
"${CMAKE_BINARY_DIR}/ccd-config-version.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/ccd")
|
||||
|
||||
set(CCD_PKGCONFIG_DESCRIPTION
|
||||
"Library for collision detection between convex shapes")
|
||||
configure_file(ccd.pc.in ccd.pc @ONLY)
|
||||
install(FILES "${CMAKE_BINARY_DIR}/ccd.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
|
||||
install(FILES BSD-LICENSE DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/doc/ccd")
|
||||
@ -0,0 +1,6 @@
|
||||
SUBDIRS = src
|
||||
|
||||
EXTRA_DIST = doc \
|
||||
BSD-LICENSE \
|
||||
README.md \
|
||||
make-release.sh
|
||||
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
libtoolize -f -c
|
||||
aclocal
|
||||
autoheader -f
|
||||
autoconf
|
||||
automake -a --foreign -f -c
|
||||
@ -0,0 +1,14 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set(CCD_VERSION_MAJOR @CCD_VERSION_MAJOR@)
|
||||
set(CCD_VERSION_MINOR @CCD_VERSION_MINOR@)
|
||||
set(CCD_VERSION @CCD_VERSION@)
|
||||
|
||||
set(CCD_SOVERSION @CCD_SOVERSION@)
|
||||
|
||||
set(CCD_FOUND ON)
|
||||
set_and_check(CCD_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
|
||||
set_and_check(CCD_LIBRARY_DIRS "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
|
||||
set(CCD_LIBRARIES ccd)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/ccd-targets.cmake")
|
||||
@ -0,0 +1,13 @@
|
||||
# Generated by CMake @CMAKE_VERSION@ for ccd
|
||||
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: @CCD_PKGCONFIG_DESCRIPTION@
|
||||
Version: @CCD_VERSION@
|
||||
Requires: @CCD_PKGCONFIG_REQUIRES@
|
||||
Libs: -L${libdir} -lccd @CCD_PKGCONFIG_EXTRA_LIBS@
|
||||
Cflags: -I${includedir}
|
||||
@ -0,0 +1,50 @@
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
#AC_PREREQ([2.65])
|
||||
AC_INIT([libccd], [2.0], [danfis@danfis.cz])
|
||||
AC_CONFIG_SRCDIR([src/ccd.c])
|
||||
AC_CONFIG_HEADERS([src/ccd/config.h])
|
||||
AM_INIT_AUTOMAKE
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
AC_DISABLE_SHARED
|
||||
LT_INIT
|
||||
|
||||
# Checks for libraries.
|
||||
AC_CHECK_LIB([m], [main])
|
||||
# FIXME: Replace `main' with a function in `-lrt':
|
||||
AC_CHECK_LIB([rt], [main])
|
||||
|
||||
# Checks for header files.
|
||||
AC_CHECK_HEADERS([float.h stdlib.h string.h unistd.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_SIZE_T
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_FORK
|
||||
AC_FUNC_REALLOC
|
||||
AC_CHECK_FUNCS([clock_gettime])
|
||||
|
||||
use_double=no
|
||||
AC_ARG_ENABLE(double-precision,
|
||||
AS_HELP_STRING([--enable-double-precision],
|
||||
[enable double precision computations instead of single precision]),
|
||||
[use_double=yes])
|
||||
if test $use_double = no
|
||||
then
|
||||
AC_DEFINE([CCD_SINGLE], [], [use single precision])
|
||||
else
|
||||
AC_DEFINE([CCD_DOUBLE], [], [use double precision])
|
||||
fi
|
||||
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
src/testsuites/Makefile
|
||||
src/testsuites/cu/Makefile])
|
||||
AC_OUTPUT
|
||||
@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Creates .tar.gz package of specified version.
|
||||
# Takes one argument - identification of commit
|
||||
|
||||
NAME=libccd
|
||||
COMMIT=""
|
||||
CMD="git archive"
|
||||
|
||||
# read arguments
|
||||
COMMIT="$1"
|
||||
|
||||
if [ "$COMMIT" = "" ]; then
|
||||
echo "Usage: $0 commit [--notest] [--nodoc]"
|
||||
echo "Error: you must specify commit which should be packed"
|
||||
exit -1;
|
||||
fi;
|
||||
|
||||
|
||||
PREFIX=${NAME}-$COMMIT/
|
||||
FN=${NAME}-$COMMIT.tar.gz
|
||||
|
||||
if echo "$COMMIT" | grep '^v[0-9]\.[0-9]\+' >/dev/null 2>&1; then
|
||||
tmp=$(echo "$COMMIT" | sed 's/^v//')
|
||||
PREFIX=${NAME}-$tmp/
|
||||
FN=${NAME}-$tmp.tar.gz
|
||||
fi
|
||||
|
||||
$CMD --prefix="$PREFIX" --format=tar $COMMIT | gzip >"$FN"
|
||||
echo "Package: $FN"
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
*.o
|
||||
*.a
|
||||
ccd/config.h
|
||||
ccd/config.h.in
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
if(DEFINED CCD_SINGLE OR DEFINED CCD_DOUBLE)
|
||||
# make sure only DOUBLE or SINGLE is set; default to SINGLE
|
||||
if(CCD_SINGLE)
|
||||
set(CCD_DOUBLE OFF)
|
||||
else()
|
||||
set(CCD_SINGLE ON)
|
||||
endif()
|
||||
if(CCD_DOUBLE)
|
||||
set(CCD_SINGLE OFF)
|
||||
endif()
|
||||
elseif(ENABLE_DOUBLE_PRECISION)
|
||||
set(CCD_DOUBLE ON)
|
||||
set(CCD_SINGLE OFF)
|
||||
else()
|
||||
set(CCD_DOUBLE OFF)
|
||||
set(CCD_SINGLE ON)
|
||||
endif()
|
||||
|
||||
configure_file(ccd/config.h.cmake.in ccd/config.h)
|
||||
|
||||
set(CCD_INCLUDES
|
||||
ccd/ccd.h
|
||||
ccd/compiler.h
|
||||
ccd/ccd_export.h
|
||||
ccd/quat.h
|
||||
ccd/vec3.h
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/ccd/config.h")
|
||||
|
||||
set(CCD_SOURCES
|
||||
alloc.h
|
||||
ccd.c
|
||||
dbg.h
|
||||
list.h
|
||||
mpr.c
|
||||
polytope.c
|
||||
polytope.h
|
||||
simplex.h
|
||||
support.c
|
||||
support.h
|
||||
vec3.c)
|
||||
|
||||
add_library(ccd ${CCD_INCLUDES} ${CCD_SOURCES})
|
||||
set_target_properties(ccd PROPERTIES
|
||||
PUBLIC_HEADER "${CCD_INCLUDES}"
|
||||
SOVERSION ${CCD_SOVERSION}
|
||||
VERSION ${CCD_VERSION})
|
||||
target_include_directories(ccd PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
|
||||
|
||||
if(NOT WIN32)
|
||||
find_library(LIBM_LIBRARY NAMES m)
|
||||
if(NOT LIBM_LIBRARY)
|
||||
message(FATAL_ERROR "Could NOT find required library LibM")
|
||||
endif()
|
||||
target_link_libraries(ccd "${LIBM_LIBRARY}")
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(CCD_PKGCONFIG_EXTRA_LIBS -lm PARENT_SCOPE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
export(TARGETS ccd FILE "${CMAKE_BINARY_DIR}/ccd-targets.cmake")
|
||||
|
||||
install(TARGETS ccd
|
||||
EXPORT ccd-targets
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ccd"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(EXPORT ccd-targets DESTINATION "${CMAKE_INSTALL_LIBDIR}/ccd")
|
||||
|
||||
macro (check_compiler_visibility)
|
||||
include (CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-fvisibility=hidden COMPILER_SUPPORTS_VISIBILITY)
|
||||
endmacro()
|
||||
|
||||
if(UNIX)
|
||||
check_compiler_visibility()
|
||||
if (COMPILER_SUPPORTS_VISIBILITY)
|
||||
set_target_properties(ccd
|
||||
PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND BUILD_TESTING AND NOT CCD_HIDE_ALL_SYMBOLS)
|
||||
add_subdirectory(testsuites)
|
||||
endif()
|
||||
@ -0,0 +1,80 @@
|
||||
###
|
||||
# libccd
|
||||
# ---------------------------------
|
||||
# Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
#
|
||||
#
|
||||
# This file is part of libccd.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file BDS-LICENSE for details or see
|
||||
# <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
##
|
||||
|
||||
-include Makefile.include
|
||||
|
||||
CFLAGS += -I. -fvisibility=hidden
|
||||
|
||||
TARGETS = libccd.a
|
||||
OBJS = ccd.o mpr.o support.o vec3.o polytope.o
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
libccd.a: $(OBJS)
|
||||
ar cr $@ $(OBJS)
|
||||
ranlib $@
|
||||
|
||||
ccd/config.h: ccd/config.h.m4
|
||||
$(M4) $(CONFIG_FLAGS) $< >$@
|
||||
|
||||
%.o: %.c %.h ccd/config.h
|
||||
$(CC) $(CFLAGS) $(DEFS) -c -o $@ $<
|
||||
%.o: %.c ccd/config.h
|
||||
$(CC) $(CFLAGS) $(DEFS) -c -o $@ $<
|
||||
%.h: ccd/config.h
|
||||
%.c: ccd/config.h
|
||||
|
||||
install:
|
||||
mkdir -p $(PREFIX)/$(INCLUDEDIR)/ccd
|
||||
mkdir -p $(PREFIX)/$(LIBDIR)
|
||||
cp ccd/*.h $(PREFIX)/$(INCLUDEDIR)/ccd/
|
||||
cp libccd.a $(PREFIX)/$(LIBDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
rm -f $(TARGETS)
|
||||
rm -f ccd/config.h
|
||||
if [ -d testsuites ]; then $(MAKE) -C testsuites clean; fi;
|
||||
|
||||
check:
|
||||
$(MAKE) -C testsuites check
|
||||
check-valgrind:
|
||||
$(MAKE) -C testsuites check-valgrind
|
||||
|
||||
help:
|
||||
@echo "Targets:"
|
||||
@echo " all - Build library"
|
||||
@echo " install - Install library into system"
|
||||
@echo ""
|
||||
@echo "Options:"
|
||||
@echo " CC - Path to C compiler"
|
||||
@echo " M4 - Path to m4 macro processor"
|
||||
@echo ""
|
||||
@echo " DEBUG 'yes'/'no' - Turn on/off debugging (default: 'no')"
|
||||
@echo " PROFIL 'yes'/'no' - Compiles profiling info (default: 'no')"
|
||||
@echo " NOWALL 'yes'/'no' - Turns off -Wall gcc option (default: 'no')"
|
||||
@echo " NOPEDANTIC 'yes'/'no' - Turns off -pedantic gcc option (default: 'no')"
|
||||
@echo ""
|
||||
@echo " USE_SINGLE 'yes' - Use single precision (default: 'no')"
|
||||
@echo " USE_DOUBLE 'yes' - Use double precision (default: 'yes')"
|
||||
@echo ""
|
||||
@echo " PREFIX - Prefix where library will be installed (default: /usr/local)"
|
||||
@echo " INCLUDEDIR - Directory where header files will be installed (PREFIX/INCLUDEDIR) (default: include)"
|
||||
@echo " LIBDIR - Directory where library will be installed (PREFIX/LIBDIR) (default: lib)"
|
||||
@echo ""
|
||||
|
||||
.PHONY: all clean check check-valgrind help
|
||||
@ -0,0 +1,18 @@
|
||||
SUBDIRS = . testsuites
|
||||
|
||||
lib_LTLIBRARIES = libccd.la
|
||||
|
||||
libccd_la_SOURCES = alloc.h \
|
||||
ccd/compiler.h \
|
||||
dbg.h \
|
||||
ccd.c ccd/ccd.h \
|
||||
ccd/ccd_export.h \
|
||||
list.h \
|
||||
polytope.c polytope.h \
|
||||
ccd/quat.h \
|
||||
simplex.h \
|
||||
support.c support.h \
|
||||
vec3.c ccd/vec3.h \
|
||||
mpr.c
|
||||
|
||||
libccd_la_CFLAGS = -fvisibility=hidden
|
||||
@ -0,0 +1,100 @@
|
||||
###
|
||||
# libccd
|
||||
# ---------------------------------
|
||||
# Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
#
|
||||
#
|
||||
# This file is part of libccd.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file BDS-LICENSE for details or see
|
||||
# <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
##
|
||||
|
||||
CC ?= gcc
|
||||
M4 ?= m4
|
||||
PYTHON ?= python
|
||||
|
||||
SYSTEM = $(shell uname)
|
||||
|
||||
SYSTEM_CXXFLAGS =
|
||||
SYSTEM_LDFLAGS =
|
||||
|
||||
ifeq '$(SYSTEM)' 'FreeBSD'
|
||||
SYSTEM_CXXFLAGS = -Wno-long-long
|
||||
else
|
||||
endif
|
||||
|
||||
NOWALL ?= no
|
||||
NOPEDANTIC ?= no
|
||||
DEBUG ?= no
|
||||
PROFIL ?= no
|
||||
|
||||
ifeq '$(PROFIL)' 'yes'
|
||||
DEBUG = yes
|
||||
endif
|
||||
|
||||
ifeq '$(DEBUG)' 'yes'
|
||||
CFLAGS = -g
|
||||
endif
|
||||
ifeq '$(PROFIL)' 'yes'
|
||||
CFLAGS += -pg
|
||||
endif
|
||||
|
||||
ifneq '$(NOWALL)' 'yes'
|
||||
CFLAGS += -Wall
|
||||
endif
|
||||
ifneq '$(NOPEDANTIC)' 'yes'
|
||||
CFLAGS += -pedantic
|
||||
endif
|
||||
|
||||
CONFIG_FLAGS =
|
||||
USE_DOUBLE ?= yes
|
||||
USE_SINGLE ?= no
|
||||
|
||||
ifeq '$(USE_SINGLE)' 'yes'
|
||||
CONFIG_FLAGS += -DUSE_SINGLE
|
||||
USE_DOUBLE = no
|
||||
endif
|
||||
ifeq '$(USE_DOUBLE)' 'yes'
|
||||
CONFIG_FLAGS += -DUSE_DOUBLE
|
||||
endif
|
||||
|
||||
CFLAGS += --std=gnu99
|
||||
LDFLAGS += $(SYSTEM_LDFLAGS)
|
||||
|
||||
CHECKTARGETS =
|
||||
check-dep: $(CHECKTARGETS)
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
INCLUDEDIR ?= include
|
||||
LIBDIR ?= lib
|
||||
|
||||
showvars:
|
||||
@echo "SYSTEM = "$(SYSTEM)
|
||||
@echo ""
|
||||
@echo "CC = $(CC)"
|
||||
@echo "M4 = $(M4)"
|
||||
@echo ""
|
||||
@echo "DEBUG = $(DEBUG)"
|
||||
@echo "PROFIL = $(PROFIL)"
|
||||
@echo "NOWALL = $(NOWALL)"
|
||||
@echo "NOPEDANTIC = $(NOPEDANTIC)"
|
||||
@echo "USE_SINGLE = $(USE_SINGLE)"
|
||||
@echo "USE_DOUBLE = $(USE_DOUBLE)"
|
||||
@echo ""
|
||||
@echo "CFLAGS = $(CFLAGS)"
|
||||
@echo "LDFLAGS = $(LDFLAGS)"
|
||||
@echo "CONFIG_FLAGS = $(CONFIG_FLAGS)"
|
||||
@echo ""
|
||||
@echo "PREFIX = $(PREFIX)"
|
||||
@echo "INCLUDEDIR = $(INCLUDEDIR)"
|
||||
@echo "LIBDIR = $(LIBDIR)"
|
||||
@echo ""
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
.PHONY: showvars
|
||||
@ -0,0 +1,50 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_ALLOC_H__
|
||||
#define __CCD_ALLOC_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* Functions and macros required for memory allocation.
|
||||
*/
|
||||
|
||||
/* Memory allocation: */
|
||||
#define __CCD_ALLOC_MEMORY(type, ptr_old, size) \
|
||||
(type *)realloc((void *)ptr_old, (size))
|
||||
|
||||
/** Allocate memory for one element of type. */
|
||||
#define CCD_ALLOC(type) \
|
||||
__CCD_ALLOC_MEMORY(type, NULL, sizeof(type))
|
||||
|
||||
/** Allocate memory for array of elements of type type. */
|
||||
#define CCD_ALLOC_ARR(type, num_elements) \
|
||||
__CCD_ALLOC_MEMORY(type, NULL, sizeof(type) * (num_elements))
|
||||
|
||||
#define CCD_REALLOC_ARR(ptr, type, num_elements) \
|
||||
__CCD_ALLOC_MEMORY(type, ptr, sizeof(type) * (num_elements))
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CCD_ALLOC_H__ */
|
||||
@ -0,0 +1,996 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2012 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <float.h>
|
||||
#include <ccd/ccd.h>
|
||||
#include <ccd/vec3.h>
|
||||
#include "simplex.h"
|
||||
#include "polytope.h"
|
||||
#include "alloc.h"
|
||||
#include "dbg.h"
|
||||
|
||||
|
||||
/** Performs GJK algorithm. Returns 0 if intersection was found and simplex
|
||||
* is filled with resulting polytope. */
|
||||
static int __ccdGJK(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_simplex_t *simplex);
|
||||
|
||||
/** Performs GJK+EPA algorithm. Returns 0 if intersection was found and
|
||||
* pt is filled with resulting polytope and nearest with pointer to
|
||||
* nearest element (vertex, edge, face) of polytope to origin. */
|
||||
static int __ccdGJKEPA(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd,
|
||||
ccd_pt_t *pt, ccd_pt_el_t **nearest);
|
||||
|
||||
|
||||
/** Returns true if simplex contains origin.
|
||||
* This function also alteres simplex and dir according to further
|
||||
* processing of GJK algorithm. */
|
||||
static int doSimplex(ccd_simplex_t *simplex, ccd_vec3_t *dir);
|
||||
static int doSimplex2(ccd_simplex_t *simplex, ccd_vec3_t *dir);
|
||||
static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir);
|
||||
static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir);
|
||||
|
||||
/** d = a x b x c */
|
||||
_ccd_inline void tripleCross(const ccd_vec3_t *a, const ccd_vec3_t *b,
|
||||
const ccd_vec3_t *c, ccd_vec3_t *d);
|
||||
|
||||
|
||||
/** Transforms simplex to polytope. It is assumed that simplex has 4
|
||||
* vertices. */
|
||||
static int simplexToPolytope4(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd,
|
||||
ccd_simplex_t *simplex,
|
||||
ccd_pt_t *pt, ccd_pt_el_t **nearest);
|
||||
|
||||
/** Transforms simplex to polytope, three vertices required */
|
||||
static int simplexToPolytope3(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd,
|
||||
const ccd_simplex_t *simplex,
|
||||
ccd_pt_t *pt, ccd_pt_el_t **nearest);
|
||||
|
||||
/** Transforms simplex to polytope, two vertices required */
|
||||
static int simplexToPolytope2(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd,
|
||||
const ccd_simplex_t *simplex,
|
||||
ccd_pt_t *pt, ccd_pt_el_t **nearest);
|
||||
|
||||
/** Expands polytope using new vertex v.
|
||||
* Return 0 on success, -2 on memory allocation failure.*/
|
||||
static int expandPolytope(ccd_pt_t *pt, ccd_pt_el_t *el,
|
||||
const ccd_support_t *newv);
|
||||
|
||||
/** Finds next support point (at stores it in out argument).
|
||||
* Returns 0 on success, -1 otherwise */
|
||||
static int nextSupport(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
const ccd_pt_el_t *el,
|
||||
ccd_support_t *out);
|
||||
|
||||
|
||||
|
||||
void ccdFirstDirDefault(const void *o1, const void *o2, ccd_vec3_t *dir)
|
||||
{
|
||||
ccdVec3Set(dir, CCD_ONE, CCD_ZERO, CCD_ZERO);
|
||||
}
|
||||
|
||||
int ccdGJKIntersect(const void *obj1, const void *obj2, const ccd_t *ccd)
|
||||
{
|
||||
ccd_simplex_t simplex;
|
||||
return __ccdGJK(obj1, obj2, ccd, &simplex) == 0;
|
||||
}
|
||||
|
||||
int ccdGJKSeparate(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_vec3_t *sep)
|
||||
{
|
||||
ccd_pt_t polytope;
|
||||
ccd_pt_el_t *nearest;
|
||||
int ret;
|
||||
|
||||
ccdPtInit(&polytope);
|
||||
|
||||
ret = __ccdGJKEPA(obj1, obj2, ccd, &polytope, &nearest);
|
||||
|
||||
// set separation vector
|
||||
if (nearest)
|
||||
ccdVec3Copy(sep, &nearest->witness);
|
||||
|
||||
ccdPtDestroy(&polytope);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int penEPAPosCmp(const void *a, const void *b)
|
||||
{
|
||||
ccd_pt_vertex_t *v1, *v2;
|
||||
v1 = *(ccd_pt_vertex_t **)a;
|
||||
v2 = *(ccd_pt_vertex_t **)b;
|
||||
|
||||
if (ccdEq(v1->dist, v2->dist)){
|
||||
return 0;
|
||||
}else if (v1->dist < v2->dist){
|
||||
return -1;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int penEPAPos(const ccd_pt_t *pt, const ccd_pt_el_t *nearest,
|
||||
ccd_vec3_t *pos)
|
||||
{
|
||||
ccd_pt_vertex_t *v;
|
||||
ccd_pt_vertex_t **vs;
|
||||
size_t i, len;
|
||||
ccd_real_t scale;
|
||||
|
||||
// compute median
|
||||
len = 0;
|
||||
ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
|
||||
len++;
|
||||
}
|
||||
|
||||
vs = CCD_ALLOC_ARR(ccd_pt_vertex_t *, len);
|
||||
if (vs == NULL)
|
||||
return -1;
|
||||
|
||||
i = 0;
|
||||
ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
|
||||
vs[i++] = v;
|
||||
}
|
||||
|
||||
qsort(vs, len, sizeof(ccd_pt_vertex_t *), penEPAPosCmp);
|
||||
|
||||
ccdVec3Set(pos, CCD_ZERO, CCD_ZERO, CCD_ZERO);
|
||||
scale = CCD_ZERO;
|
||||
if (len % 2 == 1)
|
||||
len++;
|
||||
|
||||
for (i = 0; i < len / 2; i++){
|
||||
ccdVec3Add(pos, &vs[i]->v.v1);
|
||||
ccdVec3Add(pos, &vs[i]->v.v2);
|
||||
scale += CCD_REAL(2.);
|
||||
}
|
||||
ccdVec3Scale(pos, CCD_ONE / scale);
|
||||
|
||||
free(vs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ccdGJKPenetration(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos)
|
||||
{
|
||||
ccd_pt_t polytope;
|
||||
ccd_pt_el_t *nearest;
|
||||
int ret;
|
||||
|
||||
ccdPtInit(&polytope);
|
||||
|
||||
ret = __ccdGJKEPA(obj1, obj2, ccd, &polytope, &nearest);
|
||||
|
||||
// set separation vector
|
||||
if (ret == 0 && nearest){
|
||||
// compute depth of penetration
|
||||
*depth = CCD_SQRT(nearest->dist);
|
||||
|
||||
// store normalized direction vector
|
||||
ccdVec3Copy(dir, &nearest->witness);
|
||||
ccdVec3Normalize(dir);
|
||||
|
||||
// compute position
|
||||
if (penEPAPos(&polytope, nearest, pos) != 0){
|
||||
ccdPtDestroy(&polytope);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
ccdPtDestroy(&polytope);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int __ccdGJK(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_simplex_t *simplex)
|
||||
{
|
||||
unsigned long iterations;
|
||||
ccd_vec3_t dir; // direction vector
|
||||
ccd_support_t last; // last support point
|
||||
int do_simplex_res;
|
||||
|
||||
// initialize simplex struct
|
||||
ccdSimplexInit(simplex);
|
||||
|
||||
// get first direction
|
||||
ccd->first_dir(obj1, obj2, &dir);
|
||||
// get first support point
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &last);
|
||||
// and add this point to simplex as last one
|
||||
ccdSimplexAdd(simplex, &last);
|
||||
|
||||
// set up direction vector to as (O - last) which is exactly -last
|
||||
ccdVec3Copy(&dir, &last.v);
|
||||
ccdVec3Scale(&dir, -CCD_ONE);
|
||||
|
||||
// start iterations
|
||||
for (iterations = 0UL; iterations < ccd->max_iterations; ++iterations) {
|
||||
// obtain support point
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &last);
|
||||
|
||||
// check if farthest point in Minkowski difference in direction dir
|
||||
// isn't somewhere before origin (the test on negative dot product)
|
||||
// - because if it is, objects are not intersecting at all.
|
||||
if (ccdVec3Dot(&last.v, &dir) < CCD_ZERO){
|
||||
return -1; // intersection not found
|
||||
}
|
||||
|
||||
// add last support vector to simplex
|
||||
ccdSimplexAdd(simplex, &last);
|
||||
|
||||
// if doSimplex returns 1 if objects intersect, -1 if objects don't
|
||||
// intersect and 0 if algorithm should continue
|
||||
do_simplex_res = doSimplex(simplex, &dir);
|
||||
if (do_simplex_res == 1){
|
||||
return 0; // intersection found
|
||||
}else if (do_simplex_res == -1){
|
||||
return -1; // intersection not found
|
||||
}
|
||||
|
||||
if (ccdIsZero(ccdVec3Len2(&dir))){
|
||||
return -1; // intersection not found
|
||||
}
|
||||
}
|
||||
|
||||
// intersection wasn't found
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __ccdGJKEPA(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd,
|
||||
ccd_pt_t *polytope, ccd_pt_el_t **nearest)
|
||||
{
|
||||
ccd_simplex_t simplex;
|
||||
ccd_support_t supp; // support point
|
||||
int ret, size;
|
||||
|
||||
*nearest = NULL;
|
||||
|
||||
// run GJK and obtain terminal simplex
|
||||
ret = __ccdGJK(obj1, obj2, ccd, &simplex);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
// transform simplex to polytope - simplex won't be used anymore
|
||||
size = ccdSimplexSize(&simplex);
|
||||
if (size == 4){
|
||||
ret = simplexToPolytope4(obj1, obj2, ccd, &simplex, polytope, nearest);
|
||||
}else if (size == 3){
|
||||
ret = simplexToPolytope3(obj1, obj2, ccd, &simplex, polytope, nearest);
|
||||
}else{ // size == 2
|
||||
ret = simplexToPolytope2(obj1, obj2, ccd, &simplex, polytope, nearest);
|
||||
}
|
||||
|
||||
if (ret == -1){
|
||||
// touching contact
|
||||
return 0;
|
||||
}else if (ret == -2){
|
||||
// failed memory allocation
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
while (1){
|
||||
// get triangle nearest to origin
|
||||
*nearest = ccdPtNearest(polytope);
|
||||
|
||||
// get next support point
|
||||
if (nextSupport(obj1, obj2, ccd, *nearest, &supp) != 0)
|
||||
break;
|
||||
|
||||
// expand nearest triangle using new point - supp
|
||||
if (expandPolytope(polytope, *nearest, &supp) != 0)
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int doSimplex2(ccd_simplex_t *simplex, ccd_vec3_t *dir)
|
||||
{
|
||||
const ccd_support_t *A, *B;
|
||||
ccd_vec3_t AB, AO, tmp;
|
||||
ccd_real_t dot;
|
||||
|
||||
// get last added as A
|
||||
A = ccdSimplexLast(simplex);
|
||||
// get the other point
|
||||
B = ccdSimplexPoint(simplex, 0);
|
||||
// compute AB oriented segment
|
||||
ccdVec3Sub2(&AB, &B->v, &A->v);
|
||||
// compute AO vector
|
||||
ccdVec3Copy(&AO, &A->v);
|
||||
ccdVec3Scale(&AO, -CCD_ONE);
|
||||
|
||||
// dot product AB . AO
|
||||
dot = ccdVec3Dot(&AB, &AO);
|
||||
|
||||
// check if origin doesn't lie on AB segment
|
||||
ccdVec3Cross(&tmp, &AB, &AO);
|
||||
if (ccdIsZero(ccdVec3Len2(&tmp)) && dot > CCD_ZERO){
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if origin is in area where AB segment is
|
||||
if (ccdIsZero(dot) || dot < CCD_ZERO){
|
||||
// origin is in outside are of A
|
||||
ccdSimplexSet(simplex, 0, A);
|
||||
ccdSimplexSetSize(simplex, 1);
|
||||
ccdVec3Copy(dir, &AO);
|
||||
}else{
|
||||
// origin is in area where AB segment is
|
||||
|
||||
// keep simplex untouched and set direction to
|
||||
// AB x AO x AB
|
||||
tripleCross(&AB, &AO, &AB, dir);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir)
|
||||
{
|
||||
const ccd_support_t *A, *B, *C;
|
||||
ccd_vec3_t AO, AB, AC, ABC, tmp;
|
||||
ccd_real_t dot, dist;
|
||||
|
||||
// get last added as A
|
||||
A = ccdSimplexLast(simplex);
|
||||
// get the other points
|
||||
B = ccdSimplexPoint(simplex, 1);
|
||||
C = ccdSimplexPoint(simplex, 0);
|
||||
|
||||
// check touching contact
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL);
|
||||
if (ccdIsZero(dist)){
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if triangle is really triangle (has area > 0)
|
||||
// if not simplex can't be expanded and thus no itersection is found
|
||||
if (ccdVec3Eq(&A->v, &B->v) || ccdVec3Eq(&A->v, &C->v)){
|
||||
return -1;
|
||||
}
|
||||
|
||||
// compute AO vector
|
||||
ccdVec3Copy(&AO, &A->v);
|
||||
ccdVec3Scale(&AO, -CCD_ONE);
|
||||
|
||||
// compute AB and AC segments and ABC vector (perpendircular to triangle)
|
||||
ccdVec3Sub2(&AB, &B->v, &A->v);
|
||||
ccdVec3Sub2(&AC, &C->v, &A->v);
|
||||
ccdVec3Cross(&ABC, &AB, &AC);
|
||||
|
||||
ccdVec3Cross(&tmp, &ABC, &AC);
|
||||
dot = ccdVec3Dot(&tmp, &AO);
|
||||
if (ccdIsZero(dot) || dot > CCD_ZERO){
|
||||
dot = ccdVec3Dot(&AC, &AO);
|
||||
if (ccdIsZero(dot) || dot > CCD_ZERO){
|
||||
// C is already in place
|
||||
ccdSimplexSet(simplex, 1, A);
|
||||
ccdSimplexSetSize(simplex, 2);
|
||||
tripleCross(&AC, &AO, &AC, dir);
|
||||
}else{
|
||||
ccd_do_simplex3_45:
|
||||
dot = ccdVec3Dot(&AB, &AO);
|
||||
if (ccdIsZero(dot) || dot > CCD_ZERO){
|
||||
ccdSimplexSet(simplex, 0, B);
|
||||
ccdSimplexSet(simplex, 1, A);
|
||||
ccdSimplexSetSize(simplex, 2);
|
||||
tripleCross(&AB, &AO, &AB, dir);
|
||||
}else{
|
||||
ccdSimplexSet(simplex, 0, A);
|
||||
ccdSimplexSetSize(simplex, 1);
|
||||
ccdVec3Copy(dir, &AO);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
ccdVec3Cross(&tmp, &AB, &ABC);
|
||||
dot = ccdVec3Dot(&tmp, &AO);
|
||||
if (ccdIsZero(dot) || dot > CCD_ZERO){
|
||||
goto ccd_do_simplex3_45;
|
||||
}else{
|
||||
dot = ccdVec3Dot(&ABC, &AO);
|
||||
if (ccdIsZero(dot) || dot > CCD_ZERO){
|
||||
ccdVec3Copy(dir, &ABC);
|
||||
}else{
|
||||
ccd_support_t Ctmp;
|
||||
ccdSupportCopy(&Ctmp, C);
|
||||
ccdSimplexSet(simplex, 0, B);
|
||||
ccdSimplexSet(simplex, 1, &Ctmp);
|
||||
|
||||
ccdVec3Copy(dir, &ABC);
|
||||
ccdVec3Scale(dir, -CCD_ONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir)
|
||||
{
|
||||
const ccd_support_t *A, *B, *C, *D;
|
||||
ccd_vec3_t AO, AB, AC, AD, ABC, ACD, ADB;
|
||||
int B_on_ACD, C_on_ADB, D_on_ABC;
|
||||
int AB_O, AC_O, AD_O;
|
||||
ccd_real_t dist;
|
||||
|
||||
// get last added as A
|
||||
A = ccdSimplexLast(simplex);
|
||||
// get the other points
|
||||
B = ccdSimplexPoint(simplex, 2);
|
||||
C = ccdSimplexPoint(simplex, 1);
|
||||
D = ccdSimplexPoint(simplex, 0);
|
||||
|
||||
// check if tetrahedron is really tetrahedron (has volume > 0)
|
||||
// if it is not simplex can't be expanded and thus no intersection is
|
||||
// found
|
||||
dist = ccdVec3PointTriDist2(&A->v, &B->v, &C->v, &D->v, NULL);
|
||||
if (ccdIsZero(dist)){
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check if origin lies on some of tetrahedron's face - if so objects
|
||||
// intersect
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL);
|
||||
if (ccdIsZero(dist))
|
||||
return 1;
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &C->v, &D->v, NULL);
|
||||
if (ccdIsZero(dist))
|
||||
return 1;
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &D->v, NULL);
|
||||
if (ccdIsZero(dist))
|
||||
return 1;
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &B->v, &C->v, &D->v, NULL);
|
||||
if (ccdIsZero(dist))
|
||||
return 1;
|
||||
|
||||
// compute AO, AB, AC, AD segments and ABC, ACD, ADB normal vectors
|
||||
ccdVec3Copy(&AO, &A->v);
|
||||
ccdVec3Scale(&AO, -CCD_ONE);
|
||||
ccdVec3Sub2(&AB, &B->v, &A->v);
|
||||
ccdVec3Sub2(&AC, &C->v, &A->v);
|
||||
ccdVec3Sub2(&AD, &D->v, &A->v);
|
||||
ccdVec3Cross(&ABC, &AB, &AC);
|
||||
ccdVec3Cross(&ACD, &AC, &AD);
|
||||
ccdVec3Cross(&ADB, &AD, &AB);
|
||||
|
||||
// side (positive or negative) of B, C, D relative to planes ACD, ADB
|
||||
// and ABC respectively
|
||||
B_on_ACD = ccdSign(ccdVec3Dot(&ACD, &AB));
|
||||
C_on_ADB = ccdSign(ccdVec3Dot(&ADB, &AC));
|
||||
D_on_ABC = ccdSign(ccdVec3Dot(&ABC, &AD));
|
||||
|
||||
// whether origin is on same side of ACD, ADB, ABC as B, C, D
|
||||
// respectively
|
||||
AB_O = ccdSign(ccdVec3Dot(&ACD, &AO)) == B_on_ACD;
|
||||
AC_O = ccdSign(ccdVec3Dot(&ADB, &AO)) == C_on_ADB;
|
||||
AD_O = ccdSign(ccdVec3Dot(&ABC, &AO)) == D_on_ABC;
|
||||
|
||||
if (AB_O && AC_O && AD_O){
|
||||
// origin is in tetrahedron
|
||||
return 1;
|
||||
|
||||
// rearrange simplex to triangle and call doSimplex3()
|
||||
}else if (!AB_O){
|
||||
// B is farthest from the origin among all of the tetrahedron's
|
||||
// points, so remove it from the list and go on with the triangle
|
||||
// case
|
||||
|
||||
// D and C are in place
|
||||
ccdSimplexSet(simplex, 2, A);
|
||||
ccdSimplexSetSize(simplex, 3);
|
||||
}else if (!AC_O){
|
||||
// C is farthest
|
||||
ccdSimplexSet(simplex, 1, D);
|
||||
ccdSimplexSet(simplex, 0, B);
|
||||
ccdSimplexSet(simplex, 2, A);
|
||||
ccdSimplexSetSize(simplex, 3);
|
||||
}else{ // (!AD_O)
|
||||
ccdSimplexSet(simplex, 0, C);
|
||||
ccdSimplexSet(simplex, 1, B);
|
||||
ccdSimplexSet(simplex, 2, A);
|
||||
ccdSimplexSetSize(simplex, 3);
|
||||
}
|
||||
|
||||
return doSimplex3(simplex, dir);
|
||||
}
|
||||
|
||||
static int doSimplex(ccd_simplex_t *simplex, ccd_vec3_t *dir)
|
||||
{
|
||||
if (ccdSimplexSize(simplex) == 2){
|
||||
// simplex contains segment only one segment
|
||||
return doSimplex2(simplex, dir);
|
||||
}else if (ccdSimplexSize(simplex) == 3){
|
||||
// simplex contains triangle
|
||||
return doSimplex3(simplex, dir);
|
||||
}else{ // ccdSimplexSize(simplex) == 4
|
||||
// tetrahedron - this is the only shape which can encapsule origin
|
||||
// so doSimplex4() also contains test on it
|
||||
return doSimplex4(simplex, dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_ccd_inline void tripleCross(const ccd_vec3_t *a, const ccd_vec3_t *b,
|
||||
const ccd_vec3_t *c, ccd_vec3_t *d)
|
||||
{
|
||||
ccd_vec3_t e;
|
||||
ccdVec3Cross(&e, a, b);
|
||||
ccdVec3Cross(d, &e, c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Transforms simplex to polytope. It is assumed that simplex has 4
|
||||
* vertices! */
|
||||
static int simplexToPolytope4(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd,
|
||||
ccd_simplex_t *simplex,
|
||||
ccd_pt_t *pt, ccd_pt_el_t **nearest)
|
||||
{
|
||||
const ccd_support_t *a, *b, *c, *d;
|
||||
int use_polytope3;
|
||||
ccd_real_t dist;
|
||||
ccd_pt_vertex_t *v[4];
|
||||
ccd_pt_edge_t *e[6];
|
||||
size_t i;
|
||||
|
||||
a = ccdSimplexPoint(simplex, 0);
|
||||
b = ccdSimplexPoint(simplex, 1);
|
||||
c = ccdSimplexPoint(simplex, 2);
|
||||
d = ccdSimplexPoint(simplex, 3);
|
||||
|
||||
// check if origin lies on some of tetrahedron's face - if so use
|
||||
// simplexToPolytope3()
|
||||
use_polytope3 = 0;
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &b->v, &c->v, NULL);
|
||||
if (ccdIsZero(dist)){
|
||||
use_polytope3 = 1;
|
||||
}
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &c->v, &d->v, NULL);
|
||||
if (ccdIsZero(dist)){
|
||||
use_polytope3 = 1;
|
||||
ccdSimplexSet(simplex, 1, c);
|
||||
ccdSimplexSet(simplex, 2, d);
|
||||
}
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &b->v, &d->v, NULL);
|
||||
if (ccdIsZero(dist)){
|
||||
use_polytope3 = 1;
|
||||
ccdSimplexSet(simplex, 2, d);
|
||||
}
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &b->v, &c->v, &d->v, NULL);
|
||||
if (ccdIsZero(dist)){
|
||||
use_polytope3 = 1;
|
||||
ccdSimplexSet(simplex, 0, b);
|
||||
ccdSimplexSet(simplex, 1, c);
|
||||
ccdSimplexSet(simplex, 2, d);
|
||||
}
|
||||
|
||||
if (use_polytope3){
|
||||
ccdSimplexSetSize(simplex, 3);
|
||||
return simplexToPolytope3(obj1, obj2, ccd, simplex, pt, nearest);
|
||||
}
|
||||
|
||||
// no touching contact - simply create tetrahedron
|
||||
for (i = 0; i < 4; i++){
|
||||
v[i] = ccdPtAddVertex(pt, ccdSimplexPoint(simplex, i));
|
||||
}
|
||||
|
||||
e[0] = ccdPtAddEdge(pt, v[0], v[1]);
|
||||
e[1] = ccdPtAddEdge(pt, v[1], v[2]);
|
||||
e[2] = ccdPtAddEdge(pt, v[2], v[0]);
|
||||
e[3] = ccdPtAddEdge(pt, v[3], v[0]);
|
||||
e[4] = ccdPtAddEdge(pt, v[3], v[1]);
|
||||
e[5] = ccdPtAddEdge(pt, v[3], v[2]);
|
||||
|
||||
// ccdPtAdd*() functions return NULL either if the memory allocation
|
||||
// failed of if any of the input pointers are NULL, so the bad
|
||||
// allocation can be checked by the last calls of ccdPtAddFace()
|
||||
// because the rest of the bad allocations eventually "bubble up" here
|
||||
if (ccdPtAddFace(pt, e[0], e[1], e[2]) == NULL
|
||||
|| ccdPtAddFace(pt, e[3], e[4], e[0]) == NULL
|
||||
|| ccdPtAddFace(pt, e[4], e[5], e[1]) == NULL
|
||||
|| ccdPtAddFace(pt, e[5], e[3], e[2]) == NULL){
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Transforms simplex to polytope, three vertices required */
|
||||
static int simplexToPolytope3(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd,
|
||||
const ccd_simplex_t *simplex,
|
||||
ccd_pt_t *pt, ccd_pt_el_t **nearest)
|
||||
{
|
||||
const ccd_support_t *a, *b, *c;
|
||||
ccd_support_t d, d2;
|
||||
ccd_vec3_t ab, ac, dir;
|
||||
ccd_pt_vertex_t *v[5];
|
||||
ccd_pt_edge_t *e[9];
|
||||
ccd_real_t dist, dist2;
|
||||
|
||||
*nearest = NULL;
|
||||
|
||||
a = ccdSimplexPoint(simplex, 0);
|
||||
b = ccdSimplexPoint(simplex, 1);
|
||||
c = ccdSimplexPoint(simplex, 2);
|
||||
|
||||
// If only one triangle left from previous GJK run origin lies on this
|
||||
// triangle. So it is necessary to expand triangle into two
|
||||
// tetrahedrons connected with base (which is exactly abc triangle).
|
||||
|
||||
// get next support point in direction of normal of triangle
|
||||
ccdVec3Sub2(&ab, &b->v, &a->v);
|
||||
ccdVec3Sub2(&ac, &c->v, &a->v);
|
||||
ccdVec3Cross(&dir, &ab, &ac);
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &d);
|
||||
dist = ccdVec3PointTriDist2(&d.v, &a->v, &b->v, &c->v, NULL);
|
||||
|
||||
// and second one take in opposite direction
|
||||
ccdVec3Scale(&dir, -CCD_ONE);
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &d2);
|
||||
dist2 = ccdVec3PointTriDist2(&d2.v, &a->v, &b->v, &c->v, NULL);
|
||||
|
||||
// check if face isn't already on edge of minkowski sum and thus we
|
||||
// have touching contact
|
||||
if (ccdIsZero(dist) || ccdIsZero(dist2)){
|
||||
v[0] = ccdPtAddVertex(pt, a);
|
||||
v[1] = ccdPtAddVertex(pt, b);
|
||||
v[2] = ccdPtAddVertex(pt, c);
|
||||
e[0] = ccdPtAddEdge(pt, v[0], v[1]);
|
||||
e[1] = ccdPtAddEdge(pt, v[1], v[2]);
|
||||
e[2] = ccdPtAddEdge(pt, v[2], v[0]);
|
||||
*nearest = (ccd_pt_el_t *)ccdPtAddFace(pt, e[0], e[1], e[2]);
|
||||
if (*nearest == NULL)
|
||||
return -2;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// form polyhedron
|
||||
v[0] = ccdPtAddVertex(pt, a);
|
||||
v[1] = ccdPtAddVertex(pt, b);
|
||||
v[2] = ccdPtAddVertex(pt, c);
|
||||
v[3] = ccdPtAddVertex(pt, &d);
|
||||
v[4] = ccdPtAddVertex(pt, &d2);
|
||||
|
||||
e[0] = ccdPtAddEdge(pt, v[0], v[1]);
|
||||
e[1] = ccdPtAddEdge(pt, v[1], v[2]);
|
||||
e[2] = ccdPtAddEdge(pt, v[2], v[0]);
|
||||
|
||||
e[3] = ccdPtAddEdge(pt, v[3], v[0]);
|
||||
e[4] = ccdPtAddEdge(pt, v[3], v[1]);
|
||||
e[5] = ccdPtAddEdge(pt, v[3], v[2]);
|
||||
|
||||
e[6] = ccdPtAddEdge(pt, v[4], v[0]);
|
||||
e[7] = ccdPtAddEdge(pt, v[4], v[1]);
|
||||
e[8] = ccdPtAddEdge(pt, v[4], v[2]);
|
||||
|
||||
if (ccdPtAddFace(pt, e[3], e[4], e[0]) == NULL
|
||||
|| ccdPtAddFace(pt, e[4], e[5], e[1]) == NULL
|
||||
|| ccdPtAddFace(pt, e[5], e[3], e[2]) == NULL
|
||||
|
||||
|| ccdPtAddFace(pt, e[6], e[7], e[0]) == NULL
|
||||
|| ccdPtAddFace(pt, e[7], e[8], e[1]) == NULL
|
||||
|| ccdPtAddFace(pt, e[8], e[6], e[2]) == NULL){
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Transforms simplex to polytope, two vertices required */
|
||||
static int simplexToPolytope2(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd,
|
||||
const ccd_simplex_t *simplex,
|
||||
ccd_pt_t *pt, ccd_pt_el_t **nearest)
|
||||
{
|
||||
const ccd_support_t *a, *b;
|
||||
ccd_vec3_t ab, ac, dir;
|
||||
ccd_support_t supp[4];
|
||||
ccd_pt_vertex_t *v[6];
|
||||
ccd_pt_edge_t *e[12];
|
||||
size_t i;
|
||||
int found;
|
||||
|
||||
a = ccdSimplexPoint(simplex, 0);
|
||||
b = ccdSimplexPoint(simplex, 1);
|
||||
|
||||
// This situation is a bit tricky. If only one segment comes from
|
||||
// previous run of GJK - it means that either this segment is on
|
||||
// minkowski edge (and thus we have touch contact) or it it isn't and
|
||||
// therefore segment is somewhere *inside* minkowski sum and it *must*
|
||||
// be possible to fully enclose this segment with polyhedron formed by
|
||||
// at least 8 triangle faces.
|
||||
|
||||
// get first support point (any)
|
||||
found = 0;
|
||||
for (i = 0; i < ccd_points_on_sphere_len; i++){
|
||||
__ccdSupport(obj1, obj2, &ccd_points_on_sphere[i], ccd, &supp[0]);
|
||||
if (!ccdVec3Eq(&a->v, &supp[0].v) && !ccdVec3Eq(&b->v, &supp[0].v)){
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
goto simplexToPolytope2_touching_contact;
|
||||
|
||||
// get second support point in opposite direction than supp[0]
|
||||
ccdVec3Copy(&dir, &supp[0].v);
|
||||
ccdVec3Scale(&dir, -CCD_ONE);
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &supp[1]);
|
||||
if (ccdVec3Eq(&a->v, &supp[1].v) || ccdVec3Eq(&b->v, &supp[1].v))
|
||||
goto simplexToPolytope2_touching_contact;
|
||||
|
||||
// next will be in direction of normal of triangle a,supp[0],supp[1]
|
||||
ccdVec3Sub2(&ab, &supp[0].v, &a->v);
|
||||
ccdVec3Sub2(&ac, &supp[1].v, &a->v);
|
||||
ccdVec3Cross(&dir, &ab, &ac);
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &supp[2]);
|
||||
if (ccdVec3Eq(&a->v, &supp[2].v) || ccdVec3Eq(&b->v, &supp[2].v))
|
||||
goto simplexToPolytope2_touching_contact;
|
||||
|
||||
// and last one will be in opposite direction
|
||||
ccdVec3Scale(&dir, -CCD_ONE);
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &supp[3]);
|
||||
if (ccdVec3Eq(&a->v, &supp[3].v) || ccdVec3Eq(&b->v, &supp[3].v))
|
||||
goto simplexToPolytope2_touching_contact;
|
||||
|
||||
goto simplexToPolytope2_not_touching_contact;
|
||||
simplexToPolytope2_touching_contact:
|
||||
v[0] = ccdPtAddVertex(pt, a);
|
||||
v[1] = ccdPtAddVertex(pt, b);
|
||||
*nearest = (ccd_pt_el_t *)ccdPtAddEdge(pt, v[0], v[1]);
|
||||
if (*nearest == NULL)
|
||||
return -2;
|
||||
|
||||
return -1;
|
||||
|
||||
simplexToPolytope2_not_touching_contact:
|
||||
// form polyhedron
|
||||
v[0] = ccdPtAddVertex(pt, a);
|
||||
v[1] = ccdPtAddVertex(pt, &supp[0]);
|
||||
v[2] = ccdPtAddVertex(pt, b);
|
||||
v[3] = ccdPtAddVertex(pt, &supp[1]);
|
||||
v[4] = ccdPtAddVertex(pt, &supp[2]);
|
||||
v[5] = ccdPtAddVertex(pt, &supp[3]);
|
||||
|
||||
e[0] = ccdPtAddEdge(pt, v[0], v[1]);
|
||||
e[1] = ccdPtAddEdge(pt, v[1], v[2]);
|
||||
e[2] = ccdPtAddEdge(pt, v[2], v[3]);
|
||||
e[3] = ccdPtAddEdge(pt, v[3], v[0]);
|
||||
|
||||
e[4] = ccdPtAddEdge(pt, v[4], v[0]);
|
||||
e[5] = ccdPtAddEdge(pt, v[4], v[1]);
|
||||
e[6] = ccdPtAddEdge(pt, v[4], v[2]);
|
||||
e[7] = ccdPtAddEdge(pt, v[4], v[3]);
|
||||
|
||||
e[8] = ccdPtAddEdge(pt, v[5], v[0]);
|
||||
e[9] = ccdPtAddEdge(pt, v[5], v[1]);
|
||||
e[10] = ccdPtAddEdge(pt, v[5], v[2]);
|
||||
e[11] = ccdPtAddEdge(pt, v[5], v[3]);
|
||||
|
||||
if (ccdPtAddFace(pt, e[4], e[5], e[0]) == NULL
|
||||
|| ccdPtAddFace(pt, e[5], e[6], e[1]) == NULL
|
||||
|| ccdPtAddFace(pt, e[6], e[7], e[2]) == NULL
|
||||
|| ccdPtAddFace(pt, e[7], e[4], e[3]) == NULL
|
||||
|
||||
|| ccdPtAddFace(pt, e[8], e[9], e[0]) == NULL
|
||||
|| ccdPtAddFace(pt, e[9], e[10], e[1]) == NULL
|
||||
|| ccdPtAddFace(pt, e[10], e[11], e[2]) == NULL
|
||||
|| ccdPtAddFace(pt, e[11], e[8], e[3]) == NULL){
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Expands polytope's tri by new vertex v. Triangle tri is replaced by
|
||||
* three triangles each with one vertex in v. */
|
||||
static int expandPolytope(ccd_pt_t *pt, ccd_pt_el_t *el,
|
||||
const ccd_support_t *newv)
|
||||
{
|
||||
ccd_pt_vertex_t *v[5];
|
||||
ccd_pt_edge_t *e[8];
|
||||
ccd_pt_face_t *f[2];
|
||||
|
||||
|
||||
// element can be either segment or triangle
|
||||
if (el->type == CCD_PT_EDGE){
|
||||
// In this case, segment should be replaced by new point.
|
||||
// Simpliest case is when segment stands alone and in this case
|
||||
// this segment is replaced by two other segments both connected to
|
||||
// newv.
|
||||
// Segment can be also connected to max two faces and in that case
|
||||
// each face must be replaced by two other faces. To do this
|
||||
// correctly it is necessary to have correctly ordered edges and
|
||||
// vertices which is exactly what is done in following code.
|
||||
//
|
||||
|
||||
ccdPtEdgeVertices((const ccd_pt_edge_t *)el, &v[0], &v[2]);
|
||||
|
||||
ccdPtEdgeFaces((ccd_pt_edge_t *)el, &f[0], &f[1]);
|
||||
|
||||
if (f[0]){
|
||||
ccdPtFaceEdges(f[0], &e[0], &e[1], &e[2]);
|
||||
if (e[0] == (ccd_pt_edge_t *)el){
|
||||
e[0] = e[2];
|
||||
}else if (e[1] == (ccd_pt_edge_t *)el){
|
||||
e[1] = e[2];
|
||||
}
|
||||
ccdPtEdgeVertices(e[0], &v[1], &v[3]);
|
||||
if (v[1] != v[0] && v[3] != v[0]){
|
||||
e[2] = e[0];
|
||||
e[0] = e[1];
|
||||
e[1] = e[2];
|
||||
if (v[1] == v[2])
|
||||
v[1] = v[3];
|
||||
}else{
|
||||
if (v[1] == v[0])
|
||||
v[1] = v[3];
|
||||
}
|
||||
|
||||
if (f[1]){
|
||||
ccdPtFaceEdges(f[1], &e[2], &e[3], &e[4]);
|
||||
if (e[2] == (ccd_pt_edge_t *)el){
|
||||
e[2] = e[4];
|
||||
}else if (e[3] == (ccd_pt_edge_t *)el){
|
||||
e[3] = e[4];
|
||||
}
|
||||
ccdPtEdgeVertices(e[2], &v[3], &v[4]);
|
||||
if (v[3] != v[2] && v[4] != v[2]){
|
||||
e[4] = e[2];
|
||||
e[2] = e[3];
|
||||
e[3] = e[4];
|
||||
if (v[3] == v[0])
|
||||
v[3] = v[4];
|
||||
}else{
|
||||
if (v[3] == v[2])
|
||||
v[3] = v[4];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
v[4] = ccdPtAddVertex(pt, newv);
|
||||
|
||||
ccdPtDelFace(pt, f[0]);
|
||||
if (f[1]){
|
||||
ccdPtDelFace(pt, f[1]);
|
||||
ccdPtDelEdge(pt, (ccd_pt_edge_t *)el);
|
||||
}
|
||||
|
||||
e[4] = ccdPtAddEdge(pt, v[4], v[2]);
|
||||
e[5] = ccdPtAddEdge(pt, v[4], v[0]);
|
||||
e[6] = ccdPtAddEdge(pt, v[4], v[1]);
|
||||
if (f[1])
|
||||
e[7] = ccdPtAddEdge(pt, v[4], v[3]);
|
||||
|
||||
|
||||
if (ccdPtAddFace(pt, e[1], e[4], e[6]) == NULL
|
||||
|| ccdPtAddFace(pt, e[0], e[6], e[5]) == NULL){
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (f[1]){
|
||||
if (ccdPtAddFace(pt, e[3], e[5], e[7]) == NULL
|
||||
|| ccdPtAddFace(pt, e[4], e[7], e[2]) == NULL){
|
||||
return -2;
|
||||
}
|
||||
}else{
|
||||
if (ccdPtAddFace(pt, e[4], e[5], (ccd_pt_edge_t *)el) == NULL)
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
}else{ // el->type == CCD_PT_FACE
|
||||
// replace triangle by tetrahedron without base (base would be the
|
||||
// triangle that will be removed)
|
||||
|
||||
// get triplet of surrounding edges and vertices of triangle face
|
||||
ccdPtFaceEdges((const ccd_pt_face_t *)el, &e[0], &e[1], &e[2]);
|
||||
ccdPtEdgeVertices(e[0], &v[0], &v[1]);
|
||||
ccdPtEdgeVertices(e[1], &v[2], &v[3]);
|
||||
|
||||
// following code sorts edges to have e[0] between vertices 0-1,
|
||||
// e[1] between 1-2 and e[2] between 2-0
|
||||
if (v[2] != v[1] && v[3] != v[1]){
|
||||
// swap e[1] and e[2]
|
||||
e[3] = e[1];
|
||||
e[1] = e[2];
|
||||
e[2] = e[3];
|
||||
}
|
||||
if (v[3] != v[0] && v[3] != v[1])
|
||||
v[2] = v[3];
|
||||
|
||||
// remove triangle face
|
||||
ccdPtDelFace(pt, (ccd_pt_face_t *)el);
|
||||
|
||||
// expand triangle to tetrahedron
|
||||
v[3] = ccdPtAddVertex(pt, newv);
|
||||
e[3] = ccdPtAddEdge(pt, v[3], v[0]);
|
||||
e[4] = ccdPtAddEdge(pt, v[3], v[1]);
|
||||
e[5] = ccdPtAddEdge(pt, v[3], v[2]);
|
||||
|
||||
if (ccdPtAddFace(pt, e[3], e[4], e[0]) == NULL
|
||||
|| ccdPtAddFace(pt, e[4], e[5], e[1]) == NULL
|
||||
|| ccdPtAddFace(pt, e[5], e[3], e[2]) == NULL){
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Finds next support point (and stores it in out argument).
|
||||
* Returns 0 on success, -1 otherwise */
|
||||
static int nextSupport(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
const ccd_pt_el_t *el,
|
||||
ccd_support_t *out)
|
||||
{
|
||||
ccd_vec3_t *a, *b, *c;
|
||||
ccd_real_t dist;
|
||||
|
||||
if (el->type == CCD_PT_VERTEX)
|
||||
return -1;
|
||||
|
||||
// touch contact
|
||||
if (ccdIsZero(el->dist))
|
||||
return -1;
|
||||
|
||||
__ccdSupport(obj1, obj2, &el->witness, ccd, out);
|
||||
|
||||
// Compute dist of support point along element witness point direction
|
||||
// so we can determine whether we expanded a polytope surrounding the
|
||||
// origin a bit.
|
||||
dist = ccdVec3Dot(&out->v, &el->witness);
|
||||
|
||||
if (dist - el->dist < ccd->epa_tolerance)
|
||||
return -1;
|
||||
|
||||
if (el->type == CCD_PT_EDGE){
|
||||
// fetch end points of edge
|
||||
ccdPtEdgeVec3((ccd_pt_edge_t *)el, &a, &b);
|
||||
|
||||
// get distance from segment
|
||||
dist = ccdVec3PointSegmentDist2(&out->v, a, b, NULL);
|
||||
}else{ // el->type == CCD_PT_FACE
|
||||
// fetch vertices of triangle face
|
||||
ccdPtFaceVec3((ccd_pt_face_t *)el, &a, &b, &c);
|
||||
|
||||
// check if new point can significantly expand polytope
|
||||
dist = ccdVec3PointTriDist2(&out->v, a, b, c, NULL);
|
||||
}
|
||||
|
||||
if (dist < ccd->epa_tolerance)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010,2011 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_H__
|
||||
#define __CCD_H__
|
||||
|
||||
#include <ccd/vec3.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* Type of *support* function that takes pointer to 3D object and direction
|
||||
* and returns (via vec argument) furthest point from object in specified
|
||||
* direction.
|
||||
*/
|
||||
typedef void (*ccd_support_fn)(const void *obj, const ccd_vec3_t *dir,
|
||||
ccd_vec3_t *vec);
|
||||
|
||||
/**
|
||||
* Returns (via dir argument) first direction vector that will be used in
|
||||
* initialization of algorithm.
|
||||
*/
|
||||
typedef void (*ccd_first_dir_fn)(const void *obj1, const void *obj2,
|
||||
ccd_vec3_t *dir);
|
||||
|
||||
|
||||
/**
|
||||
* Returns (via center argument) geometric center (some point near center)
|
||||
* of given object.
|
||||
*/
|
||||
typedef void (*ccd_center_fn)(const void *obj1, ccd_vec3_t *center);
|
||||
|
||||
/**
|
||||
* Main structure of CCD algorithm.
|
||||
*/
|
||||
struct _ccd_t {
|
||||
ccd_first_dir_fn first_dir; //!< Returns initial direction where first
|
||||
//!< support point will be searched
|
||||
ccd_support_fn support1; //!< Function that returns support point of
|
||||
//!< first object
|
||||
ccd_support_fn support2; //!< Function that returns support point of
|
||||
//!< second object
|
||||
|
||||
ccd_center_fn center1; //!< Function that returns geometric center of
|
||||
//!< first object
|
||||
ccd_center_fn center2; //!< Function that returns geometric center of
|
||||
//!< second object
|
||||
|
||||
unsigned long max_iterations; //!< Maximal number of iterations
|
||||
ccd_real_t epa_tolerance;
|
||||
ccd_real_t mpr_tolerance; //!< Boundary tolerance for MPR algorithm
|
||||
ccd_real_t dist_tolerance;
|
||||
};
|
||||
typedef struct _ccd_t ccd_t;
|
||||
|
||||
/**
|
||||
* Default first direction.
|
||||
*/
|
||||
CCD_EXPORT void ccdFirstDirDefault(const void *o1, const void *o2,
|
||||
ccd_vec3_t *dir);
|
||||
|
||||
#define CCD_INIT(ccd) \
|
||||
do { \
|
||||
(ccd)->first_dir = ccdFirstDirDefault; \
|
||||
(ccd)->support1 = NULL; \
|
||||
(ccd)->support2 = NULL; \
|
||||
(ccd)->center1 = NULL; \
|
||||
(ccd)->center2 = NULL; \
|
||||
\
|
||||
(ccd)->max_iterations = (unsigned long)-1; \
|
||||
(ccd)->epa_tolerance = CCD_REAL(0.0001); \
|
||||
(ccd)->mpr_tolerance = CCD_REAL(0.0001); \
|
||||
(ccd)->dist_tolerance = CCD_REAL(1E-6); \
|
||||
} while(0)
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if two given objects interest.
|
||||
*/
|
||||
CCD_EXPORT int ccdGJKIntersect(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd);
|
||||
|
||||
/**
|
||||
* This function computes separation vector of two objects. Separation
|
||||
* vector is minimal translation of obj2 to get obj1 and obj2 speparated
|
||||
* (without intersection).
|
||||
* Returns 0 if obj1 and obj2 intersect and sep is filled with translation
|
||||
* vector. If obj1 and obj2 don't intersect -1 is returned.
|
||||
* If memory allocation fails -2 is returned.
|
||||
*/
|
||||
CCD_EXPORT int ccdGJKSeparate(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_vec3_t *sep);
|
||||
|
||||
/**
|
||||
* Computes penetration of obj2 into obj1.
|
||||
* Depth of penetration, direction and position is returned. It means that
|
||||
* if obj2 is translated by distance depth in direction dir objects will
|
||||
* have touching contact, pos should be position in global coordinates
|
||||
* where force should take a place.
|
||||
*
|
||||
* CCD+EPA algorithm is used.
|
||||
*
|
||||
* Returns 0 if obj1 and obj2 intersect and depth, dir and pos are filled
|
||||
* if given non-NULL pointers.
|
||||
* If obj1 and obj2 don't intersect -1 is returned.
|
||||
* If memory allocation fails -2 is returned.
|
||||
*/
|
||||
CCD_EXPORT int ccdGJKPenetration(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_real_t *depth,
|
||||
ccd_vec3_t *dir, ccd_vec3_t *pos);
|
||||
|
||||
/**
|
||||
* Returns true if two given objects intersect - MPR algorithm is used.
|
||||
*/
|
||||
CCD_EXPORT int ccdMPRIntersect(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd);
|
||||
|
||||
/**
|
||||
* Computes penetration of obj2 into obj1.
|
||||
* Depth of penetration, direction and position is returned, i.e. if obj2
|
||||
* is translated by computed depth in resulting direction obj1 and obj2
|
||||
* would have touching contact. Position is point in global coordinates
|
||||
* where force should take a place.
|
||||
*
|
||||
* Minkowski Portal Refinement algorithm is used (MPR, a.k.a. XenoCollide,
|
||||
* see Game Programming Gem 7).
|
||||
*
|
||||
* Returns 0 if obj1 and obj2 intersect, otherwise -1 is returned.
|
||||
*/
|
||||
CCD_EXPORT int ccdMPRPenetration(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_real_t *depth,
|
||||
ccd_vec3_t *dir, ccd_vec3_t *pos);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CCD_H__ */
|
||||
@ -0,0 +1,26 @@
|
||||
#ifndef CCD_EXPORT_H
|
||||
#define CCD_EXPORT_H
|
||||
|
||||
#ifdef CCD_STATIC_DEFINE
|
||||
# define CCD_EXPORT
|
||||
#else
|
||||
# ifdef _MSC_VER
|
||||
# ifdef ccd_EXPORTS
|
||||
# define CCD_EXPORT __declspec(dllexport)
|
||||
# else /* ccd_EXPORTS */
|
||||
# define CCD_EXPORT __declspec(dllimport)
|
||||
# endif /* ccd_EXPORTS */
|
||||
# else
|
||||
# ifndef CCD_EXPORT
|
||||
# ifdef ccd_EXPORTS
|
||||
/* We are building this library */
|
||||
# define CCD_EXPORT __attribute__((visibility("default")))
|
||||
# else
|
||||
/* We are using this library */
|
||||
# define CCD_EXPORT __attribute__((visibility("default")))
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,64 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_COMPILER_H__
|
||||
#define __CCD_COMPILER_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define ccd_offsetof(TYPE, MEMBER) offsetof(TYPE, MEMBER)
|
||||
|
||||
#define ccd_container_of(ptr, type, member) \
|
||||
(type *)( (char *)ptr - ccd_offsetof(type, member))
|
||||
|
||||
/**
|
||||
* Marks inline function.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
# define _ccd_inline static inline __attribute__((always_inline))
|
||||
#else /* __GNUC__ */
|
||||
# define _ccd_inline static __inline
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
|
||||
/**
|
||||
* __prefetch(x) - prefetches the cacheline at "x" for read
|
||||
* __prefetchw(x) - prefetches the cacheline at "x" for write
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
# define _ccd_prefetch(x) __builtin_prefetch(x)
|
||||
# define _ccd_prefetchw(x) __builtin_prefetch(x,1)
|
||||
#else /* __GNUC__ */
|
||||
# define _ccd_prefetch(x) ((void)0)
|
||||
# define _ccd_prefetchw(x) ((void)0)
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
|
||||
#ifdef __ICC
|
||||
// disable unused parameter warning
|
||||
# pragma warning(disable:869)
|
||||
// disable annoying "operands are evaluated in unspecified order" warning
|
||||
# pragma warning(disable:981)
|
||||
#endif /* __ICC */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// disable unsafe function warning
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#endif /* __CCD_COMPILER_H__ */
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
#ifndef __CCD_CONFIG_H__
|
||||
#define __CCD_CONFIG_H__
|
||||
|
||||
#cmakedefine CCD_SINGLE
|
||||
#cmakedefine CCD_DOUBLE
|
||||
|
||||
#endif /* __CCD_CONFIG_H__ */
|
||||
@ -0,0 +1,7 @@
|
||||
#ifndef __CCD_CONFIG_H__
|
||||
#define __CCD_CONFIG_H__
|
||||
|
||||
ifdef(`USE_SINGLE', `#define CCD_SINGLE')
|
||||
ifdef(`USE_DOUBLE', `#define CCD_DOUBLE')
|
||||
|
||||
#endif /* __CCD_CONFIG_H__ */
|
||||
@ -0,0 +1,231 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_QUAT_H__
|
||||
#define __CCD_QUAT_H__
|
||||
|
||||
#include <ccd/compiler.h>
|
||||
#include <ccd/vec3.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
struct _ccd_quat_t {
|
||||
ccd_real_t q[4]; //!< x, y, z, w
|
||||
};
|
||||
typedef struct _ccd_quat_t ccd_quat_t;
|
||||
|
||||
#define CCD_QUAT(name, x, y, z, w) \
|
||||
ccd_quat_t name = { {x, y, z, w} }
|
||||
|
||||
_ccd_inline ccd_real_t ccdQuatLen2(const ccd_quat_t *q);
|
||||
_ccd_inline ccd_real_t ccdQuatLen(const ccd_quat_t *q);
|
||||
|
||||
_ccd_inline void ccdQuatSet(ccd_quat_t *q, ccd_real_t x, ccd_real_t y, ccd_real_t z, ccd_real_t w);
|
||||
_ccd_inline void ccdQuatCopy(ccd_quat_t *dest, const ccd_quat_t *src);
|
||||
|
||||
_ccd_inline int ccdQuatNormalize(ccd_quat_t *q);
|
||||
|
||||
_ccd_inline void ccdQuatSetAngleAxis(ccd_quat_t *q,
|
||||
ccd_real_t angle, const ccd_vec3_t *axis);
|
||||
|
||||
_ccd_inline void ccdQuatScale(ccd_quat_t *q, ccd_real_t k);
|
||||
|
||||
/**
|
||||
* q = q * q2
|
||||
*/
|
||||
_ccd_inline void ccdQuatMul(ccd_quat_t *q, const ccd_quat_t *q2);
|
||||
|
||||
/**
|
||||
* q = a * b
|
||||
*/
|
||||
_ccd_inline void ccdQuatMul2(ccd_quat_t *q,
|
||||
const ccd_quat_t *a, const ccd_quat_t *b);
|
||||
|
||||
/**
|
||||
* Inverts quaternion.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
_ccd_inline int ccdQuatInvert(ccd_quat_t *q);
|
||||
_ccd_inline int ccdQuatInvert2(ccd_quat_t *dest, const ccd_quat_t *src);
|
||||
|
||||
|
||||
/**
|
||||
* Rotate vector v by quaternion q.
|
||||
*/
|
||||
_ccd_inline void ccdQuatRotVec(ccd_vec3_t *v, const ccd_quat_t *q);
|
||||
|
||||
|
||||
/**** INLINES ****/
|
||||
_ccd_inline ccd_real_t ccdQuatLen2(const ccd_quat_t *q)
|
||||
{
|
||||
ccd_real_t len;
|
||||
|
||||
len = q->q[0] * q->q[0];
|
||||
len += q->q[1] * q->q[1];
|
||||
len += q->q[2] * q->q[2];
|
||||
len += q->q[3] * q->q[3];
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
_ccd_inline ccd_real_t ccdQuatLen(const ccd_quat_t *q)
|
||||
{
|
||||
return CCD_SQRT(ccdQuatLen2(q));
|
||||
}
|
||||
|
||||
_ccd_inline void ccdQuatSet(ccd_quat_t *q, ccd_real_t x, ccd_real_t y, ccd_real_t z, ccd_real_t w)
|
||||
{
|
||||
q->q[0] = x;
|
||||
q->q[1] = y;
|
||||
q->q[2] = z;
|
||||
q->q[3] = w;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdQuatCopy(ccd_quat_t *dest, const ccd_quat_t *src)
|
||||
{
|
||||
*dest = *src;
|
||||
}
|
||||
|
||||
|
||||
_ccd_inline int ccdQuatNormalize(ccd_quat_t *q)
|
||||
{
|
||||
ccd_real_t len = ccdQuatLen(q);
|
||||
if (len < CCD_EPS)
|
||||
return 0;
|
||||
|
||||
ccdQuatScale(q, CCD_ONE / len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdQuatSetAngleAxis(ccd_quat_t *q,
|
||||
ccd_real_t angle, const ccd_vec3_t *axis)
|
||||
{
|
||||
ccd_real_t a, x, y, z, n, s;
|
||||
|
||||
a = angle/2;
|
||||
x = ccdVec3X(axis);
|
||||
y = ccdVec3Y(axis);
|
||||
z = ccdVec3Z(axis);
|
||||
n = CCD_SQRT(x*x + y*y + z*z);
|
||||
|
||||
// axis==0? (treat this the same as angle==0 with an arbitrary axis)
|
||||
if (n < CCD_EPS){
|
||||
q->q[0] = q->q[1] = q->q[2] = CCD_ZERO;
|
||||
q->q[3] = CCD_ONE;
|
||||
}else{
|
||||
s = sin(a)/n;
|
||||
|
||||
q->q[3] = cos(a);
|
||||
q->q[0] = x*s;
|
||||
q->q[1] = y*s;
|
||||
q->q[2] = z*s;
|
||||
|
||||
ccdQuatNormalize(q);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_ccd_inline void ccdQuatScale(ccd_quat_t *q, ccd_real_t k)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < 4; i++)
|
||||
q->q[i] *= k;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdQuatMul(ccd_quat_t *q, const ccd_quat_t *q2)
|
||||
{
|
||||
ccd_quat_t a;
|
||||
ccdQuatCopy(&a, q);
|
||||
ccdQuatMul2(q, &a, q2);
|
||||
}
|
||||
|
||||
_ccd_inline void ccdQuatMul2(ccd_quat_t *q,
|
||||
const ccd_quat_t *a, const ccd_quat_t *b)
|
||||
{
|
||||
q->q[0] = a->q[3] * b->q[0]
|
||||
+ a->q[0] * b->q[3]
|
||||
+ a->q[1] * b->q[2]
|
||||
- a->q[2] * b->q[1];
|
||||
q->q[1] = a->q[3] * b->q[1]
|
||||
+ a->q[1] * b->q[3]
|
||||
- a->q[0] * b->q[2]
|
||||
+ a->q[2] * b->q[0];
|
||||
q->q[2] = a->q[3] * b->q[2]
|
||||
+ a->q[2] * b->q[3]
|
||||
+ a->q[0] * b->q[1]
|
||||
- a->q[1] * b->q[0];
|
||||
q->q[3] = a->q[3] * b->q[3]
|
||||
- a->q[0] * b->q[0]
|
||||
- a->q[1] * b->q[1]
|
||||
- a->q[2] * b->q[2];
|
||||
}
|
||||
|
||||
_ccd_inline int ccdQuatInvert(ccd_quat_t *q)
|
||||
{
|
||||
ccd_real_t len2 = ccdQuatLen2(q);
|
||||
if (len2 < CCD_EPS)
|
||||
return -1;
|
||||
|
||||
len2 = CCD_ONE / len2;
|
||||
|
||||
q->q[0] = -q->q[0] * len2;
|
||||
q->q[1] = -q->q[1] * len2;
|
||||
q->q[2] = -q->q[2] * len2;
|
||||
q->q[3] = q->q[3] * len2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
_ccd_inline int ccdQuatInvert2(ccd_quat_t *dest, const ccd_quat_t *src)
|
||||
{
|
||||
ccdQuatCopy(dest, src);
|
||||
return ccdQuatInvert(dest);
|
||||
}
|
||||
|
||||
_ccd_inline void ccdQuatRotVec(ccd_vec3_t *v, const ccd_quat_t *q)
|
||||
{
|
||||
// original version: 31 mul + 21 add
|
||||
// optimized version: 18 mul + 12 add
|
||||
// formula: v = v + 2 * cross(q.xyz, cross(q.xyz, v) + q.w * v)
|
||||
ccd_real_t cross1_x, cross1_y, cross1_z, cross2_x, cross2_y, cross2_z;
|
||||
ccd_real_t x, y, z, w;
|
||||
ccd_real_t vx, vy, vz;
|
||||
|
||||
vx = ccdVec3X(v);
|
||||
vy = ccdVec3Y(v);
|
||||
vz = ccdVec3Z(v);
|
||||
|
||||
w = q->q[3];
|
||||
x = q->q[0];
|
||||
y = q->q[1];
|
||||
z = q->q[2];
|
||||
|
||||
cross1_x = y * vz - z * vy + w * vx;
|
||||
cross1_y = z * vx - x * vz + w * vy;
|
||||
cross1_z = x * vy - y * vx + w * vz;
|
||||
cross2_x = y * cross1_z - z * cross1_y;
|
||||
cross2_y = z * cross1_x - x * cross1_z;
|
||||
cross2_z = x * cross1_y - y * cross1_x;
|
||||
ccdVec3Set(v, vx + 2 * cross2_x, vy + 2 * cross2_y, vz + 2 * cross2_z);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CCD_QUAT_H__ */
|
||||
@ -0,0 +1,340 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010-2013 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_VEC3_H__
|
||||
#define __CCD_VEC3_H__
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <stdlib.h>
|
||||
#include <ccd/compiler.h>
|
||||
#include <ccd/config.h>
|
||||
#include <ccd/ccd_export.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#ifndef CCD_SINGLE
|
||||
# ifndef CCD_DOUBLE
|
||||
# error You must define CCD_SINGLE or CCD_DOUBLE
|
||||
# endif /* CCD_DOUBLE */
|
||||
#endif /* CCD_SINGLE */
|
||||
|
||||
#ifdef WIN32
|
||||
# define CCD_FMIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#endif /* WIN32 */
|
||||
|
||||
#ifdef CCD_SINGLE
|
||||
# ifdef CCD_DOUBLE
|
||||
# error You can define either CCD_SINGLE or CCD_DOUBLE, not both!
|
||||
# endif /* CCD_DOUBLE */
|
||||
|
||||
typedef float ccd_real_t;
|
||||
|
||||
//# define CCD_EPS 1E-6
|
||||
# define CCD_EPS FLT_EPSILON
|
||||
|
||||
# define CCD_REAL_MAX FLT_MAX
|
||||
|
||||
# define CCD_REAL(x) (x ## f) /*!< form a constant */
|
||||
# define CCD_SQRT(x) (sqrtf(x)) /*!< square root */
|
||||
# define CCD_FABS(x) (fabsf(x)) /*!< absolute value */
|
||||
# define CCD_FMAX(x, y) (fmaxf((x), (y))) /*!< maximum of two floats */
|
||||
|
||||
# ifndef CCD_FMIN
|
||||
# define CCD_FMIN(x, y) (fminf((x), (y))) /*!< minimum of two floats */
|
||||
# endif /* CCD_FMIN */
|
||||
|
||||
#endif /* CCD_SINGLE */
|
||||
|
||||
#ifdef CCD_DOUBLE
|
||||
typedef double ccd_real_t;
|
||||
|
||||
//# define CCD_EPS 1E-10
|
||||
# define CCD_EPS DBL_EPSILON
|
||||
|
||||
# define CCD_REAL_MAX DBL_MAX
|
||||
|
||||
# define CCD_REAL(x) (x) /*!< form a constant */
|
||||
# define CCD_SQRT(x) (sqrt(x)) /*!< square root */
|
||||
# define CCD_FABS(x) (fabs(x)) /*!< absolute value */
|
||||
# define CCD_FMAX(x, y) (fmax((x), (y))) /*!< maximum of two floats */
|
||||
|
||||
# ifndef CCD_FMIN
|
||||
# define CCD_FMIN(x, y) (fmin((x), (y))) /*!< minimum of two floats */
|
||||
# endif /* CCD_FMIN */
|
||||
|
||||
#endif /* CCD_DOUBLE */
|
||||
|
||||
#define CCD_ONE CCD_REAL(1.)
|
||||
#define CCD_ZERO CCD_REAL(0.)
|
||||
|
||||
struct _ccd_vec3_t {
|
||||
ccd_real_t v[3];
|
||||
};
|
||||
typedef struct _ccd_vec3_t ccd_vec3_t;
|
||||
|
||||
|
||||
/**
|
||||
* Holds origin (0,0,0) - this variable is meant to be read-only!
|
||||
*/
|
||||
CCD_EXPORT extern ccd_vec3_t *ccd_vec3_origin;
|
||||
|
||||
/**
|
||||
* Array of points uniformly distributed on unit sphere.
|
||||
*/
|
||||
CCD_EXPORT extern ccd_vec3_t *ccd_points_on_sphere;
|
||||
CCD_EXPORT extern size_t ccd_points_on_sphere_len;
|
||||
|
||||
/** Returns sign of value. */
|
||||
_ccd_inline int ccdSign(ccd_real_t val);
|
||||
/** Returns true if val is zero. **/
|
||||
_ccd_inline int ccdIsZero(ccd_real_t val);
|
||||
/** Returns true if a and b equal. **/
|
||||
_ccd_inline int ccdEq(ccd_real_t a, ccd_real_t b);
|
||||
|
||||
|
||||
#define CCD_VEC3_STATIC(x, y, z) \
|
||||
{ { (x), (y), (z) } }
|
||||
|
||||
#define CCD_VEC3(name, x, y, z) \
|
||||
ccd_vec3_t name = CCD_VEC3_STATIC((x), (y), (z))
|
||||
|
||||
_ccd_inline ccd_real_t ccdVec3X(const ccd_vec3_t *v);
|
||||
_ccd_inline ccd_real_t ccdVec3Y(const ccd_vec3_t *v);
|
||||
_ccd_inline ccd_real_t ccdVec3Z(const ccd_vec3_t *v);
|
||||
|
||||
/**
|
||||
* Returns true if a and b equal.
|
||||
*/
|
||||
_ccd_inline int ccdVec3Eq(const ccd_vec3_t *a, const ccd_vec3_t *b);
|
||||
|
||||
/**
|
||||
* Returns squared length of vector.
|
||||
*/
|
||||
_ccd_inline ccd_real_t ccdVec3Len2(const ccd_vec3_t *v);
|
||||
|
||||
/**
|
||||
* Returns distance between a and b.
|
||||
*/
|
||||
_ccd_inline ccd_real_t ccdVec3Dist2(const ccd_vec3_t *a, const ccd_vec3_t *b);
|
||||
|
||||
|
||||
_ccd_inline void ccdVec3Set(ccd_vec3_t *v, ccd_real_t x, ccd_real_t y, ccd_real_t z);
|
||||
|
||||
/**
|
||||
* v = w
|
||||
*/
|
||||
_ccd_inline void ccdVec3Copy(ccd_vec3_t *v, const ccd_vec3_t *w);
|
||||
|
||||
/**
|
||||
* Substracts coordinates of vector w from vector v. v = v - w
|
||||
*/
|
||||
_ccd_inline void ccdVec3Sub(ccd_vec3_t *v, const ccd_vec3_t *w);
|
||||
|
||||
/**
|
||||
* Adds coordinates of vector w to vector v. v = v + w
|
||||
*/
|
||||
_ccd_inline void ccdVec3Add(ccd_vec3_t *v, const ccd_vec3_t *w);
|
||||
|
||||
/**
|
||||
* d = v - w
|
||||
*/
|
||||
_ccd_inline void ccdVec3Sub2(ccd_vec3_t *d, const ccd_vec3_t *v, const ccd_vec3_t *w);
|
||||
|
||||
/**
|
||||
* d = d * k;
|
||||
*/
|
||||
_ccd_inline void ccdVec3Scale(ccd_vec3_t *d, ccd_real_t k);
|
||||
|
||||
/**
|
||||
* Normalizes given vector to unit length.
|
||||
*/
|
||||
_ccd_inline void ccdVec3Normalize(ccd_vec3_t *d);
|
||||
|
||||
|
||||
/**
|
||||
* Dot product of two vectors.
|
||||
*/
|
||||
_ccd_inline ccd_real_t ccdVec3Dot(const ccd_vec3_t *a, const ccd_vec3_t *b);
|
||||
|
||||
/**
|
||||
* Cross product: d = a x b.
|
||||
*/
|
||||
_ccd_inline void ccdVec3Cross(ccd_vec3_t *d, const ccd_vec3_t *a, const ccd_vec3_t *b);
|
||||
|
||||
|
||||
/**
|
||||
* Returns distance^2 of point P to segment ab.
|
||||
* If witness is non-NULL it is filled with coordinates of point from which
|
||||
* was computed distance to point P.
|
||||
*/
|
||||
CCD_EXPORT ccd_real_t ccdVec3PointSegmentDist2(const ccd_vec3_t *P,
|
||||
const ccd_vec3_t *a,
|
||||
const ccd_vec3_t *b,
|
||||
ccd_vec3_t *witness);
|
||||
|
||||
/**
|
||||
* Returns distance^2 of point P from triangle formed by triplet a, b, c.
|
||||
* If witness vector is provided it is filled with coordinates of point
|
||||
* from which was computed distance to point P.
|
||||
*/
|
||||
CCD_EXPORT ccd_real_t ccdVec3PointTriDist2(const ccd_vec3_t *P,
|
||||
const ccd_vec3_t *a,
|
||||
const ccd_vec3_t *b,
|
||||
const ccd_vec3_t *c,
|
||||
ccd_vec3_t *witness);
|
||||
|
||||
|
||||
/**** INLINES ****/
|
||||
_ccd_inline int ccdSign(ccd_real_t val)
|
||||
{
|
||||
if (ccdIsZero(val)){
|
||||
return 0;
|
||||
}else if (val < CCD_ZERO){
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
_ccd_inline int ccdIsZero(ccd_real_t val)
|
||||
{
|
||||
return CCD_FABS(val) < CCD_EPS;
|
||||
}
|
||||
|
||||
_ccd_inline int ccdEq(ccd_real_t _a, ccd_real_t _b)
|
||||
{
|
||||
ccd_real_t ab;
|
||||
ccd_real_t a, b;
|
||||
|
||||
ab = CCD_FABS(_a - _b);
|
||||
if (CCD_FABS(ab) < CCD_EPS)
|
||||
return 1;
|
||||
|
||||
a = CCD_FABS(_a);
|
||||
b = CCD_FABS(_b);
|
||||
if (b > a){
|
||||
return ab < CCD_EPS * b;
|
||||
}else{
|
||||
return ab < CCD_EPS * a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_ccd_inline ccd_real_t ccdVec3X(const ccd_vec3_t *v)
|
||||
{
|
||||
return v->v[0];
|
||||
}
|
||||
|
||||
_ccd_inline ccd_real_t ccdVec3Y(const ccd_vec3_t *v)
|
||||
{
|
||||
return v->v[1];
|
||||
}
|
||||
|
||||
_ccd_inline ccd_real_t ccdVec3Z(const ccd_vec3_t *v)
|
||||
{
|
||||
return v->v[2];
|
||||
}
|
||||
|
||||
_ccd_inline int ccdVec3Eq(const ccd_vec3_t *a, const ccd_vec3_t *b)
|
||||
{
|
||||
return ccdEq(ccdVec3X(a), ccdVec3X(b))
|
||||
&& ccdEq(ccdVec3Y(a), ccdVec3Y(b))
|
||||
&& ccdEq(ccdVec3Z(a), ccdVec3Z(b));
|
||||
}
|
||||
|
||||
_ccd_inline ccd_real_t ccdVec3Len2(const ccd_vec3_t *v)
|
||||
{
|
||||
return ccdVec3Dot(v, v);
|
||||
}
|
||||
|
||||
_ccd_inline ccd_real_t ccdVec3Dist2(const ccd_vec3_t *a, const ccd_vec3_t *b)
|
||||
{
|
||||
ccd_vec3_t ab;
|
||||
ccdVec3Sub2(&ab, a, b);
|
||||
return ccdVec3Len2(&ab);
|
||||
}
|
||||
|
||||
_ccd_inline void ccdVec3Set(ccd_vec3_t *v, ccd_real_t x, ccd_real_t y, ccd_real_t z)
|
||||
{
|
||||
v->v[0] = x;
|
||||
v->v[1] = y;
|
||||
v->v[2] = z;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdVec3Copy(ccd_vec3_t *v, const ccd_vec3_t *w)
|
||||
{
|
||||
*v = *w;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdVec3Sub(ccd_vec3_t *v, const ccd_vec3_t *w)
|
||||
{
|
||||
v->v[0] -= w->v[0];
|
||||
v->v[1] -= w->v[1];
|
||||
v->v[2] -= w->v[2];
|
||||
}
|
||||
_ccd_inline void ccdVec3Sub2(ccd_vec3_t *d, const ccd_vec3_t *v, const ccd_vec3_t *w)
|
||||
{
|
||||
d->v[0] = v->v[0] - w->v[0];
|
||||
d->v[1] = v->v[1] - w->v[1];
|
||||
d->v[2] = v->v[2] - w->v[2];
|
||||
}
|
||||
|
||||
_ccd_inline void ccdVec3Add(ccd_vec3_t *v, const ccd_vec3_t *w)
|
||||
{
|
||||
v->v[0] += w->v[0];
|
||||
v->v[1] += w->v[1];
|
||||
v->v[2] += w->v[2];
|
||||
}
|
||||
|
||||
_ccd_inline void ccdVec3Scale(ccd_vec3_t *d, ccd_real_t k)
|
||||
{
|
||||
d->v[0] *= k;
|
||||
d->v[1] *= k;
|
||||
d->v[2] *= k;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdVec3Normalize(ccd_vec3_t *d)
|
||||
{
|
||||
ccd_real_t k = CCD_ONE / CCD_SQRT(ccdVec3Len2(d));
|
||||
ccdVec3Scale(d, k);
|
||||
}
|
||||
|
||||
_ccd_inline ccd_real_t ccdVec3Dot(const ccd_vec3_t *a, const ccd_vec3_t *b)
|
||||
{
|
||||
ccd_real_t dot;
|
||||
|
||||
dot = a->v[0] * b->v[0];
|
||||
dot += a->v[1] * b->v[1];
|
||||
dot += a->v[2] * b->v[2];
|
||||
return dot;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdVec3Cross(ccd_vec3_t *d, const ccd_vec3_t *a, const ccd_vec3_t *b)
|
||||
{
|
||||
d->v[0] = (a->v[1] * b->v[2]) - (a->v[2] * b->v[1]);
|
||||
d->v[1] = (a->v[2] * b->v[0]) - (a->v[0] * b->v[2]);
|
||||
d->v[2] = (a->v[0] * b->v[1]) - (a->v[1] * b->v[0]);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CCD_VEC3_H__ */
|
||||
@ -0,0 +1,65 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_DBG_H__
|
||||
#define __CCD_DBG_H__
|
||||
|
||||
/**
|
||||
* Some macros which can be used for printing debug info to stderr if macro
|
||||
* NDEBUG not defined.
|
||||
*
|
||||
* DBG_PROLOGUE can be specified as string and this string will be
|
||||
* prepended to output text
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef DBG_PROLOGUE
|
||||
# define DBG_PROLOGUE
|
||||
#endif
|
||||
|
||||
# define DBG(format, ...) do { \
|
||||
fprintf(stderr, DBG_PROLOGUE "%s :: " format "\n", __func__, ## __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
|
||||
# define DBG2(str) do { \
|
||||
fprintf(stderr, DBG_PROLOGUE "%s :: " str "\n", __func__); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
|
||||
# define DBG_VEC3(vec, prefix) do {\
|
||||
fprintf(stderr, DBG_PROLOGUE "%s :: %s[%lf %lf %lf]\n", \
|
||||
__func__, prefix, ccdVec3X(vec), ccdVec3Y(vec), ccdVec3Z(vec)); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
/*
|
||||
# define DBG_VEC3(vec, prefix) do {\
|
||||
fprintf(stderr, DBG_PROLOGUE "%s :: %s[%.20lf %.20lf %.20lf]\n", \
|
||||
__func__, prefix, ccdVec3X(vec), ccdVec3Y(vec), ccdVec3Z(vec)); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
*/
|
||||
|
||||
#else
|
||||
# define DBG(format, ...)
|
||||
# define DBG2(str)
|
||||
# define DBG_VEC3(v, prefix)
|
||||
#endif
|
||||
|
||||
#endif /* __CCD_DBG_H__ */
|
||||
@ -0,0 +1,155 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_LIST_H__
|
||||
#define __CCD_LIST_H__
|
||||
|
||||
#include <string.h>
|
||||
#include <ccd/compiler.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
struct _ccd_list_t {
|
||||
struct _ccd_list_t *next, *prev;
|
||||
};
|
||||
typedef struct _ccd_list_t ccd_list_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the struct for this entry.
|
||||
* @ptr: the &ccd_list_t pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define ccdListEntry(ptr, type, member) \
|
||||
ccd_container_of(ptr, type, member)
|
||||
|
||||
/**
|
||||
* Iterates over list.
|
||||
*/
|
||||
#define ccdListForEach(list, item) \
|
||||
for (item = (list)->next; \
|
||||
_ccd_prefetch((item)->next), item != (list); \
|
||||
item = (item)->next)
|
||||
|
||||
/**
|
||||
* Iterates over list safe against remove of list entry
|
||||
*/
|
||||
#define ccdListForEachSafe(list, item, tmp) \
|
||||
for (item = (list)->next, tmp = (item)->next; \
|
||||
item != (list); \
|
||||
item = tmp, tmp = (item)->next)
|
||||
|
||||
/**
|
||||
* Iterates over list of given type.
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define ccdListForEachEntry(head, pos, postype, member) \
|
||||
for (pos = ccdListEntry((head)->next, postype, member); \
|
||||
_ccd_prefetch(pos->member.next), &pos->member != (head); \
|
||||
pos = ccdListEntry(pos->member.next, postype, member))
|
||||
|
||||
/**
|
||||
* Iterates over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define ccdListForEachEntrySafe(head, pos, postype, n, ntype, member) \
|
||||
for (pos = ccdListEntry((head)->next, postype, member), \
|
||||
n = ccdListEntry(pos->member.next, postype, member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = ccdListEntry(n->member.next, ntype, member))
|
||||
|
||||
|
||||
/**
|
||||
* Initialize list.
|
||||
*/
|
||||
_ccd_inline void ccdListInit(ccd_list_t *l);
|
||||
|
||||
_ccd_inline ccd_list_t *ccdListNext(ccd_list_t *l);
|
||||
_ccd_inline ccd_list_t *ccdListPrev(ccd_list_t *l);
|
||||
|
||||
/**
|
||||
* Returns true if list is empty.
|
||||
*/
|
||||
_ccd_inline int ccdListEmpty(const ccd_list_t *head);
|
||||
|
||||
/**
|
||||
* Appends item to end of the list l.
|
||||
*/
|
||||
_ccd_inline void ccdListAppend(ccd_list_t *l, ccd_list_t *item);
|
||||
|
||||
/**
|
||||
* Removes item from list.
|
||||
*/
|
||||
_ccd_inline void ccdListDel(ccd_list_t *item);
|
||||
|
||||
|
||||
|
||||
///
|
||||
/// INLINES:
|
||||
///
|
||||
|
||||
_ccd_inline void ccdListInit(ccd_list_t *l)
|
||||
{
|
||||
l->next = l;
|
||||
l->prev = l;
|
||||
}
|
||||
|
||||
_ccd_inline ccd_list_t *ccdListNext(ccd_list_t *l)
|
||||
{
|
||||
return l->next;
|
||||
}
|
||||
|
||||
_ccd_inline ccd_list_t *ccdListPrev(ccd_list_t *l)
|
||||
{
|
||||
return l->prev;
|
||||
}
|
||||
|
||||
_ccd_inline int ccdListEmpty(const ccd_list_t *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdListAppend(ccd_list_t *l, ccd_list_t *new)
|
||||
{
|
||||
new->prev = l->prev;
|
||||
new->next = l;
|
||||
l->prev->next = new;
|
||||
l->prev = new;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdListDel(ccd_list_t *item)
|
||||
{
|
||||
item->next->prev = item->prev;
|
||||
item->prev->next = item->next;
|
||||
item->next = item;
|
||||
item->prev = item;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CCD_LIST_H__ */
|
||||
@ -0,0 +1,543 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010,2011 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ccd/ccd.h>
|
||||
#include "simplex.h"
|
||||
#include "dbg.h"
|
||||
|
||||
/** Finds origin (center) of Minkowski difference (actually it can be any
|
||||
* interior point of Minkowski difference. */
|
||||
_ccd_inline void findOrigin(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_support_t *center);
|
||||
|
||||
/** Discovers initial portal - that is tetrahedron that intersects with
|
||||
* origin ray (ray from center of Minkowski diff to (0,0,0).
|
||||
*
|
||||
* Returns -1 if already recognized that origin is outside Minkowski
|
||||
* portal.
|
||||
* Returns 1 if origin lies on v1 of simplex (only v0 and v1 are present
|
||||
* in simplex).
|
||||
* Returns 2 if origin lies on v0-v1 segment.
|
||||
* Returns 0 if portal was built.
|
||||
*/
|
||||
static int discoverPortal(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_simplex_t *portal);
|
||||
|
||||
|
||||
/** Expands portal towards origin and determine if objects intersect.
|
||||
* Already established portal must be given as argument.
|
||||
* If intersection is found 0 is returned, -1 otherwise */
|
||||
static int refinePortal(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_simplex_t *portal);
|
||||
|
||||
/** Finds penetration info by expanding provided portal. */
|
||||
static void findPenetr(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_simplex_t *portal,
|
||||
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos);
|
||||
|
||||
/** Finds penetration info if origin lies on portal's v1 */
|
||||
static void findPenetrTouch(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_simplex_t *portal,
|
||||
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos);
|
||||
|
||||
/** Find penetration info if origin lies on portal's segment v0-v1 */
|
||||
static void findPenetrSegment(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_simplex_t *portal,
|
||||
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos);
|
||||
|
||||
/** Finds position vector from fully established portal */
|
||||
static void findPos(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
const ccd_simplex_t *portal, ccd_vec3_t *pos);
|
||||
|
||||
/** Extends portal with new support point.
|
||||
* Portal must have face v1-v2-v3 arranged to face outside portal. */
|
||||
_ccd_inline void expandPortal(ccd_simplex_t *portal,
|
||||
const ccd_support_t *v4);
|
||||
|
||||
/** Fill dir with direction outside portal. Portal's v1-v2-v3 face must be
|
||||
* arranged in correct order! */
|
||||
_ccd_inline void portalDir(const ccd_simplex_t *portal, ccd_vec3_t *dir);
|
||||
|
||||
/** Returns true if portal encapsules origin (0,0,0), dir is direction of
|
||||
* v1-v2-v3 face. */
|
||||
_ccd_inline int portalEncapsulesOrigin(const ccd_simplex_t *portal,
|
||||
const ccd_vec3_t *dir);
|
||||
|
||||
/** Returns true if portal with new point v4 would reach specified
|
||||
* tolerance (i.e. returns true if portal can _not_ significantly expand
|
||||
* within Minkowski difference).
|
||||
*
|
||||
* v4 is candidate for new point in portal, dir is direction in which v4
|
||||
* was obtained. */
|
||||
_ccd_inline int portalReachTolerance(const ccd_simplex_t *portal,
|
||||
const ccd_support_t *v4,
|
||||
const ccd_vec3_t *dir,
|
||||
const ccd_t *ccd);
|
||||
|
||||
/** Returns true if portal expanded by new point v4 could possibly contain
|
||||
* origin, dir is direction in which v4 was obtained. */
|
||||
_ccd_inline int portalCanEncapsuleOrigin(const ccd_simplex_t *portal,
|
||||
const ccd_support_t *v4,
|
||||
const ccd_vec3_t *dir);
|
||||
|
||||
|
||||
int ccdMPRIntersect(const void *obj1, const void *obj2, const ccd_t *ccd)
|
||||
{
|
||||
ccd_simplex_t portal;
|
||||
int res;
|
||||
|
||||
// Phase 1: Portal discovery - find portal that intersects with origin
|
||||
// ray (ray from center of Minkowski diff to origin of coordinates)
|
||||
res = discoverPortal(obj1, obj2, ccd, &portal);
|
||||
if (res < 0)
|
||||
return 0;
|
||||
if (res > 0)
|
||||
return 1;
|
||||
|
||||
// Phase 2: Portal refinement
|
||||
res = refinePortal(obj1, obj2, ccd, &portal);
|
||||
return (res == 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
int ccdMPRPenetration(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos)
|
||||
{
|
||||
ccd_simplex_t portal;
|
||||
int res;
|
||||
|
||||
// Phase 1: Portal discovery
|
||||
res = discoverPortal(obj1, obj2, ccd, &portal);
|
||||
if (res < 0){
|
||||
// Origin isn't inside portal - no collision.
|
||||
return -1;
|
||||
|
||||
}else if (res == 1){
|
||||
// Touching contact on portal's v1.
|
||||
findPenetrTouch(obj1, obj2, ccd, &portal, depth, dir, pos);
|
||||
|
||||
}else if (res == 2){
|
||||
// Origin lies on v0-v1 segment.
|
||||
findPenetrSegment(obj1, obj2, ccd, &portal, depth, dir, pos);
|
||||
|
||||
}else if (res == 0){
|
||||
// Phase 2: Portal refinement
|
||||
res = refinePortal(obj1, obj2, ccd, &portal);
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
// Phase 3. Penetration info
|
||||
findPenetr(obj1, obj2, ccd, &portal, depth, dir, pos);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
_ccd_inline void findOrigin(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_support_t *center)
|
||||
{
|
||||
ccd->center1(obj1, ¢er->v1);
|
||||
ccd->center2(obj2, ¢er->v2);
|
||||
ccdVec3Sub2(¢er->v, ¢er->v1, ¢er->v2);
|
||||
}
|
||||
|
||||
static int discoverPortal(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_simplex_t *portal)
|
||||
{
|
||||
ccd_vec3_t dir, va, vb;
|
||||
ccd_real_t dot;
|
||||
int cont;
|
||||
|
||||
// vertex 0 is center of portal
|
||||
findOrigin(obj1, obj2, ccd, ccdSimplexPointW(portal, 0));
|
||||
ccdSimplexSetSize(portal, 1);
|
||||
|
||||
if (ccdVec3Eq(&ccdSimplexPoint(portal, 0)->v, ccd_vec3_origin)){
|
||||
// Portal's center lies on origin (0,0,0) => we know that objects
|
||||
// intersect but we would need to know penetration info.
|
||||
// So move center little bit...
|
||||
ccdVec3Set(&va, CCD_EPS * CCD_REAL(10.), CCD_ZERO, CCD_ZERO);
|
||||
ccdVec3Add(&ccdSimplexPointW(portal, 0)->v, &va);
|
||||
}
|
||||
|
||||
|
||||
// vertex 1 = support in direction of origin
|
||||
ccdVec3Copy(&dir, &ccdSimplexPoint(portal, 0)->v);
|
||||
ccdVec3Scale(&dir, CCD_REAL(-1.));
|
||||
ccdVec3Normalize(&dir);
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, ccdSimplexPointW(portal, 1));
|
||||
ccdSimplexSetSize(portal, 2);
|
||||
|
||||
// test if origin isn't outside of v1
|
||||
dot = ccdVec3Dot(&ccdSimplexPoint(portal, 1)->v, &dir);
|
||||
if (ccdIsZero(dot) || dot < CCD_ZERO)
|
||||
return -1;
|
||||
|
||||
|
||||
// vertex 2
|
||||
ccdVec3Cross(&dir, &ccdSimplexPoint(portal, 0)->v,
|
||||
&ccdSimplexPoint(portal, 1)->v);
|
||||
if (ccdIsZero(ccdVec3Len2(&dir))){
|
||||
if (ccdVec3Eq(&ccdSimplexPoint(portal, 1)->v, ccd_vec3_origin)){
|
||||
// origin lies on v1
|
||||
return 1;
|
||||
}else{
|
||||
// origin lies on v0-v1 segment
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
ccdVec3Normalize(&dir);
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, ccdSimplexPointW(portal, 2));
|
||||
dot = ccdVec3Dot(&ccdSimplexPoint(portal, 2)->v, &dir);
|
||||
if (ccdIsZero(dot) || dot < CCD_ZERO)
|
||||
return -1;
|
||||
|
||||
ccdSimplexSetSize(portal, 3);
|
||||
|
||||
// vertex 3 direction
|
||||
ccdVec3Sub2(&va, &ccdSimplexPoint(portal, 1)->v,
|
||||
&ccdSimplexPoint(portal, 0)->v);
|
||||
ccdVec3Sub2(&vb, &ccdSimplexPoint(portal, 2)->v,
|
||||
&ccdSimplexPoint(portal, 0)->v);
|
||||
ccdVec3Cross(&dir, &va, &vb);
|
||||
ccdVec3Normalize(&dir);
|
||||
|
||||
// it is better to form portal faces to be oriented "outside" origin
|
||||
dot = ccdVec3Dot(&dir, &ccdSimplexPoint(portal, 0)->v);
|
||||
if (dot > CCD_ZERO){
|
||||
ccdSimplexSwap(portal, 1, 2);
|
||||
ccdVec3Scale(&dir, CCD_REAL(-1.));
|
||||
}
|
||||
|
||||
while (ccdSimplexSize(portal) < 4){
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, ccdSimplexPointW(portal, 3));
|
||||
dot = ccdVec3Dot(&ccdSimplexPoint(portal, 3)->v, &dir);
|
||||
if (ccdIsZero(dot) || dot < CCD_ZERO)
|
||||
return -1;
|
||||
|
||||
cont = 0;
|
||||
|
||||
// test if origin is outside (v1, v0, v3) - set v2 as v3 and
|
||||
// continue
|
||||
ccdVec3Cross(&va, &ccdSimplexPoint(portal, 1)->v,
|
||||
&ccdSimplexPoint(portal, 3)->v);
|
||||
dot = ccdVec3Dot(&va, &ccdSimplexPoint(portal, 0)->v);
|
||||
if (dot < CCD_ZERO && !ccdIsZero(dot)){
|
||||
ccdSimplexSet(portal, 2, ccdSimplexPoint(portal, 3));
|
||||
cont = 1;
|
||||
}
|
||||
|
||||
if (!cont){
|
||||
// test if origin is outside (v3, v0, v2) - set v1 as v3 and
|
||||
// continue
|
||||
ccdVec3Cross(&va, &ccdSimplexPoint(portal, 3)->v,
|
||||
&ccdSimplexPoint(portal, 2)->v);
|
||||
dot = ccdVec3Dot(&va, &ccdSimplexPoint(portal, 0)->v);
|
||||
if (dot < CCD_ZERO && !ccdIsZero(dot)){
|
||||
ccdSimplexSet(portal, 1, ccdSimplexPoint(portal, 3));
|
||||
cont = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cont){
|
||||
ccdVec3Sub2(&va, &ccdSimplexPoint(portal, 1)->v,
|
||||
&ccdSimplexPoint(portal, 0)->v);
|
||||
ccdVec3Sub2(&vb, &ccdSimplexPoint(portal, 2)->v,
|
||||
&ccdSimplexPoint(portal, 0)->v);
|
||||
ccdVec3Cross(&dir, &va, &vb);
|
||||
ccdVec3Normalize(&dir);
|
||||
}else{
|
||||
ccdSimplexSetSize(portal, 4);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int refinePortal(const void *obj1, const void *obj2,
|
||||
const ccd_t *ccd, ccd_simplex_t *portal)
|
||||
{
|
||||
ccd_vec3_t dir;
|
||||
ccd_support_t v4;
|
||||
|
||||
while (1){
|
||||
// compute direction outside the portal (from v0 throught v1,v2,v3
|
||||
// face)
|
||||
portalDir(portal, &dir);
|
||||
|
||||
// test if origin is inside the portal
|
||||
if (portalEncapsulesOrigin(portal, &dir))
|
||||
return 0;
|
||||
|
||||
// get next support point
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &v4);
|
||||
|
||||
// test if v4 can expand portal to contain origin and if portal
|
||||
// expanding doesn't reach given tolerance
|
||||
if (!portalCanEncapsuleOrigin(portal, &v4, &dir)
|
||||
|| portalReachTolerance(portal, &v4, &dir, ccd)){
|
||||
return -1;
|
||||
}
|
||||
|
||||
// v1-v2-v3 triangle must be rearranged to face outside Minkowski
|
||||
// difference (direction from v0).
|
||||
expandPortal(portal, &v4);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void findPenetr(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_simplex_t *portal,
|
||||
ccd_real_t *depth, ccd_vec3_t *pdir, ccd_vec3_t *pos)
|
||||
{
|
||||
ccd_vec3_t dir;
|
||||
ccd_support_t v4;
|
||||
unsigned long iterations;
|
||||
|
||||
iterations = 0UL;
|
||||
while (1){
|
||||
// compute portal direction and obtain next support point
|
||||
portalDir(portal, &dir);
|
||||
__ccdSupport(obj1, obj2, &dir, ccd, &v4);
|
||||
|
||||
// reached tolerance -> find penetration info
|
||||
if (portalReachTolerance(portal, &v4, &dir, ccd)
|
||||
|| iterations > ccd->max_iterations){
|
||||
*depth = ccdVec3PointTriDist2(ccd_vec3_origin,
|
||||
&ccdSimplexPoint(portal, 1)->v,
|
||||
&ccdSimplexPoint(portal, 2)->v,
|
||||
&ccdSimplexPoint(portal, 3)->v,
|
||||
pdir);
|
||||
*depth = CCD_SQRT(*depth);
|
||||
if (ccdIsZero(*depth)){
|
||||
// If depth is zero, then we have a touching contact.
|
||||
// So following findPenetrTouch(), we assign zero to
|
||||
// the direction vector (it can actually be anything
|
||||
// according to the decription of ccdMPRPenetration
|
||||
// function).
|
||||
ccdVec3Copy(pdir, ccd_vec3_origin);
|
||||
}else{
|
||||
ccdVec3Normalize(pdir);
|
||||
}
|
||||
|
||||
// barycentric coordinates:
|
||||
findPos(obj1, obj2, ccd, portal, pos);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
expandPortal(portal, &v4);
|
||||
|
||||
iterations++;
|
||||
}
|
||||
}
|
||||
|
||||
static void findPenetrTouch(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_simplex_t *portal,
|
||||
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos)
|
||||
{
|
||||
// Touching contact on portal's v1 - so depth is zero and direction
|
||||
// is unimportant and pos can be guessed
|
||||
*depth = CCD_REAL(0.);
|
||||
ccdVec3Copy(dir, ccd_vec3_origin);
|
||||
|
||||
ccdVec3Copy(pos, &ccdSimplexPoint(portal, 1)->v1);
|
||||
ccdVec3Add(pos, &ccdSimplexPoint(portal, 1)->v2);
|
||||
ccdVec3Scale(pos, 0.5);
|
||||
}
|
||||
|
||||
static void findPenetrSegment(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
ccd_simplex_t *portal,
|
||||
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos)
|
||||
{
|
||||
/*
|
||||
ccd_vec3_t vec;
|
||||
ccd_real_t k;
|
||||
*/
|
||||
|
||||
// Origin lies on v0-v1 segment.
|
||||
// Depth is distance to v1, direction also and position must be
|
||||
// computed
|
||||
|
||||
ccdVec3Copy(pos, &ccdSimplexPoint(portal, 1)->v1);
|
||||
ccdVec3Add(pos, &ccdSimplexPoint(portal, 1)->v2);
|
||||
ccdVec3Scale(pos, CCD_REAL(0.5));
|
||||
|
||||
/*
|
||||
ccdVec3Sub2(&vec, &ccdSimplexPoint(portal, 1)->v,
|
||||
&ccdSimplexPoint(portal, 0)->v);
|
||||
k = CCD_SQRT(ccdVec3Len2(&ccdSimplexPoint(portal, 0)->v));
|
||||
k /= CCD_SQRT(ccdVec3Len2(&vec));
|
||||
ccdVec3Scale(&vec, -k);
|
||||
ccdVec3Add(pos, &vec);
|
||||
*/
|
||||
|
||||
ccdVec3Copy(dir, &ccdSimplexPoint(portal, 1)->v);
|
||||
*depth = CCD_SQRT(ccdVec3Len2(dir));
|
||||
ccdVec3Normalize(dir);
|
||||
}
|
||||
|
||||
|
||||
static void findPos(const void *obj1, const void *obj2, const ccd_t *ccd,
|
||||
const ccd_simplex_t *portal, ccd_vec3_t *pos)
|
||||
{
|
||||
ccd_vec3_t dir;
|
||||
size_t i;
|
||||
ccd_real_t b[4], sum, inv;
|
||||
ccd_vec3_t vec, p1, p2;
|
||||
|
||||
portalDir(portal, &dir);
|
||||
|
||||
// use barycentric coordinates of tetrahedron to find origin
|
||||
ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 1)->v,
|
||||
&ccdSimplexPoint(portal, 2)->v);
|
||||
b[0] = ccdVec3Dot(&vec, &ccdSimplexPoint(portal, 3)->v);
|
||||
|
||||
ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 3)->v,
|
||||
&ccdSimplexPoint(portal, 2)->v);
|
||||
b[1] = ccdVec3Dot(&vec, &ccdSimplexPoint(portal, 0)->v);
|
||||
|
||||
ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 0)->v,
|
||||
&ccdSimplexPoint(portal, 1)->v);
|
||||
b[2] = ccdVec3Dot(&vec, &ccdSimplexPoint(portal, 3)->v);
|
||||
|
||||
ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 2)->v,
|
||||
&ccdSimplexPoint(portal, 1)->v);
|
||||
b[3] = ccdVec3Dot(&vec, &ccdSimplexPoint(portal, 0)->v);
|
||||
|
||||
sum = b[0] + b[1] + b[2] + b[3];
|
||||
|
||||
if (ccdIsZero(sum) || sum < CCD_ZERO){
|
||||
b[0] = CCD_REAL(0.);
|
||||
|
||||
ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 2)->v,
|
||||
&ccdSimplexPoint(portal, 3)->v);
|
||||
b[1] = ccdVec3Dot(&vec, &dir);
|
||||
ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 3)->v,
|
||||
&ccdSimplexPoint(portal, 1)->v);
|
||||
b[2] = ccdVec3Dot(&vec, &dir);
|
||||
ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 1)->v,
|
||||
&ccdSimplexPoint(portal, 2)->v);
|
||||
b[3] = ccdVec3Dot(&vec, &dir);
|
||||
|
||||
sum = b[1] + b[2] + b[3];
|
||||
}
|
||||
|
||||
inv = CCD_REAL(1.) / sum;
|
||||
|
||||
ccdVec3Copy(&p1, ccd_vec3_origin);
|
||||
ccdVec3Copy(&p2, ccd_vec3_origin);
|
||||
for (i = 0; i < 4; i++){
|
||||
ccdVec3Copy(&vec, &ccdSimplexPoint(portal, i)->v1);
|
||||
ccdVec3Scale(&vec, b[i]);
|
||||
ccdVec3Add(&p1, &vec);
|
||||
|
||||
ccdVec3Copy(&vec, &ccdSimplexPoint(portal, i)->v2);
|
||||
ccdVec3Scale(&vec, b[i]);
|
||||
ccdVec3Add(&p2, &vec);
|
||||
}
|
||||
ccdVec3Scale(&p1, inv);
|
||||
ccdVec3Scale(&p2, inv);
|
||||
|
||||
ccdVec3Copy(pos, &p1);
|
||||
ccdVec3Add(pos, &p2);
|
||||
ccdVec3Scale(pos, 0.5);
|
||||
}
|
||||
|
||||
_ccd_inline void expandPortal(ccd_simplex_t *portal,
|
||||
const ccd_support_t *v4)
|
||||
{
|
||||
ccd_real_t dot;
|
||||
ccd_vec3_t v4v0;
|
||||
|
||||
ccdVec3Cross(&v4v0, &v4->v, &ccdSimplexPoint(portal, 0)->v);
|
||||
dot = ccdVec3Dot(&ccdSimplexPoint(portal, 1)->v, &v4v0);
|
||||
if (dot > CCD_ZERO){
|
||||
dot = ccdVec3Dot(&ccdSimplexPoint(portal, 2)->v, &v4v0);
|
||||
if (dot > CCD_ZERO){
|
||||
ccdSimplexSet(portal, 1, v4);
|
||||
}else{
|
||||
ccdSimplexSet(portal, 3, v4);
|
||||
}
|
||||
}else{
|
||||
dot = ccdVec3Dot(&ccdSimplexPoint(portal, 3)->v, &v4v0);
|
||||
if (dot > CCD_ZERO){
|
||||
ccdSimplexSet(portal, 2, v4);
|
||||
}else{
|
||||
ccdSimplexSet(portal, 1, v4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ccd_inline void portalDir(const ccd_simplex_t *portal, ccd_vec3_t *dir)
|
||||
{
|
||||
ccd_vec3_t v2v1, v3v1;
|
||||
|
||||
ccdVec3Sub2(&v2v1, &ccdSimplexPoint(portal, 2)->v,
|
||||
&ccdSimplexPoint(portal, 1)->v);
|
||||
ccdVec3Sub2(&v3v1, &ccdSimplexPoint(portal, 3)->v,
|
||||
&ccdSimplexPoint(portal, 1)->v);
|
||||
ccdVec3Cross(dir, &v2v1, &v3v1);
|
||||
ccdVec3Normalize(dir);
|
||||
}
|
||||
|
||||
_ccd_inline int portalEncapsulesOrigin(const ccd_simplex_t *portal,
|
||||
const ccd_vec3_t *dir)
|
||||
{
|
||||
ccd_real_t dot;
|
||||
dot = ccdVec3Dot(dir, &ccdSimplexPoint(portal, 1)->v);
|
||||
return ccdIsZero(dot) || dot > CCD_ZERO;
|
||||
}
|
||||
|
||||
_ccd_inline int portalReachTolerance(const ccd_simplex_t *portal,
|
||||
const ccd_support_t *v4,
|
||||
const ccd_vec3_t *dir,
|
||||
const ccd_t *ccd)
|
||||
{
|
||||
ccd_real_t dv1, dv2, dv3, dv4;
|
||||
ccd_real_t dot1, dot2, dot3;
|
||||
|
||||
// find the smallest dot product of dir and {v1-v4, v2-v4, v3-v4}
|
||||
|
||||
dv1 = ccdVec3Dot(&ccdSimplexPoint(portal, 1)->v, dir);
|
||||
dv2 = ccdVec3Dot(&ccdSimplexPoint(portal, 2)->v, dir);
|
||||
dv3 = ccdVec3Dot(&ccdSimplexPoint(portal, 3)->v, dir);
|
||||
dv4 = ccdVec3Dot(&v4->v, dir);
|
||||
|
||||
dot1 = dv4 - dv1;
|
||||
dot2 = dv4 - dv2;
|
||||
dot3 = dv4 - dv3;
|
||||
|
||||
dot1 = CCD_FMIN(dot1, dot2);
|
||||
dot1 = CCD_FMIN(dot1, dot3);
|
||||
|
||||
return ccdEq(dot1, ccd->mpr_tolerance) || dot1 < ccd->mpr_tolerance;
|
||||
}
|
||||
|
||||
_ccd_inline int portalCanEncapsuleOrigin(const ccd_simplex_t *portal,
|
||||
const ccd_support_t *v4,
|
||||
const ccd_vec3_t *dir)
|
||||
{
|
||||
ccd_real_t dot;
|
||||
dot = ccdVec3Dot(&v4->v, dir);
|
||||
return ccdIsZero(dot) || dot > CCD_ZERO;
|
||||
}
|
||||
@ -0,0 +1,298 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <float.h>
|
||||
#include "polytope.h"
|
||||
#include "alloc.h"
|
||||
|
||||
_ccd_inline void _ccdPtNearestUpdate(ccd_pt_t *pt, ccd_pt_el_t *el)
|
||||
{
|
||||
if (ccdEq(pt->nearest_dist, el->dist)){
|
||||
if (el->type < pt->nearest_type){
|
||||
pt->nearest = el;
|
||||
pt->nearest_dist = el->dist;
|
||||
pt->nearest_type = el->type;
|
||||
}
|
||||
}else if (el->dist < pt->nearest_dist){
|
||||
pt->nearest = el;
|
||||
pt->nearest_dist = el->dist;
|
||||
pt->nearest_type = el->type;
|
||||
}
|
||||
}
|
||||
|
||||
static void _ccdPtNearestRenew(ccd_pt_t *pt)
|
||||
{
|
||||
ccd_pt_vertex_t *v;
|
||||
ccd_pt_edge_t *e;
|
||||
ccd_pt_face_t *f;
|
||||
|
||||
pt->nearest_dist = CCD_REAL_MAX;
|
||||
pt->nearest_type = 3;
|
||||
pt->nearest = NULL;
|
||||
|
||||
ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
|
||||
_ccdPtNearestUpdate(pt, (ccd_pt_el_t *)v);
|
||||
}
|
||||
|
||||
ccdListForEachEntry(&pt->edges, e, ccd_pt_edge_t, list){
|
||||
_ccdPtNearestUpdate(pt, (ccd_pt_el_t *)e);
|
||||
}
|
||||
|
||||
ccdListForEachEntry(&pt->faces, f, ccd_pt_face_t, list){
|
||||
_ccdPtNearestUpdate(pt, (ccd_pt_el_t *)f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ccdPtInit(ccd_pt_t *pt)
|
||||
{
|
||||
ccdListInit(&pt->vertices);
|
||||
ccdListInit(&pt->edges);
|
||||
ccdListInit(&pt->faces);
|
||||
|
||||
pt->nearest = NULL;
|
||||
pt->nearest_dist = CCD_REAL_MAX;
|
||||
pt->nearest_type = 3;
|
||||
}
|
||||
|
||||
void ccdPtDestroy(ccd_pt_t *pt)
|
||||
{
|
||||
ccd_pt_face_t *f, *f2;
|
||||
ccd_pt_edge_t *e, *e2;
|
||||
ccd_pt_vertex_t *v, *v2;
|
||||
|
||||
// first delete all faces
|
||||
ccdListForEachEntrySafe(&pt->faces, f, ccd_pt_face_t, f2, ccd_pt_face_t, list){
|
||||
ccdPtDelFace(pt, f);
|
||||
}
|
||||
|
||||
// delete all edges
|
||||
ccdListForEachEntrySafe(&pt->edges, e, ccd_pt_edge_t, e2, ccd_pt_edge_t, list){
|
||||
ccdPtDelEdge(pt, e);
|
||||
}
|
||||
|
||||
// delete all vertices
|
||||
ccdListForEachEntrySafe(&pt->vertices, v, ccd_pt_vertex_t, v2, ccd_pt_vertex_t, list){
|
||||
ccdPtDelVertex(pt, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ccd_pt_vertex_t *ccdPtAddVertex(ccd_pt_t *pt, const ccd_support_t *v)
|
||||
{
|
||||
ccd_pt_vertex_t *vert;
|
||||
|
||||
vert = CCD_ALLOC(ccd_pt_vertex_t);
|
||||
if (vert == NULL)
|
||||
return NULL;
|
||||
|
||||
vert->type = CCD_PT_VERTEX;
|
||||
ccdSupportCopy(&vert->v, v);
|
||||
|
||||
vert->dist = ccdVec3Len2(&vert->v.v);
|
||||
ccdVec3Copy(&vert->witness, &vert->v.v);
|
||||
|
||||
ccdListInit(&vert->edges);
|
||||
|
||||
// add vertex to list
|
||||
ccdListAppend(&pt->vertices, &vert->list);
|
||||
|
||||
// update position in .nearest array
|
||||
_ccdPtNearestUpdate(pt, (ccd_pt_el_t *)vert);
|
||||
|
||||
return vert;
|
||||
}
|
||||
|
||||
ccd_pt_edge_t *ccdPtAddEdge(ccd_pt_t *pt, ccd_pt_vertex_t *v1,
|
||||
ccd_pt_vertex_t *v2)
|
||||
{
|
||||
const ccd_vec3_t *a, *b;
|
||||
ccd_pt_edge_t *edge;
|
||||
|
||||
if (v1 == NULL || v2 == NULL)
|
||||
return NULL;
|
||||
|
||||
edge = CCD_ALLOC(ccd_pt_edge_t);
|
||||
if (edge == NULL)
|
||||
return NULL;
|
||||
|
||||
edge->type = CCD_PT_EDGE;
|
||||
edge->vertex[0] = v1;
|
||||
edge->vertex[1] = v2;
|
||||
edge->faces[0] = edge->faces[1] = NULL;
|
||||
|
||||
a = &edge->vertex[0]->v.v;
|
||||
b = &edge->vertex[1]->v.v;
|
||||
edge->dist = ccdVec3PointSegmentDist2(ccd_vec3_origin, a, b, &edge->witness);
|
||||
|
||||
ccdListAppend(&edge->vertex[0]->edges, &edge->vertex_list[0]);
|
||||
ccdListAppend(&edge->vertex[1]->edges, &edge->vertex_list[1]);
|
||||
|
||||
ccdListAppend(&pt->edges, &edge->list);
|
||||
|
||||
// update position in .nearest array
|
||||
_ccdPtNearestUpdate(pt, (ccd_pt_el_t *)edge);
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
ccd_pt_face_t *ccdPtAddFace(ccd_pt_t *pt, ccd_pt_edge_t *e1,
|
||||
ccd_pt_edge_t *e2,
|
||||
ccd_pt_edge_t *e3)
|
||||
{
|
||||
const ccd_vec3_t *a, *b, *c;
|
||||
ccd_pt_face_t *face;
|
||||
ccd_pt_edge_t *e;
|
||||
size_t i;
|
||||
|
||||
if (e1 == NULL || e2 == NULL || e3 == NULL)
|
||||
return NULL;
|
||||
|
||||
face = CCD_ALLOC(ccd_pt_face_t);
|
||||
if (face == NULL)
|
||||
return NULL;
|
||||
|
||||
face->type = CCD_PT_FACE;
|
||||
face->edge[0] = e1;
|
||||
face->edge[1] = e2;
|
||||
face->edge[2] = e3;
|
||||
|
||||
// obtain triplet of vertices
|
||||
a = &face->edge[0]->vertex[0]->v.v;
|
||||
b = &face->edge[0]->vertex[1]->v.v;
|
||||
e = face->edge[1];
|
||||
if (e->vertex[0] != face->edge[0]->vertex[0]
|
||||
&& e->vertex[0] != face->edge[0]->vertex[1]){
|
||||
c = &e->vertex[0]->v.v;
|
||||
}else{
|
||||
c = &e->vertex[1]->v.v;
|
||||
}
|
||||
face->dist = ccdVec3PointTriDist2(ccd_vec3_origin, a, b, c, &face->witness);
|
||||
|
||||
|
||||
for (i = 0; i < 3; i++){
|
||||
if (face->edge[i]->faces[0] == NULL){
|
||||
face->edge[i]->faces[0] = face;
|
||||
}else{
|
||||
face->edge[i]->faces[1] = face;
|
||||
}
|
||||
}
|
||||
|
||||
ccdListAppend(&pt->faces, &face->list);
|
||||
|
||||
// update position in .nearest array
|
||||
_ccdPtNearestUpdate(pt, (ccd_pt_el_t *)face);
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
|
||||
void ccdPtRecomputeDistances(ccd_pt_t *pt)
|
||||
{
|
||||
ccd_pt_vertex_t *v;
|
||||
ccd_pt_edge_t *e;
|
||||
ccd_pt_face_t *f;
|
||||
const ccd_vec3_t *a, *b, *c;
|
||||
ccd_real_t dist;
|
||||
|
||||
ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
|
||||
dist = ccdVec3Len2(&v->v.v);
|
||||
v->dist = dist;
|
||||
ccdVec3Copy(&v->witness, &v->v.v);
|
||||
}
|
||||
|
||||
ccdListForEachEntry(&pt->edges, e, ccd_pt_edge_t, list){
|
||||
a = &e->vertex[0]->v.v;
|
||||
b = &e->vertex[1]->v.v;
|
||||
dist = ccdVec3PointSegmentDist2(ccd_vec3_origin, a, b, &e->witness);
|
||||
e->dist = dist;
|
||||
}
|
||||
|
||||
ccdListForEachEntry(&pt->faces, f, ccd_pt_face_t, list){
|
||||
// obtain triplet of vertices
|
||||
a = &f->edge[0]->vertex[0]->v.v;
|
||||
b = &f->edge[0]->vertex[1]->v.v;
|
||||
e = f->edge[1];
|
||||
if (e->vertex[0] != f->edge[0]->vertex[0]
|
||||
&& e->vertex[0] != f->edge[0]->vertex[1]){
|
||||
c = &e->vertex[0]->v.v;
|
||||
}else{
|
||||
c = &e->vertex[1]->v.v;
|
||||
}
|
||||
|
||||
dist = ccdVec3PointTriDist2(ccd_vec3_origin, a, b, c, &f->witness);
|
||||
f->dist = dist;
|
||||
}
|
||||
}
|
||||
|
||||
ccd_pt_el_t *ccdPtNearest(ccd_pt_t *pt)
|
||||
{
|
||||
if (!pt->nearest){
|
||||
_ccdPtNearestRenew(pt);
|
||||
}
|
||||
return pt->nearest;
|
||||
}
|
||||
|
||||
|
||||
void ccdPtDumpSVT(ccd_pt_t *pt, const char *fn)
|
||||
{
|
||||
FILE *fout;
|
||||
|
||||
fout = fopen(fn, "a");
|
||||
if (fout == NULL)
|
||||
return;
|
||||
|
||||
ccdPtDumpSVT2(pt, fout);
|
||||
|
||||
fclose(fout);
|
||||
}
|
||||
|
||||
void ccdPtDumpSVT2(ccd_pt_t *pt, FILE *fout)
|
||||
{
|
||||
ccd_pt_vertex_t *v, *a, *b, *c;
|
||||
ccd_pt_edge_t *e;
|
||||
ccd_pt_face_t *f;
|
||||
size_t i;
|
||||
|
||||
fprintf(fout, "-----\n");
|
||||
|
||||
fprintf(fout, "Points:\n");
|
||||
i = 0;
|
||||
ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
|
||||
v->id = i++;
|
||||
fprintf(fout, "%lf %lf %lf\n",
|
||||
ccdVec3X(&v->v.v), ccdVec3Y(&v->v.v), ccdVec3Z(&v->v.v));
|
||||
}
|
||||
|
||||
fprintf(fout, "Edges:\n");
|
||||
ccdListForEachEntry(&pt->edges, e, ccd_pt_edge_t, list){
|
||||
fprintf(fout, "%d %d\n", e->vertex[0]->id, e->vertex[1]->id);
|
||||
}
|
||||
|
||||
fprintf(fout, "Faces:\n");
|
||||
ccdListForEachEntry(&pt->faces, f, ccd_pt_face_t, list){
|
||||
a = f->edge[0]->vertex[0];
|
||||
b = f->edge[0]->vertex[1];
|
||||
c = f->edge[1]->vertex[0];
|
||||
if (c == a || c == b){
|
||||
c = f->edge[1]->vertex[1];
|
||||
}
|
||||
fprintf(fout, "%d %d %d\n", a->id, b->id, c->id);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,322 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_POLYTOPE_H__
|
||||
#define __CCD_POLYTOPE_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "support.h"
|
||||
#include "list.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define CCD_PT_VERTEX 1
|
||||
#define CCD_PT_EDGE 2
|
||||
#define CCD_PT_FACE 3
|
||||
|
||||
|
||||
#define __CCD_PT_EL \
|
||||
int type; /*! type of element */ \
|
||||
ccd_real_t dist; /*! distance from origin */ \
|
||||
ccd_vec3_t witness; /*! witness point of projection of origin */ \
|
||||
ccd_list_t list; /*! list of elements of same type */
|
||||
|
||||
/**
|
||||
* General polytope element.
|
||||
* Could be vertex, edge or triangle.
|
||||
*/
|
||||
struct _ccd_pt_el_t {
|
||||
__CCD_PT_EL
|
||||
};
|
||||
typedef struct _ccd_pt_el_t ccd_pt_el_t;
|
||||
|
||||
struct _ccd_pt_edge_t;
|
||||
struct _ccd_pt_face_t;
|
||||
|
||||
/**
|
||||
* Polytope's vertex.
|
||||
*/
|
||||
struct _ccd_pt_vertex_t {
|
||||
__CCD_PT_EL
|
||||
|
||||
int id;
|
||||
ccd_support_t v;
|
||||
ccd_list_t edges; //!< List of edges
|
||||
};
|
||||
typedef struct _ccd_pt_vertex_t ccd_pt_vertex_t;
|
||||
|
||||
/**
|
||||
* Polytope's edge.
|
||||
*/
|
||||
struct _ccd_pt_edge_t {
|
||||
__CCD_PT_EL
|
||||
|
||||
ccd_pt_vertex_t *vertex[2]; //!< Reference to vertices
|
||||
struct _ccd_pt_face_t *faces[2]; //!< Reference to faces
|
||||
|
||||
ccd_list_t vertex_list[2]; //!< List items in vertices' lists
|
||||
};
|
||||
typedef struct _ccd_pt_edge_t ccd_pt_edge_t;
|
||||
|
||||
/**
|
||||
* Polytope's triangle faces.
|
||||
*/
|
||||
struct _ccd_pt_face_t {
|
||||
__CCD_PT_EL
|
||||
|
||||
ccd_pt_edge_t *edge[3]; //!< Reference to surrounding edges
|
||||
};
|
||||
typedef struct _ccd_pt_face_t ccd_pt_face_t;
|
||||
|
||||
|
||||
/**
|
||||
* Struct containing polytope.
|
||||
*/
|
||||
struct _ccd_pt_t {
|
||||
ccd_list_t vertices; //!< List of vertices
|
||||
ccd_list_t edges; //!< List of edges
|
||||
ccd_list_t faces; //!< List of faces
|
||||
|
||||
ccd_pt_el_t *nearest;
|
||||
ccd_real_t nearest_dist;
|
||||
int nearest_type;
|
||||
};
|
||||
typedef struct _ccd_pt_t ccd_pt_t;
|
||||
|
||||
|
||||
CCD_EXPORT void ccdPtInit(ccd_pt_t *pt);
|
||||
CCD_EXPORT void ccdPtDestroy(ccd_pt_t *pt);
|
||||
|
||||
/**
|
||||
* Returns vertices surrounding given triangle face.
|
||||
*/
|
||||
_ccd_inline void ccdPtFaceVec3(const ccd_pt_face_t *face,
|
||||
ccd_vec3_t **a,
|
||||
ccd_vec3_t **b,
|
||||
ccd_vec3_t **c);
|
||||
_ccd_inline void ccdPtFaceVertices(const ccd_pt_face_t *face,
|
||||
ccd_pt_vertex_t **a,
|
||||
ccd_pt_vertex_t **b,
|
||||
ccd_pt_vertex_t **c);
|
||||
_ccd_inline void ccdPtFaceEdges(const ccd_pt_face_t *f,
|
||||
ccd_pt_edge_t **a,
|
||||
ccd_pt_edge_t **b,
|
||||
ccd_pt_edge_t **c);
|
||||
|
||||
_ccd_inline void ccdPtEdgeVec3(const ccd_pt_edge_t *e,
|
||||
ccd_vec3_t **a,
|
||||
ccd_vec3_t **b);
|
||||
_ccd_inline void ccdPtEdgeVertices(const ccd_pt_edge_t *e,
|
||||
ccd_pt_vertex_t **a,
|
||||
ccd_pt_vertex_t **b);
|
||||
_ccd_inline void ccdPtEdgeFaces(const ccd_pt_edge_t *e,
|
||||
ccd_pt_face_t **f1,
|
||||
ccd_pt_face_t **f2);
|
||||
|
||||
|
||||
/**
|
||||
* Adds vertex to polytope and returns pointer to newly created vertex.
|
||||
*/
|
||||
CCD_EXPORT ccd_pt_vertex_t *ccdPtAddVertex(ccd_pt_t *pt, const ccd_support_t *v);
|
||||
_ccd_inline ccd_pt_vertex_t *ccdPtAddVertexCoords(ccd_pt_t *pt,
|
||||
ccd_real_t x, ccd_real_t y, ccd_real_t z);
|
||||
|
||||
/**
|
||||
* Adds edge to polytope.
|
||||
*/
|
||||
CCD_EXPORT ccd_pt_edge_t *ccdPtAddEdge(ccd_pt_t *pt, ccd_pt_vertex_t *v1,
|
||||
ccd_pt_vertex_t *v2);
|
||||
|
||||
/**
|
||||
* Adds face to polytope.
|
||||
*/
|
||||
CCD_EXPORT ccd_pt_face_t *ccdPtAddFace(ccd_pt_t *pt, ccd_pt_edge_t *e1,
|
||||
ccd_pt_edge_t *e2,
|
||||
ccd_pt_edge_t *e3);
|
||||
|
||||
/**
|
||||
* Deletes vertex from polytope.
|
||||
* Returns 0 on success, -1 otherwise.
|
||||
*/
|
||||
_ccd_inline int ccdPtDelVertex(ccd_pt_t *pt, ccd_pt_vertex_t *);
|
||||
_ccd_inline int ccdPtDelEdge(ccd_pt_t *pt, ccd_pt_edge_t *);
|
||||
_ccd_inline int ccdPtDelFace(ccd_pt_t *pt, ccd_pt_face_t *);
|
||||
|
||||
|
||||
/**
|
||||
* Recompute distances from origin for all elements in pt.
|
||||
*/
|
||||
CCD_EXPORT void ccdPtRecomputeDistances(ccd_pt_t *pt);
|
||||
|
||||
/**
|
||||
* Returns nearest element to origin.
|
||||
*/
|
||||
CCD_EXPORT ccd_pt_el_t *ccdPtNearest(ccd_pt_t *pt);
|
||||
|
||||
|
||||
CCD_EXPORT void ccdPtDumpSVT(ccd_pt_t *pt, const char *fn);
|
||||
CCD_EXPORT void ccdPtDumpSVT2(ccd_pt_t *pt, FILE *);
|
||||
|
||||
|
||||
/**** INLINES ****/
|
||||
_ccd_inline ccd_pt_vertex_t *ccdPtAddVertexCoords(ccd_pt_t *pt,
|
||||
ccd_real_t x, ccd_real_t y, ccd_real_t z)
|
||||
{
|
||||
ccd_support_t s;
|
||||
ccdVec3Set(&s.v, x, y, z);
|
||||
return ccdPtAddVertex(pt, &s);
|
||||
}
|
||||
|
||||
_ccd_inline int ccdPtDelVertex(ccd_pt_t *pt, ccd_pt_vertex_t *v)
|
||||
{
|
||||
// test if any edge is connected to this vertex
|
||||
if (!ccdListEmpty(&v->edges))
|
||||
return -1;
|
||||
|
||||
// delete vertex from main list
|
||||
ccdListDel(&v->list);
|
||||
|
||||
if ((void *)pt->nearest == (void *)v){
|
||||
pt->nearest = NULL;
|
||||
}
|
||||
|
||||
free(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_ccd_inline int ccdPtDelEdge(ccd_pt_t *pt, ccd_pt_edge_t *e)
|
||||
{
|
||||
// text if any face is connected to this edge (faces[] is always
|
||||
// aligned to lower indices)
|
||||
if (e->faces[0] != NULL)
|
||||
return -1;
|
||||
|
||||
// disconnect edge from lists of edges in vertex struct
|
||||
ccdListDel(&e->vertex_list[0]);
|
||||
ccdListDel(&e->vertex_list[1]);
|
||||
|
||||
// disconnect edge from main list
|
||||
ccdListDel(&e->list);
|
||||
|
||||
if ((void *)pt->nearest == (void *)e){
|
||||
pt->nearest = NULL;
|
||||
}
|
||||
|
||||
free(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_ccd_inline int ccdPtDelFace(ccd_pt_t *pt, ccd_pt_face_t *f)
|
||||
{
|
||||
ccd_pt_edge_t *e;
|
||||
size_t i;
|
||||
|
||||
// remove face from edges' recerence lists
|
||||
for (i = 0; i < 3; i++){
|
||||
e = f->edge[i];
|
||||
if (e->faces[0] == f){
|
||||
e->faces[0] = e->faces[1];
|
||||
}
|
||||
e->faces[1] = NULL;
|
||||
}
|
||||
|
||||
// remove face from list of all faces
|
||||
ccdListDel(&f->list);
|
||||
|
||||
if ((void *)pt->nearest == (void *)f){
|
||||
pt->nearest = NULL;
|
||||
}
|
||||
|
||||
free(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdPtFaceVec3(const ccd_pt_face_t *face,
|
||||
ccd_vec3_t **a,
|
||||
ccd_vec3_t **b,
|
||||
ccd_vec3_t **c)
|
||||
{
|
||||
*a = &face->edge[0]->vertex[0]->v.v;
|
||||
*b = &face->edge[0]->vertex[1]->v.v;
|
||||
|
||||
if (face->edge[1]->vertex[0] != face->edge[0]->vertex[0]
|
||||
&& face->edge[1]->vertex[0] != face->edge[0]->vertex[1]){
|
||||
*c = &face->edge[1]->vertex[0]->v.v;
|
||||
}else{
|
||||
*c = &face->edge[1]->vertex[1]->v.v;
|
||||
}
|
||||
}
|
||||
|
||||
_ccd_inline void ccdPtFaceVertices(const ccd_pt_face_t *face,
|
||||
ccd_pt_vertex_t **a,
|
||||
ccd_pt_vertex_t **b,
|
||||
ccd_pt_vertex_t **c)
|
||||
{
|
||||
*a = face->edge[0]->vertex[0];
|
||||
*b = face->edge[0]->vertex[1];
|
||||
|
||||
if (face->edge[1]->vertex[0] != face->edge[0]->vertex[0]
|
||||
&& face->edge[1]->vertex[0] != face->edge[0]->vertex[1]){
|
||||
*c = face->edge[1]->vertex[0];
|
||||
}else{
|
||||
*c = face->edge[1]->vertex[1];
|
||||
}
|
||||
}
|
||||
|
||||
_ccd_inline void ccdPtFaceEdges(const ccd_pt_face_t *f,
|
||||
ccd_pt_edge_t **a,
|
||||
ccd_pt_edge_t **b,
|
||||
ccd_pt_edge_t **c)
|
||||
{
|
||||
*a = f->edge[0];
|
||||
*b = f->edge[1];
|
||||
*c = f->edge[2];
|
||||
}
|
||||
|
||||
_ccd_inline void ccdPtEdgeVec3(const ccd_pt_edge_t *e,
|
||||
ccd_vec3_t **a,
|
||||
ccd_vec3_t **b)
|
||||
{
|
||||
*a = &e->vertex[0]->v.v;
|
||||
*b = &e->vertex[1]->v.v;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdPtEdgeVertices(const ccd_pt_edge_t *e,
|
||||
ccd_pt_vertex_t **a,
|
||||
ccd_pt_vertex_t **b)
|
||||
{
|
||||
*a = e->vertex[0];
|
||||
*b = e->vertex[1];
|
||||
}
|
||||
|
||||
_ccd_inline void ccdPtEdgeFaces(const ccd_pt_edge_t *e,
|
||||
ccd_pt_face_t **f1,
|
||||
ccd_pt_face_t **f2)
|
||||
{
|
||||
*f1 = e->faces[0];
|
||||
*f2 = e->faces[1];
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CCD_POLYTOPE_H__ */
|
||||
@ -0,0 +1,104 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_SIMPLEX_H__
|
||||
#define __CCD_SIMPLEX_H__
|
||||
|
||||
#include <ccd/compiler.h>
|
||||
#include "support.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
struct _ccd_simplex_t {
|
||||
ccd_support_t ps[4];
|
||||
int last; //!< index of last added point
|
||||
};
|
||||
typedef struct _ccd_simplex_t ccd_simplex_t;
|
||||
|
||||
|
||||
_ccd_inline void ccdSimplexInit(ccd_simplex_t *s);
|
||||
_ccd_inline int ccdSimplexSize(const ccd_simplex_t *s);
|
||||
_ccd_inline const ccd_support_t *ccdSimplexLast(const ccd_simplex_t *s);
|
||||
_ccd_inline const ccd_support_t *ccdSimplexPoint(const ccd_simplex_t *s, int idx);
|
||||
_ccd_inline ccd_support_t *ccdSimplexPointW(ccd_simplex_t *s, int idx);
|
||||
|
||||
_ccd_inline void ccdSimplexAdd(ccd_simplex_t *s, const ccd_support_t *v);
|
||||
_ccd_inline void ccdSimplexSet(ccd_simplex_t *s, size_t pos, const ccd_support_t *a);
|
||||
_ccd_inline void ccdSimplexSetSize(ccd_simplex_t *s, int size);
|
||||
_ccd_inline void ccdSimplexSwap(ccd_simplex_t *s, size_t pos1, size_t pos2);
|
||||
|
||||
|
||||
/**** INLINES ****/
|
||||
|
||||
_ccd_inline void ccdSimplexInit(ccd_simplex_t *s)
|
||||
{
|
||||
s->last = -1;
|
||||
}
|
||||
|
||||
_ccd_inline int ccdSimplexSize(const ccd_simplex_t *s)
|
||||
{
|
||||
return s->last + 1;
|
||||
}
|
||||
|
||||
_ccd_inline const ccd_support_t *ccdSimplexLast(const ccd_simplex_t *s)
|
||||
{
|
||||
return ccdSimplexPoint(s, s->last);
|
||||
}
|
||||
|
||||
_ccd_inline const ccd_support_t *ccdSimplexPoint(const ccd_simplex_t *s, int idx)
|
||||
{
|
||||
// here is no check on boundaries
|
||||
return &s->ps[idx];
|
||||
}
|
||||
_ccd_inline ccd_support_t *ccdSimplexPointW(ccd_simplex_t *s, int idx)
|
||||
{
|
||||
return &s->ps[idx];
|
||||
}
|
||||
|
||||
_ccd_inline void ccdSimplexAdd(ccd_simplex_t *s, const ccd_support_t *v)
|
||||
{
|
||||
// here is no check on boundaries in sake of speed
|
||||
++s->last;
|
||||
ccdSupportCopy(s->ps + s->last, v);
|
||||
}
|
||||
|
||||
_ccd_inline void ccdSimplexSet(ccd_simplex_t *s, size_t pos, const ccd_support_t *a)
|
||||
{
|
||||
ccdSupportCopy(s->ps + pos, a);
|
||||
}
|
||||
|
||||
_ccd_inline void ccdSimplexSetSize(ccd_simplex_t *s, int size)
|
||||
{
|
||||
s->last = size - 1;
|
||||
}
|
||||
|
||||
_ccd_inline void ccdSimplexSwap(ccd_simplex_t *s, size_t pos1, size_t pos2)
|
||||
{
|
||||
ccd_support_t supp;
|
||||
|
||||
ccdSupportCopy(&supp, &s->ps[pos1]);
|
||||
ccdSupportCopy(&s->ps[pos1], &s->ps[pos2]);
|
||||
ccdSupportCopy(&s->ps[pos2], &supp);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CCD_SIMPLEX_H__ */
|
||||
@ -0,0 +1,34 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#include "support.h"
|
||||
|
||||
void __ccdSupport(const void *obj1, const void *obj2,
|
||||
const ccd_vec3_t *_dir, const ccd_t *ccd,
|
||||
ccd_support_t *supp)
|
||||
{
|
||||
ccd_vec3_t dir;
|
||||
|
||||
ccdVec3Copy(&dir, _dir);
|
||||
|
||||
ccd->support1(obj1, &dir, &supp->v1);
|
||||
|
||||
ccdVec3Scale(&dir, -CCD_ONE);
|
||||
ccd->support2(obj2, &dir, &supp->v2);
|
||||
|
||||
ccdVec3Sub2(&supp->v, &supp->v1, &supp->v2);
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#ifndef __CCD_SUPPORT_H__
|
||||
#define __CCD_SUPPORT_H__
|
||||
|
||||
#include <ccd/ccd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
struct _ccd_support_t {
|
||||
ccd_vec3_t v; //!< Support point in minkowski sum
|
||||
ccd_vec3_t v1; //!< Support point in obj1
|
||||
ccd_vec3_t v2; //!< Support point in obj2
|
||||
};
|
||||
typedef struct _ccd_support_t ccd_support_t;
|
||||
|
||||
_ccd_inline void ccdSupportCopy(ccd_support_t *, const ccd_support_t *s);
|
||||
|
||||
/**
|
||||
* Computes support point of obj1 and obj2 in direction dir.
|
||||
* Support point is returned via supp.
|
||||
*/
|
||||
CCD_EXPORT void __ccdSupport(const void *obj1, const void *obj2,
|
||||
const ccd_vec3_t *dir, const ccd_t *ccd,
|
||||
ccd_support_t *supp);
|
||||
|
||||
|
||||
/**** INLINES ****/
|
||||
_ccd_inline void ccdSupportCopy(ccd_support_t *d, const ccd_support_t *s)
|
||||
{
|
||||
*d = *s;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __CCD_SUPPORT_H__ */
|
||||
@ -0,0 +1,217 @@
|
||||
/***
|
||||
* libccd
|
||||
* ---------------------------------
|
||||
* Copyright (c)2010 Daniel Fiser <danfis@danfis.cz>
|
||||
*
|
||||
*
|
||||
* This file is part of libccd.
|
||||
*
|
||||
* Distributed under the OSI-approved BSD License (the "License");
|
||||
* see accompanying file BDS-LICENSE for details or see
|
||||
* <http://www.opensource.org/licenses/bsd-license.php>.
|
||||
*
|
||||
* This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the License for more information.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ccd/vec3.h>
|
||||
#include "dbg.h"
|
||||
|
||||
static CCD_VEC3(__ccd_vec3_origin, CCD_ZERO, CCD_ZERO, CCD_ZERO);
|
||||
ccd_vec3_t *ccd_vec3_origin = &__ccd_vec3_origin;
|
||||
|
||||
static ccd_vec3_t points_on_sphere[] = {
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.000000), CCD_REAL(-0.000000), CCD_REAL(-1.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.723608), CCD_REAL(-0.525725), CCD_REAL(-0.447219)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.276388), CCD_REAL(-0.850649), CCD_REAL(-0.447219)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.894426), CCD_REAL(-0.000000), CCD_REAL(-0.447216)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.276388), CCD_REAL( 0.850649), CCD_REAL(-0.447220)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.723608), CCD_REAL( 0.525725), CCD_REAL(-0.447219)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.276388), CCD_REAL(-0.850649), CCD_REAL( 0.447220)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.723608), CCD_REAL(-0.525725), CCD_REAL( 0.447219)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.723608), CCD_REAL( 0.525725), CCD_REAL( 0.447219)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.276388), CCD_REAL( 0.850649), CCD_REAL( 0.447219)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.894426), CCD_REAL( 0.000000), CCD_REAL( 0.447216)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.000000), CCD_REAL( 0.000000), CCD_REAL( 1.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.425323), CCD_REAL(-0.309011), CCD_REAL(-0.850654)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.162456), CCD_REAL(-0.499995), CCD_REAL(-0.850654)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.262869), CCD_REAL(-0.809012), CCD_REAL(-0.525738)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.425323), CCD_REAL( 0.309011), CCD_REAL(-0.850654)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.850648), CCD_REAL(-0.000000), CCD_REAL(-0.525736)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.525730), CCD_REAL(-0.000000), CCD_REAL(-0.850652)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.688190), CCD_REAL(-0.499997), CCD_REAL(-0.525736)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.162456), CCD_REAL( 0.499995), CCD_REAL(-0.850654)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.688190), CCD_REAL( 0.499997), CCD_REAL(-0.525736)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.262869), CCD_REAL( 0.809012), CCD_REAL(-0.525738)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.951058), CCD_REAL( 0.309013), CCD_REAL( 0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.951058), CCD_REAL(-0.309013), CCD_REAL( 0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.587786), CCD_REAL(-0.809017), CCD_REAL( 0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.000000), CCD_REAL(-1.000000), CCD_REAL( 0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.587786), CCD_REAL(-0.809017), CCD_REAL( 0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.951058), CCD_REAL(-0.309013), CCD_REAL(-0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.951058), CCD_REAL( 0.309013), CCD_REAL(-0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.587786), CCD_REAL( 0.809017), CCD_REAL(-0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.000000), CCD_REAL( 1.000000), CCD_REAL(-0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.587786), CCD_REAL( 0.809017), CCD_REAL(-0.000000)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.688190), CCD_REAL(-0.499997), CCD_REAL( 0.525736)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.262869), CCD_REAL(-0.809012), CCD_REAL( 0.525738)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.850648), CCD_REAL( 0.000000), CCD_REAL( 0.525736)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.262869), CCD_REAL( 0.809012), CCD_REAL( 0.525738)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.688190), CCD_REAL( 0.499997), CCD_REAL( 0.525736)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.525730), CCD_REAL( 0.000000), CCD_REAL( 0.850652)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.162456), CCD_REAL(-0.499995), CCD_REAL( 0.850654)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.425323), CCD_REAL(-0.309011), CCD_REAL( 0.850654)),
|
||||
CCD_VEC3_STATIC(CCD_REAL(-0.425323), CCD_REAL( 0.309011), CCD_REAL( 0.850654)),
|
||||
CCD_VEC3_STATIC(CCD_REAL( 0.162456), CCD_REAL( 0.499995), CCD_REAL( 0.850654))
|
||||
};
|
||||
ccd_vec3_t *ccd_points_on_sphere = points_on_sphere;
|
||||
size_t ccd_points_on_sphere_len = sizeof(points_on_sphere) / sizeof(ccd_vec3_t);
|
||||
|
||||
|
||||
_ccd_inline ccd_real_t __ccdVec3PointSegmentDist2(const ccd_vec3_t *P,
|
||||
const ccd_vec3_t *x0,
|
||||
const ccd_vec3_t *b,
|
||||
ccd_vec3_t *witness)
|
||||
{
|
||||
// The computation comes from solving equation of segment:
|
||||
// S(t) = x0 + t.d
|
||||
// where - x0 is initial point of segment
|
||||
// - d is direction of segment from x0 (|d| > 0)
|
||||
// - t belongs to <0, 1> interval
|
||||
//
|
||||
// Than, distance from a segment to some point P can be expressed:
|
||||
// D(t) = |x0 + t.d - P|^2
|
||||
// which is distance from any point on segment. Minimization
|
||||
// of this function brings distance from P to segment.
|
||||
// Minimization of D(t) leads to simple quadratic equation that's
|
||||
// solving is straightforward.
|
||||
//
|
||||
// Bonus of this method is witness point for free.
|
||||
|
||||
ccd_real_t dist, t;
|
||||
ccd_vec3_t d, a;
|
||||
|
||||
// direction of segment
|
||||
ccdVec3Sub2(&d, b, x0);
|
||||
|
||||
// precompute vector from P to x0
|
||||
ccdVec3Sub2(&a, x0, P);
|
||||
|
||||
t = -CCD_REAL(1.) * ccdVec3Dot(&a, &d);
|
||||
t /= ccdVec3Len2(&d);
|
||||
|
||||
if (t < CCD_ZERO || ccdIsZero(t)){
|
||||
dist = ccdVec3Dist2(x0, P);
|
||||
if (witness)
|
||||
ccdVec3Copy(witness, x0);
|
||||
}else if (t > CCD_ONE || ccdEq(t, CCD_ONE)){
|
||||
dist = ccdVec3Dist2(b, P);
|
||||
if (witness)
|
||||
ccdVec3Copy(witness, b);
|
||||
}else{
|
||||
if (witness){
|
||||
ccdVec3Copy(witness, &d);
|
||||
ccdVec3Scale(witness, t);
|
||||
ccdVec3Add(witness, x0);
|
||||
dist = ccdVec3Dist2(witness, P);
|
||||
}else{
|
||||
// recycling variables
|
||||
ccdVec3Scale(&d, t);
|
||||
ccdVec3Add(&d, &a);
|
||||
dist = ccdVec3Len2(&d);
|
||||
}
|
||||
}
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
ccd_real_t ccdVec3PointSegmentDist2(const ccd_vec3_t *P,
|
||||
const ccd_vec3_t *x0, const ccd_vec3_t *b,
|
||||
ccd_vec3_t *witness)
|
||||
{
|
||||
return __ccdVec3PointSegmentDist2(P, x0, b, witness);
|
||||
}
|
||||
|
||||
ccd_real_t ccdVec3PointTriDist2(const ccd_vec3_t *P,
|
||||
const ccd_vec3_t *x0, const ccd_vec3_t *B,
|
||||
const ccd_vec3_t *C,
|
||||
ccd_vec3_t *witness)
|
||||
{
|
||||
// Computation comes from analytic expression for triangle (x0, B, C)
|
||||
// T(s, t) = x0 + s.d1 + t.d2, where d1 = B - x0 and d2 = C - x0 and
|
||||
// Then equation for distance is:
|
||||
// D(s, t) = | T(s, t) - P |^2
|
||||
// This leads to minimization of quadratic function of two variables.
|
||||
// The solution from is taken only if s is between 0 and 1, t is
|
||||
// between 0 and 1 and t + s < 1, otherwise distance from segment is
|
||||
// computed.
|
||||
|
||||
ccd_vec3_t d1, d2, a;
|
||||
ccd_real_t u, v, w, p, q, r, d;
|
||||
ccd_real_t s, t, dist, dist2;
|
||||
ccd_vec3_t witness2;
|
||||
|
||||
ccdVec3Sub2(&d1, B, x0);
|
||||
ccdVec3Sub2(&d2, C, x0);
|
||||
ccdVec3Sub2(&a, x0, P);
|
||||
|
||||
u = ccdVec3Dot(&a, &a);
|
||||
v = ccdVec3Dot(&d1, &d1);
|
||||
w = ccdVec3Dot(&d2, &d2);
|
||||
p = ccdVec3Dot(&a, &d1);
|
||||
q = ccdVec3Dot(&a, &d2);
|
||||
r = ccdVec3Dot(&d1, &d2);
|
||||
|
||||
d = w * v - r * r;
|
||||
if (ccdIsZero(d)){
|
||||
// To avoid division by zero for zero (or near zero) area triangles
|
||||
s = t = -1.;
|
||||
}else{
|
||||
s = (q * r - w * p) / d;
|
||||
t = (-s * r - q) / w;
|
||||
}
|
||||
|
||||
if ((ccdIsZero(s) || s > CCD_ZERO)
|
||||
&& (ccdEq(s, CCD_ONE) || s < CCD_ONE)
|
||||
&& (ccdIsZero(t) || t > CCD_ZERO)
|
||||
&& (ccdEq(t, CCD_ONE) || t < CCD_ONE)
|
||||
&& (ccdEq(t + s, CCD_ONE) || t + s < CCD_ONE)){
|
||||
|
||||
if (witness){
|
||||
ccdVec3Scale(&d1, s);
|
||||
ccdVec3Scale(&d2, t);
|
||||
ccdVec3Copy(witness, x0);
|
||||
ccdVec3Add(witness, &d1);
|
||||
ccdVec3Add(witness, &d2);
|
||||
|
||||
dist = ccdVec3Dist2(witness, P);
|
||||
}else{
|
||||
dist = s * s * v;
|
||||
dist += t * t * w;
|
||||
dist += CCD_REAL(2.) * s * t * r;
|
||||
dist += CCD_REAL(2.) * s * p;
|
||||
dist += CCD_REAL(2.) * t * q;
|
||||
dist += u;
|
||||
}
|
||||
}else{
|
||||
dist = __ccdVec3PointSegmentDist2(P, x0, B, witness);
|
||||
|
||||
dist2 = __ccdVec3PointSegmentDist2(P, x0, C, &witness2);
|
||||
if (dist2 < dist){
|
||||
dist = dist2;
|
||||
if (witness)
|
||||
ccdVec3Copy(witness, &witness2);
|
||||
}
|
||||
|
||||
dist2 = __ccdVec3PointSegmentDist2(P, B, C, &witness2);
|
||||
if (dist2 < dist){
|
||||
dist = dist2;
|
||||
if (witness)
|
||||
ccdVec3Copy(witness, &witness2);
|
||||
}
|
||||
}
|
||||
|
||||
return dist;
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
|
||||
ifdef MINGW_PREFIX
|
||||
MINGW=1
|
||||
else
|
||||
LINUX=1
|
||||
endif
|
||||
|
||||
# Lua version
|
||||
LUAVER?=5.3
|
||||
|
||||
# Base install directory
|
||||
ifdef LINUX
|
||||
PREFIX?=/usr/local
|
||||
endif
|
||||
ifdef MINGW
|
||||
PREFIX?=$(MINGW_PREFIX)
|
||||
endif
|
||||
|
||||
# Directory where to install Lua modules
|
||||
L_DIR=$(PREFIX)/share/lua/$(LUAVER)
|
||||
# Directory where to install Lua C modules
|
||||
C_DIR=$(PREFIX)/lib/lua/$(LUAVER)
|
||||
# Directory where to install C headers
|
||||
H_DIR=$(PREFIX)/include
|
||||
# Directory where to install C libraries
|
||||
S_DIR=$(PREFIX)/lib
|
||||
|
||||
ifeq ($(D),1)
|
||||
DEBUG=1
|
||||
endif
|
||||
|
||||
ifdef LINUX
|
||||
LIBS = -lccd -lpthread
|
||||
endif
|
||||
ifdef MINGW
|
||||
LIBS = -llua
|
||||
endif
|
||||
|
||||
Tgt := moonccd
|
||||
Src := $(wildcard *.c)
|
||||
Objs := $(Src:.c=.o)
|
||||
|
||||
INCDIR = -I. -I/usr/include/lua$(LUAVER)
|
||||
|
||||
COPT += -O2
|
||||
#COPT += -O0 -g
|
||||
#COPT += -m32
|
||||
COPT += -Wfatal-errors
|
||||
COPT += -Wall -Wextra -Wpedantic
|
||||
COPT += -DCOMPAT53_PREFIX=moonccd_compat_
|
||||
COPT += -std=gnu99
|
||||
COPT += -DLUAVER=$(LUAVER)
|
||||
ifdef LINUX
|
||||
COPT += -fpic
|
||||
COPT += -DLINUX
|
||||
endif
|
||||
ifdef MINGW
|
||||
COPT += -DMINGW
|
||||
endif
|
||||
ifdef DEBUG
|
||||
COPT += -DDEBUG
|
||||
COPT += -Wshadow -Wsign-compare -Wundef -Wwrite-strings
|
||||
COPT += -Wdisabled-optimization -Wdeclaration-after-statement
|
||||
COPT += -Wmissing-prototypes -Wstrict-prototypes -Wnested-externs
|
||||
COPT += -Wold-style-definition
|
||||
#COPT += -Wc++-compat
|
||||
endif
|
||||
|
||||
override CFLAGS = $(COPT) $(INCDIR)
|
||||
|
||||
default: build
|
||||
|
||||
where:
|
||||
@echo "PREFIX="$(PREFIX)
|
||||
@echo "LUAVER="$(LUAVER)
|
||||
@echo $(L_DIR)
|
||||
@echo $(C_DIR)
|
||||
@echo $(H_DIR)
|
||||
@echo $(S_DIR)
|
||||
|
||||
clean:
|
||||
@-rm -f *.so *.dll *.o *.err *.map *.S *~ *.log
|
||||
@-rm -f $(Tgt).symbols
|
||||
|
||||
install:
|
||||
@-mkdir -pv $(H_DIR)
|
||||
@-mkdir -pv $(C_DIR)
|
||||
@-mkdir -pv $(S_DIR)
|
||||
@-mkdir -pv $(L_DIR)
|
||||
@-cp -fpv $(Tgt).h $(H_DIR)
|
||||
@-cp -fpvr ../$(Tgt) $(L_DIR)
|
||||
ifdef LINUX
|
||||
@-cp -fpv $(Tgt).so $(C_DIR)
|
||||
@-ln -fsv $(C_DIR)/$(Tgt).so $(S_DIR)/lib$(Tgt).so
|
||||
endif
|
||||
ifdef MINGW
|
||||
@-cp -fpv $(Tgt).dll $(C_DIR)
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
@-rm -f $(H_DIR)/$(Tgt).h
|
||||
@-rm -f $(C_DIR)/$(Tgt).so
|
||||
@-rm -f $(S_DIR)/lib$(Tgt).so
|
||||
@-rm -fr $(L_DIR)/$(Tgt)
|
||||
@-rm -f $(C_DIR)/$(Tgt).dll
|
||||
|
||||
build: clean $(Tgt)
|
||||
|
||||
symbols: build
|
||||
@objdump -T $(Tgt).so > $(Tgt).symbols
|
||||
|
||||
$(Tgt): $(Objs)
|
||||
ifdef LINUX
|
||||
@$(CXX) -shared -o $(Tgt).so $(Objs) $(LIBDIR) $(LIBS)
|
||||
endif
|
||||
ifdef MINGW
|
||||
@-$(CXX) -shared -o $(Tgt).dll $(Objs) $(LIBDIR) $(LIBS)
|
||||
endif
|
||||
@-rm -f $(Objs)
|
||||
@echo
|
||||
|
||||
@ -0,0 +1,317 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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 "internal.h"
|
||||
|
||||
static ud_t *Ud = NULL;
|
||||
#define PAR 1
|
||||
#define OBJ1 2
|
||||
#define OBJ2 3
|
||||
static void FirstDir(const void *obj1, const void *obj2, vec3_t *dir);
|
||||
static void Support(const void *obj_, const vec3_t *dir, vec3_t *vec);
|
||||
static void Center(const void *obj_, vec3_t *center);
|
||||
|
||||
static int freeccd(lua_State *L, ud_t *ud)
|
||||
{
|
||||
ccd_t *ccd = (ccd_t *)ud->handle;
|
||||
if (!freeuserdata(L, ud, "ccdpar"))
|
||||
return 0;
|
||||
Free(L, ccd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int newccd(lua_State *L, ccd_t *ccd, int ref[6])
|
||||
{
|
||||
ud_t *ud;
|
||||
ud = newuserdata(L, ccd, CCDPAR_MT, "ccdpar");
|
||||
ud->parent_ud = NULL;
|
||||
ud->destructor = freeccd;
|
||||
memcpy(ud->ref, ref, 6 * sizeof(int));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int New(lua_State *L)
|
||||
{
|
||||
ccd_t ccd, *ccdp;
|
||||
int ref[6];
|
||||
int t = lua_type(L, 1);
|
||||
CCD_INIT(&ccd);
|
||||
memset(ref, 0, 6 * sizeof(int));
|
||||
switch (t)
|
||||
{
|
||||
case LUA_TNONE:
|
||||
case LUA_TNIL:
|
||||
return argerror(L, 1, ERR_NOTPRESENT);
|
||||
case LUA_TTABLE:
|
||||
break;
|
||||
default:
|
||||
return argerror(L, 1, ERR_TABLE);
|
||||
}
|
||||
#define checkfn(name, ccdfield, Func, ref) \
|
||||
do \
|
||||
{ \
|
||||
lua_getfield(L, 1, name); \
|
||||
if (lua_isfunction(L, -1)) \
|
||||
/* this also unfererences any previous ref: */ \
|
||||
{ \
|
||||
Reference(L, -1, ref); \
|
||||
ccd.ccdfield = Func; \
|
||||
} \
|
||||
else if (!lua_isnoneornil(L, -1)) \
|
||||
return argerror(L, 1, ERR_FUNCTION); \
|
||||
lua_pop(L, 1); \
|
||||
} while (0)
|
||||
checkfn("first_dir", first_dir, FirstDir, ref[0]);
|
||||
checkfn("support1", support1, Support, ref[1]);
|
||||
checkfn("support2", support2, Support, ref[2]);
|
||||
checkfn("center1", center1, Center, ref[3]);
|
||||
checkfn("center2", center2, Center, ref[4]);
|
||||
#undef checkfn
|
||||
lua_getfield(L, 1, "max_iterations");
|
||||
ccd.max_iterations = luaL_optinteger(L, -1, ccd.max_iterations);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "epa_tolerance");
|
||||
ccd.epa_tolerance = luaL_optnumber(L, -1, ccd.epa_tolerance);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "mpr_tolerance");
|
||||
ccd.mpr_tolerance = luaL_optnumber(L, -1, ccd.mpr_tolerance);
|
||||
lua_pop(L, 1);
|
||||
lua_getfield(L, 1, "dist_tolerance");
|
||||
ccd.dist_tolerance = luaL_optnumber(L, -1, ccd.dist_tolerance);
|
||||
lua_pop(L, 1);
|
||||
ccdp = Malloc(L, sizeof(ccd_t));
|
||||
memcpy(ccdp, &ccd, sizeof(ccd_t));
|
||||
return newccd(L, ccdp, ref);
|
||||
}
|
||||
|
||||
static void FirstDir(const void *obj1, const void *obj2, vec3_t *dir)
|
||||
{
|
||||
#define L moonccd_L
|
||||
int rc;
|
||||
int t = lua_gettop(L);
|
||||
(void)obj1;
|
||||
(void)obj2;
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, Ud->ref[0]);
|
||||
lua_pushvalue(L, OBJ1);
|
||||
lua_pushvalue(L, OBJ2);
|
||||
rc = lua_pcall(L, 2, 1, 0);
|
||||
if (rc != LUA_OK)
|
||||
lua_error(L);
|
||||
checkvec3(L, -1, dir);
|
||||
lua_settop(L, t);
|
||||
#undef L
|
||||
}
|
||||
|
||||
static void Support(const void *obj_, const vec3_t *dir, vec3_t *vec)
|
||||
{
|
||||
#define L moonccd_L
|
||||
int ref, rc;
|
||||
int obj = (ptrdiff_t)obj_;
|
||||
int t = lua_gettop(L);
|
||||
switch (obj)
|
||||
{
|
||||
case OBJ1:
|
||||
ref = Ud->ref[1];
|
||||
break;
|
||||
case OBJ2:
|
||||
ref = Ud->ref[2];
|
||||
break;
|
||||
default:
|
||||
unexpected(L);
|
||||
return;
|
||||
}
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
lua_pushvalue(L, obj);
|
||||
pushvec3(L, dir);
|
||||
rc = lua_pcall(L, 2, 1, 0);
|
||||
if (rc != LUA_OK)
|
||||
lua_error(L);
|
||||
checkvec3(L, -1, vec);
|
||||
lua_settop(L, t);
|
||||
#undef L
|
||||
}
|
||||
|
||||
static void Center(const void *obj_, vec3_t *center)
|
||||
{
|
||||
#define L moonccd_L
|
||||
int ref, rc;
|
||||
int obj = (ptrdiff_t)obj_;
|
||||
int t = lua_gettop(L);
|
||||
switch (obj)
|
||||
{
|
||||
case OBJ1:
|
||||
ref = Ud->ref[3];
|
||||
break;
|
||||
case OBJ2:
|
||||
ref = Ud->ref[4];
|
||||
break;
|
||||
default:
|
||||
unexpected(L);
|
||||
return;
|
||||
}
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
lua_pushvalue(L, obj);
|
||||
rc = lua_pcall(L, 1, 1, 0);
|
||||
if (rc != LUA_OK)
|
||||
lua_error(L);
|
||||
checkvec3(L, -1, center);
|
||||
lua_settop(L, t);
|
||||
#undef L
|
||||
}
|
||||
|
||||
static int GJKIntersect(lua_State *L)
|
||||
{
|
||||
ccd_t *ccd = checkccd(L, PAR, &Ud);
|
||||
luaL_checkany(L, OBJ1);
|
||||
luaL_checkany(L, OBJ2);
|
||||
lua_pushboolean(L, ccdGJKIntersect((void *)OBJ1, (void *)OBJ2, ccd));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GJKSeparate(lua_State *L)
|
||||
{
|
||||
int rc;
|
||||
vec3_t sep;
|
||||
ccd_t *ccd = checkccd(L, PAR, &Ud);
|
||||
luaL_checkany(L, OBJ1);
|
||||
luaL_checkany(L, OBJ2);
|
||||
rc = ccdGJKSeparate((void *)OBJ1, (void *)OBJ2, ccd, &sep);
|
||||
switch (rc)
|
||||
{
|
||||
case 0:
|
||||
lua_pushboolean(L, 1);
|
||||
pushvec3(L, &sep);
|
||||
return 2;
|
||||
case -1:
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
case -2:
|
||||
return errmemory(L);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return unexpected(L);
|
||||
}
|
||||
|
||||
static int GJKPenetration(lua_State *L)
|
||||
{
|
||||
int rc;
|
||||
double depth;
|
||||
vec3_t dir, pos;
|
||||
ccd_t *ccd = checkccd(L, PAR, &Ud);
|
||||
luaL_checkany(L, OBJ1);
|
||||
luaL_checkany(L, OBJ2);
|
||||
rc = ccdGJKPenetration((void *)OBJ1, (void *)OBJ2, ccd, &depth, &dir, &pos);
|
||||
switch (rc)
|
||||
{
|
||||
case 0:
|
||||
lua_pushboolean(L, 1);
|
||||
lua_pushnumber(L, depth);
|
||||
pushvec3(L, &dir);
|
||||
pushvec3(L, &pos);
|
||||
return 4;
|
||||
case -1:
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
case -2:
|
||||
return errmemory(L);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return unexpected(L);
|
||||
}
|
||||
|
||||
static int MPRIntersect(lua_State *L)
|
||||
{
|
||||
ccd_t *ccd = checkccd(L, PAR, &Ud);
|
||||
luaL_checkany(L, OBJ1);
|
||||
luaL_checkany(L, OBJ2);
|
||||
lua_pushboolean(L, ccdMPRIntersect((void *)OBJ1, (void *)OBJ2, ccd));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int MPRPenetration(lua_State *L)
|
||||
{
|
||||
int rc;
|
||||
double depth;
|
||||
vec3_t dir, pos;
|
||||
ccd_t *ccd = checkccd(L, PAR, &Ud);
|
||||
luaL_checkany(L, OBJ1);
|
||||
luaL_checkany(L, OBJ2);
|
||||
rc = ccdMPRPenetration((void *)OBJ1, (void *)OBJ2, ccd, &depth, &dir, &pos);
|
||||
switch (rc)
|
||||
{
|
||||
case 0:
|
||||
lua_pushboolean(L, 1);
|
||||
lua_pushnumber(L, depth);
|
||||
pushvec3(L, &dir);
|
||||
pushvec3(L, &pos);
|
||||
return 4;
|
||||
case -1:
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
case -2:
|
||||
return errmemory(L);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return unexpected(L);
|
||||
}
|
||||
|
||||
DESTROY_FUNC(ccd)
|
||||
|
||||
static const struct luaL_Reg Methods[] =
|
||||
{
|
||||
{"free", Destroy},
|
||||
{"gjk_intersect", GJKIntersect},
|
||||
{"gjk_separate", GJKSeparate},
|
||||
{"gjk_penetration", GJKPenetration},
|
||||
{"mpr_intersect", MPRIntersect},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static const struct luaL_Reg MetaMethods[] =
|
||||
{
|
||||
{"__gc", Destroy},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
static const struct luaL_Reg Functions[] =
|
||||
{
|
||||
{"new", New},
|
||||
{"free", Destroy},
|
||||
{"gjk_intersect", GJKIntersect},
|
||||
{"gjk_separate", GJKSeparate},
|
||||
{"gjk_penetration", GJKPenetration},
|
||||
{"mpr_intersect", MPRIntersect},
|
||||
{"mpr_penetration", MPRPenetration},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
void moonccd_open_ccd(lua_State *L)
|
||||
{
|
||||
udata_define(L, CCDPAR_MT, Methods, MetaMethods);
|
||||
luaL_setfuncs(L, Functions, 0);
|
||||
}
|
||||
@ -0,0 +1,324 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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 "internal.h"
|
||||
|
||||
#define GLMATH_COMPAT (tovec3 != LUA_NOREF)
|
||||
/* References to the MoonGLMATH functions used to convert pushed values to glmath objects. */
|
||||
static int tovec3 = LUA_NOREF;
|
||||
static int toquat = LUA_NOREF;
|
||||
|
||||
int isglmathcompat(void)
|
||||
{
|
||||
return GLMATH_COMPAT;
|
||||
}
|
||||
|
||||
int glmathcompat(lua_State *L, int on)
|
||||
{
|
||||
if (on)
|
||||
{
|
||||
if (GLMATH_COMPAT)
|
||||
return 0; /* already enabled */
|
||||
if (luaL_dostring(L, "return require('moonglmath').tovec3") != 0)
|
||||
lua_error(L);
|
||||
tovec3 = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
if (luaL_dostring(L, "return require('moonglmath').toquat") != 0)
|
||||
lua_error(L);
|
||||
toquat = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!GLMATH_COMPAT)
|
||||
return 0; /* already disabled */
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, tovec3);
|
||||
tovec3 = LUA_NOREF;
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, toquat);
|
||||
toquat = LUA_NOREF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* vec3_t ----------------------------------------------------------*/
|
||||
|
||||
int testvec3(lua_State *L, int arg, vec3_t *dst)
|
||||
{
|
||||
int isnum;
|
||||
int t = lua_type(L, arg);
|
||||
switch (t)
|
||||
{
|
||||
case LUA_TNONE:
|
||||
case LUA_TNIL:
|
||||
return ERR_NOTPRESENT;
|
||||
case LUA_TTABLE:
|
||||
break;
|
||||
default:
|
||||
return ERR_TABLE;
|
||||
}
|
||||
#define POP \
|
||||
if (!isnum) \
|
||||
{ \
|
||||
lua_pop(L, 1); \
|
||||
return ERR_VALUE; \
|
||||
} \
|
||||
lua_pop(L, 1);
|
||||
lua_rawgeti(L, arg, 1);
|
||||
dst->v[0] = lua_tonumberx(L, -1, &isnum);
|
||||
POP
|
||||
lua_rawgeti(L, arg, 2);
|
||||
dst->v[1] = lua_tonumberx(L, -1, &isnum);
|
||||
POP
|
||||
lua_rawgeti(L, arg, 3);
|
||||
dst->v[2] = lua_tonumberx(L, -1, &isnum);
|
||||
POP
|
||||
#undef POP
|
||||
return 0;
|
||||
}
|
||||
|
||||
int optvec3(lua_State *L, int arg, vec3_t *dst)
|
||||
{
|
||||
int ec = testvec3(L, arg, dst);
|
||||
if (ec < 0)
|
||||
return argerror(L, arg, ec);
|
||||
return ec;
|
||||
}
|
||||
|
||||
int checkvec3(lua_State *L, int arg, vec3_t *dst)
|
||||
{
|
||||
int ec = testvec3(L, arg, dst);
|
||||
if (ec)
|
||||
return argerror(L, arg, ec);
|
||||
return ec;
|
||||
}
|
||||
|
||||
void pushvec3(lua_State *L, const vec3_t *val)
|
||||
{
|
||||
if (GLMATH_COMPAT)
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, tovec3);
|
||||
lua_newtable(L);
|
||||
lua_pushnumber(L, val->v[0]);
|
||||
lua_rawseti(L, -2, 1);
|
||||
lua_pushnumber(L, val->v[1]);
|
||||
lua_rawseti(L, -2, 2);
|
||||
lua_pushnumber(L, val->v[2]);
|
||||
lua_rawseti(L, -2, 3);
|
||||
if (GLMATH_COMPAT && lua_pcall(L, 1, 1, 0) != LUA_OK)
|
||||
{
|
||||
unexpected(L);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vec3_t *checkvec3list(lua_State *L, int arg, int *countp, int *err)
|
||||
/* Check if the value at arg is a table of vecs and returns the corresponding
|
||||
* array of vec3_t, stroing the size in *countp. The array is Malloc()'d and the
|
||||
* caller is in charge of Free()ing it.
|
||||
* If err=NULL, raises an error on failure, otherwise returns NULL and stores
|
||||
* the ERR_XXX code in *err.
|
||||
*/
|
||||
{
|
||||
int count, i;
|
||||
vec3_t *dst = NULL;
|
||||
*countp = 0;
|
||||
#define ERR(ec) \
|
||||
do \
|
||||
{ \
|
||||
if (err) \
|
||||
*err = (ec); \
|
||||
else \
|
||||
argerror(L, arg, (ec)); \
|
||||
return NULL; \
|
||||
} while (0)
|
||||
if (lua_isnoneornil(L, arg))
|
||||
ERR(ERR_NOTPRESENT);
|
||||
if (lua_type(L, arg) != LUA_TTABLE)
|
||||
ERR(ERR_TABLE);
|
||||
|
||||
count = luaL_len(L, arg);
|
||||
if (count == 0)
|
||||
ERR(ERR_EMPTY);
|
||||
dst = MallocNoErr(L, count * sizeof(vec3_t));
|
||||
if (!dst)
|
||||
ERR(ERR_MEMORY);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
lua_rawgeti(L, arg, i + 1);
|
||||
if (testvec3(L, -1, &dst[i]) != 0)
|
||||
{
|
||||
Free(L, dst);
|
||||
ERR(ERR_TYPE);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
#undef ERR
|
||||
*countp = count;
|
||||
if (err)
|
||||
*err = 0;
|
||||
return dst;
|
||||
}
|
||||
|
||||
void pushvec3list(lua_State *L, const vec3_t *vecs, int count)
|
||||
{
|
||||
int i;
|
||||
lua_newtable(L);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
pushvec3(L, &vecs[i]);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* quat_t ----------------------------------------------------------*/
|
||||
|
||||
int testquat(lua_State *L, int arg, quat_t *dst)
|
||||
{
|
||||
int isnum;
|
||||
int t = lua_type(L, arg);
|
||||
switch (t)
|
||||
{
|
||||
case LUA_TNONE:
|
||||
case LUA_TNIL:
|
||||
return ERR_NOTPRESENT;
|
||||
case LUA_TTABLE:
|
||||
break;
|
||||
default:
|
||||
return ERR_TABLE;
|
||||
}
|
||||
#define POP \
|
||||
if (!isnum) \
|
||||
{ \
|
||||
lua_pop(L, 1); \
|
||||
return ERR_VALUE; \
|
||||
} \
|
||||
lua_pop(L, 1);
|
||||
lua_rawgeti(L, arg, 1);
|
||||
dst->q[3] = lua_tonumberx(L, -1, &isnum);
|
||||
POP // w
|
||||
lua_rawgeti(L, arg, 2);
|
||||
dst->q[0] = lua_tonumberx(L, -1, &isnum);
|
||||
POP // x
|
||||
lua_rawgeti(L, arg, 3);
|
||||
dst->q[1] = lua_tonumberx(L, -1, &isnum);
|
||||
POP // y
|
||||
lua_rawgeti(L, arg, 4);
|
||||
dst->q[2] = lua_tonumberx(L, -1, &isnum);
|
||||
POP // z
|
||||
#undef POP
|
||||
return 0;
|
||||
}
|
||||
|
||||
int optquat(lua_State *L, int arg, quat_t *dst)
|
||||
{
|
||||
int ec = testquat(L, arg, dst);
|
||||
if (ec < 0)
|
||||
return argerror(L, arg, ec);
|
||||
return ec;
|
||||
}
|
||||
|
||||
int checkquat(lua_State *L, int arg, quat_t *dst)
|
||||
{
|
||||
int ec = testquat(L, arg, dst);
|
||||
if (ec)
|
||||
return argerror(L, arg, ec);
|
||||
return ec;
|
||||
}
|
||||
|
||||
void pushquat(lua_State *L, const quat_t *val)
|
||||
{
|
||||
if (GLMATH_COMPAT)
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, toquat);
|
||||
lua_newtable(L);
|
||||
lua_pushnumber(L, val->q[3]);
|
||||
lua_rawseti(L, -2, 1); // w
|
||||
lua_pushnumber(L, val->q[0]);
|
||||
lua_rawseti(L, -2, 2); // x
|
||||
lua_pushnumber(L, val->q[1]);
|
||||
lua_rawseti(L, -2, 3); // y
|
||||
lua_pushnumber(L, val->q[2]);
|
||||
lua_rawseti(L, -2, 4); // z
|
||||
if (GLMATH_COMPAT && lua_pcall(L, 1, 1, 0) != LUA_OK)
|
||||
{
|
||||
unexpected(L);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
quat_t *checkquatlist(lua_State *L, int arg, int *countp, int *err)
|
||||
/* Check if the value at arg is a table of vecs and returns the corresponding
|
||||
* array of quat_t, stroing the size in *countp. The array is Malloc()'d and the
|
||||
* caller is in charge of Free()ing it.
|
||||
* If err=NULL, raises an error on failure, otherwise returns NULL and stores
|
||||
* the ERR_XXX code in *err.
|
||||
*/
|
||||
{
|
||||
int count, i;
|
||||
quat_t *dst = NULL;
|
||||
*countp = 0;
|
||||
#define ERR(ec) \
|
||||
do \
|
||||
{ \
|
||||
if (err) \
|
||||
*err = (ec); \
|
||||
else \
|
||||
argerror(L, arg, (ec)); \
|
||||
return NULL; \
|
||||
} while (0)
|
||||
if (lua_isnoneornil(L, arg))
|
||||
ERR(ERR_NOTPRESENT);
|
||||
if (lua_type(L, arg) != LUA_TTABLE)
|
||||
ERR(ERR_TABLE);
|
||||
|
||||
count = luaL_len(L, arg);
|
||||
if (count == 0)
|
||||
ERR(ERR_EMPTY);
|
||||
dst = MallocNoErr(L, count * sizeof(quat_t));
|
||||
if (!dst)
|
||||
ERR(ERR_MEMORY);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
lua_rawgeti(L, arg, i + 1);
|
||||
if (testquat(L, -1, &dst[i]) != 0)
|
||||
{
|
||||
Free(L, dst);
|
||||
ERR(ERR_TYPE);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
#undef ERR
|
||||
*countp = count;
|
||||
if (err)
|
||||
*err = 0;
|
||||
return dst;
|
||||
}
|
||||
|
||||
void pushquatlist(lua_State *L, const quat_t *vecs, int count)
|
||||
{
|
||||
int i;
|
||||
lua_newtable(L);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
pushquat(L, &vecs[i]);
|
||||
lua_rawseti(L, -2, i + 1);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,238 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef internalDEFINED
|
||||
#define internalDEFINED
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "moonccd.h"
|
||||
|
||||
#define TOSTR_(x) #x
|
||||
#define TOSTR(x) TOSTR_(x)
|
||||
|
||||
#include "tree.h"
|
||||
#include "objects.h"
|
||||
|
||||
/* Note: all the dynamic symbols of this library (should) start with 'moonccd_' .
|
||||
* The only exception is the luaopen_moonccd() function, which is searched for
|
||||
* with that name by Lua.
|
||||
* MoonCCD's string references on the Lua registry also start with 'moonccd_'.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
/* .c */
|
||||
#define moonccd_
|
||||
#endif
|
||||
|
||||
/* flags.c */
|
||||
#define checkflags(L, arg) luaL_checkinteger((L), (arg))
|
||||
#define optflags(L, arg, defval) luaL_optinteger((L), (arg), (defval))
|
||||
#define pushflags(L, val) lua_pushinteger((L), (val))
|
||||
|
||||
/* utils.c */
|
||||
void moonccd_utils_init(lua_State *L);
|
||||
#define copytable moonccd_copytable
|
||||
int copytable(lua_State *L);
|
||||
#define noprintf moonccd_noprintf
|
||||
int noprintf(const char *fmt, ...);
|
||||
#define now moonccd_now
|
||||
double now(void);
|
||||
#define sleeep moonccd_sleeep
|
||||
void sleeep(double seconds);
|
||||
#define since(t) (now() - (t))
|
||||
#define notavailable moonccd_notavailable
|
||||
int notavailable(lua_State *L, ...);
|
||||
#define Malloc moonccd_Malloc
|
||||
void *Malloc(lua_State *L, size_t size);
|
||||
#define MallocNoErr moonccd_MallocNoErr
|
||||
void *MallocNoErr(lua_State *L, size_t size);
|
||||
#define Strdup moonccd_Strdup
|
||||
char *Strdup(lua_State *L, const char *s);
|
||||
#define Free moonccd_Free
|
||||
void Free(lua_State *L, void *ptr);
|
||||
#define checkboolean moonccd_checkboolean
|
||||
int checkboolean(lua_State *L, int arg);
|
||||
#define testboolean moonccd_testboolean
|
||||
int testboolean(lua_State *L, int arg, int *err);
|
||||
#define optboolean moonccd_optboolean
|
||||
int optboolean(lua_State *L, int arg, int d);
|
||||
#define checklightuserdata moonccd_checklightuserdata
|
||||
void *checklightuserdata(lua_State *L, int arg);
|
||||
#define checklightuserdataorzero moonccd_checklightuserdataorzero
|
||||
void *checklightuserdataorzero(lua_State *L, int arg);
|
||||
#define optlightuserdata moonccd_optlightuserdata
|
||||
void *optlightuserdata(lua_State *L, int arg);
|
||||
#define testindex moonccd_testindex
|
||||
int testindex(lua_State *L, int arg, int *err);
|
||||
#define checkindex moonccd_checkindex
|
||||
int checkindex(lua_State *L, int arg);
|
||||
#define optindex moonccd_optindex
|
||||
int optindex(lua_State *L, int arg, int optval);
|
||||
#define pushindex moonccd_pushindex
|
||||
void pushindex(lua_State *L, int val);
|
||||
|
||||
/* datastructs.c */
|
||||
#define isglmathcompat moonccd_isglmathcompat
|
||||
int isglmathcompat(void);
|
||||
#define glmathcompat moonccd_glmathcompat
|
||||
int glmathcompat(lua_State *L, int on);
|
||||
#define testvec3 moonccd_testvec3
|
||||
int testvec3(lua_State *L, int arg, vec3_t *dst);
|
||||
#define optvec3 moonccd_optvec3
|
||||
int optvec3(lua_State *L, int arg, vec3_t *dst);
|
||||
#define checkvec3 moonccd_checkvec3
|
||||
int checkvec3(lua_State *L, int arg, vec3_t *dst);
|
||||
#define pushvec3 moonccd_pushvec3
|
||||
void pushvec3(lua_State *L, const vec3_t *val);
|
||||
#define checkvec3list moonccd_checkvec3list
|
||||
vec3_t *checkvec3list(lua_State *L, int arg, int *countp, int *err);
|
||||
#define pushvec3list moonccd_pushvec3list
|
||||
void pushvec3list(lua_State *L, const vec3_t *vecs, int count);
|
||||
#define testquat moonccd_testquat
|
||||
int testquat(lua_State *L, int arg, quat_t *dst);
|
||||
#define optquat moonccd_optquat
|
||||
int optquat(lua_State *L, int arg, quat_t *dst);
|
||||
#define checkquat moonccd_checkquat
|
||||
int checkquat(lua_State *L, int arg, quat_t *dst);
|
||||
#define pushquat moonccd_pushquat
|
||||
void pushquat(lua_State *L, const quat_t *val);
|
||||
#define checkquatlist moonccd_checkquatlist
|
||||
quat_t *checkquatlist(lua_State *L, int arg, int *countp, int *err);
|
||||
#define pushquatlist moonccd_pushquatlist
|
||||
void pushquatlist(lua_State *L, const quat_t *vecs, int count);
|
||||
|
||||
/* Internal error codes */
|
||||
#define ERR_NOTPRESENT 1
|
||||
#define ERR_SUCCESS 0
|
||||
#define ERR_GENERIC -1
|
||||
#define ERR_TYPE -2
|
||||
#define ERR_ELEMTYPE -3
|
||||
#define ERR_VALUE -4
|
||||
#define ERR_ELEMVALUE -5
|
||||
#define ERR_TABLE -6
|
||||
#define ERR_FUNCTION -7
|
||||
#define ERR_EMPTY -8
|
||||
#define ERR_MEMORY -9
|
||||
#define ERR_MALLOC_ZERO -10
|
||||
#define ERR_LENGTH -11
|
||||
#define ERR_POOL -12
|
||||
#define ERR_BOUNDARIES -13
|
||||
#define ERR_RANGE -14
|
||||
#define ERR_FOPEN -15
|
||||
#define ERR_OPERATION -16
|
||||
#define ERR_UNKNOWN -17
|
||||
#define errstring moonccd_errstring
|
||||
const char *errstring(int err);
|
||||
|
||||
/* tracing.c */
|
||||
#define trace_objects moonccd_trace_objects
|
||||
extern int trace_objects;
|
||||
|
||||
/* main.c */
|
||||
extern lua_State *moonccd_L;
|
||||
int luaopen_moonccd(lua_State *L);
|
||||
void moonccd_open_tracing(lua_State *L);
|
||||
void moonccd_open_misc(lua_State *L);
|
||||
void moonccd_open_ccd(lua_State *L);
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Debug and other utilities |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
/* If this is printed, it denotes a suspect bug: */
|
||||
#define UNEXPECTED_ERROR "unexpected error (%s, %d)", __FILE__, __LINE__
|
||||
#define unexpected(L) luaL_error((L), UNEXPECTED_ERROR)
|
||||
|
||||
/* Errors with internal error code (ERR_XXX) */
|
||||
#define failure(L, errcode) luaL_error((L), errstring((errcode)))
|
||||
#define argerror(L, arg, errcode) luaL_argerror((L), (arg), errstring((errcode)))
|
||||
#define errmemory(L) luaL_error((L), errstring((ERR_MEMORY)))
|
||||
|
||||
#define notsupported(L) luaL_error((L), "operation not supported")
|
||||
#define badvalue(L, s) lua_pushfstring((L), "invalid value '%s'", (s))
|
||||
|
||||
/* Reference/unreference variables on the Lua registry */
|
||||
#define Unreference(L, ref) \
|
||||
do \
|
||||
{ \
|
||||
if ((ref) != LUA_NOREF) \
|
||||
{ \
|
||||
luaL_unref((L), LUA_REGISTRYINDEX, (ref)); \
|
||||
(ref) = LUA_NOREF; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define Reference(L, arg, ref) \
|
||||
do \
|
||||
{ \
|
||||
Unreference((L), (ref)); \
|
||||
lua_pushvalue(L, (arg)); \
|
||||
(ref) = luaL_ref(L, LUA_REGISTRYINDEX); \
|
||||
} while (0)
|
||||
|
||||
/* DEBUG -------------------------------------------------------- */
|
||||
#if defined(DEBUG)
|
||||
|
||||
#define DBG printf
|
||||
#define TR() \
|
||||
do \
|
||||
{ \
|
||||
printf("trace %s %d\n", __FILE__, __LINE__); \
|
||||
} while (0)
|
||||
#define BK() \
|
||||
do \
|
||||
{ \
|
||||
printf("break %s %d\n", __FILE__, __LINE__); \
|
||||
getchar(); \
|
||||
} while (0)
|
||||
#define TSTART double ts = now();
|
||||
#define TSTOP \
|
||||
do \
|
||||
{ \
|
||||
ts = since(ts); \
|
||||
ts = ts * 1e6; \
|
||||
printf("%s %d %.3f us\n", __FILE__, __LINE__, ts); \
|
||||
ts = now(); \
|
||||
} while (0);
|
||||
|
||||
#else
|
||||
|
||||
#define DBG noprintf
|
||||
#define TR()
|
||||
#define BK()
|
||||
#define TSTART \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define TSTOP \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
|
||||
#endif /* DEBUG ------------------------------------------------- */
|
||||
|
||||
#endif /* internalDEFINED */
|
||||
@ -0,0 +1,106 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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 "internal.h"
|
||||
|
||||
// https://github.com/stetre/moonccd
|
||||
|
||||
lua_State *moonccd_L;
|
||||
|
||||
static void AtExit(void)
|
||||
{
|
||||
if (moonccd_L)
|
||||
{
|
||||
moonccd_L = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int AddVersions(lua_State *L)
|
||||
{
|
||||
lua_pushstring(L, "_VERSION");
|
||||
lua_pushstring(L, "MoonCCD " MOONCCD_VERSION);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "_LIBCCD_VERSION");
|
||||
lua_pushfstring(L, "libccd %s", VERSION);
|
||||
lua_settable(L, -3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int AddConstants(lua_State *L)
|
||||
{
|
||||
lua_pushnumber(L, CCD_EPS);
|
||||
lua_setfield(L, -2, "EPS");
|
||||
|
||||
lua_pushnumber(L, CCD_REAL_MAX);
|
||||
lua_setfield(L, -2, "REAL_MAX");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int IsGlmathCompat(lua_State *L)
|
||||
{
|
||||
lua_pushboolean(L, isglmathcompat());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int GlmathCompat(lua_State *L)
|
||||
{
|
||||
int on = checkboolean(L, 1);
|
||||
glmathcompat(L, on);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg Functions[] =
|
||||
{
|
||||
{"is_glmath_compat", IsGlmathCompat},
|
||||
{"glmath_compat", GlmathCompat},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
int luaopen_moonccd(lua_State *L)
|
||||
/* Lua calls this function to load the module */
|
||||
{
|
||||
moonccd_L = L;
|
||||
|
||||
moonccd_utils_init(L);
|
||||
atexit(AtExit);
|
||||
|
||||
lua_newtable(L); /* the module table */
|
||||
AddVersions(L);
|
||||
AddConstants(L);
|
||||
luaL_setfuncs(L, Functions, 0);
|
||||
moonccd_open_tracing(L);
|
||||
moonccd_open_misc(L);
|
||||
moonccd_open_ccd(L);
|
||||
|
||||
// #if 0 //@@
|
||||
// /* Add functions implemented in Lua */
|
||||
// lua_pushvalue(L, -1); lua_setglobal(L, "moonccd");
|
||||
// if(luaL_dostring(L, "require('moonccd.datastructs')") != 0) lua_error(L);
|
||||
// lua_pushnil(L); lua_setglobal(L, "moonccd");
|
||||
// #endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -0,0 +1,311 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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 "internal.h"
|
||||
|
||||
static int Origin(lua_State *L)
|
||||
{
|
||||
pushvec3(L, ccd_vec3_origin);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Points_on_sphere(lua_State *L)
|
||||
{
|
||||
pushvec3list(L, ccd_points_on_sphere, ccd_points_on_sphere_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Sign(lua_State *L)
|
||||
{
|
||||
double val = luaL_checknumber(L, 1);
|
||||
lua_pushinteger(L, ccdSign(val));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int IsZero(lua_State *L)
|
||||
{
|
||||
double val = luaL_checknumber(L, 1);
|
||||
lua_pushboolean(L, ccdIsZero(val));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Eq(lua_State *L)
|
||||
{
|
||||
double a = luaL_checknumber(L, 1);
|
||||
double b = luaL_checknumber(L, 2);
|
||||
lua_pushboolean(L, ccdEq(a, b));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Vec3Eq(lua_State *L)
|
||||
{
|
||||
vec3_t a, b;
|
||||
checkvec3(L, 1, &a);
|
||||
checkvec3(L, 2, &b);
|
||||
lua_pushboolean(L, ccdVec3Eq(&a, &b));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Vec3Len2(lua_State *L)
|
||||
{
|
||||
vec3_t v;
|
||||
checkvec3(L, 1, &v);
|
||||
lua_pushnumber(L, ccdVec3Len2(&v));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Vec3Dist2(lua_State *L)
|
||||
{
|
||||
vec3_t a, b;
|
||||
checkvec3(L, 1, &a);
|
||||
checkvec3(L, 2, &b);
|
||||
lua_pushnumber(L, ccdVec3Dist2(&a, &b));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Vec3Copy(lua_State *L)
|
||||
{
|
||||
vec3_t v;
|
||||
checkvec3(L, 1, &v);
|
||||
// ccdVec3Copy(&v, &w);
|
||||
pushvec3(L, &v);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Vec3Sub(lua_State *L)
|
||||
{
|
||||
vec3_t a, b;
|
||||
checkvec3(L, 1, &a);
|
||||
checkvec3(L, 2, &b);
|
||||
ccdVec3Sub(&a, &b);
|
||||
pushvec3(L, &a);
|
||||
return 1; // a - b
|
||||
}
|
||||
|
||||
static int Vec3Add(lua_State *L)
|
||||
{
|
||||
vec3_t a, b;
|
||||
checkvec3(L, 1, &a);
|
||||
checkvec3(L, 2, &b);
|
||||
ccdVec3Add(&a, &b);
|
||||
pushvec3(L, &a);
|
||||
return 1; // a + b
|
||||
}
|
||||
|
||||
static int Vec3Scale(lua_State *L)
|
||||
{
|
||||
vec3_t v;
|
||||
double k = luaL_checknumber(L, 2);
|
||||
checkvec3(L, 1, &v);
|
||||
ccdVec3Scale(&v, k); /* d = d * k; */
|
||||
pushvec3(L, &v);
|
||||
return 1; // d*v
|
||||
}
|
||||
|
||||
static int Vec3Normalize(lua_State *L)
|
||||
{
|
||||
vec3_t v;
|
||||
checkvec3(L, 1, &v);
|
||||
ccdVec3Normalize(&v);
|
||||
pushvec3(L, &v);
|
||||
return 1; // v/|v|
|
||||
}
|
||||
|
||||
static int Vec3Dot(lua_State *L)
|
||||
{
|
||||
vec3_t a, b;
|
||||
checkvec3(L, 1, &a);
|
||||
checkvec3(L, 2, &b);
|
||||
lua_pushnumber(L, ccdVec3Dot(&a, &b));
|
||||
return 1; // a·b
|
||||
}
|
||||
|
||||
static int Vec3Cross(lua_State *L)
|
||||
{
|
||||
vec3_t a, b, c;
|
||||
checkvec3(L, 1, &a);
|
||||
checkvec3(L, 2, &b);
|
||||
ccdVec3Cross(&c, &a, &b);
|
||||
pushvec3(L, &c);
|
||||
return 1; // a x b
|
||||
}
|
||||
|
||||
static int Vec3PointSegmentDist2(lua_State *L)
|
||||
{
|
||||
vec3_t P, a, b, witness;
|
||||
checkvec3(L, 1, &P);
|
||||
checkvec3(L, 2, &a);
|
||||
checkvec3(L, 3, &b);
|
||||
lua_pushnumber(L, ccdVec3PointSegmentDist2(&P, &a, &b, &witness));
|
||||
pushvec3(L, &witness);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int Vec3PointTriDist2(lua_State *L)
|
||||
{
|
||||
vec3_t P, a, b, c, witness;
|
||||
checkvec3(L, 1, &P);
|
||||
checkvec3(L, 2, &a);
|
||||
checkvec3(L, 3, &b);
|
||||
checkvec3(L, 4, &c);
|
||||
lua_pushnumber(L, ccdVec3PointTriDist2(&P, &a, &b, &c, &witness));
|
||||
pushvec3(L, &witness);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int QuatLen2(lua_State *L)
|
||||
{
|
||||
quat_t q;
|
||||
checkquat(L, 1, &q);
|
||||
lua_pushnumber(L, ccdQuatLen2(&q));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int QuatLen(lua_State *L)
|
||||
{
|
||||
quat_t q;
|
||||
checkquat(L, 1, &q);
|
||||
lua_pushnumber(L, ccdQuatLen(&q));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int QuatCopy(lua_State *L)
|
||||
{
|
||||
quat_t q;
|
||||
checkquat(L, 1, &q);
|
||||
// ccdQuatCopy(&dst, q);
|
||||
pushquat(L, &q);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int QuatNormalize(lua_State *L)
|
||||
{
|
||||
int rc;
|
||||
quat_t q;
|
||||
checkquat(L, 1, &q);
|
||||
rc = ccdQuatNormalize(&q);
|
||||
if (rc != 0)
|
||||
return argerror(L, 1, ERR_LENGTH);
|
||||
pushquat(L, &q);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int QuatSetAngleAxis(lua_State *L)
|
||||
{
|
||||
quat_t q;
|
||||
vec3_t axis;
|
||||
double angle = luaL_checknumber(L, 1);
|
||||
checkvec3(L, 2, &axis);
|
||||
ccdQuatSetAngleAxis(&q, angle, &axis);
|
||||
pushquat(L, &q);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int QuatScale(lua_State *L)
|
||||
{
|
||||
quat_t q;
|
||||
double k = luaL_checknumber(L, 2);
|
||||
checkquat(L, 1, &q);
|
||||
ccdQuatScale(&q, k);
|
||||
pushquat(L, &q);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int QuatMul(lua_State *L)
|
||||
{
|
||||
quat_t q, q1;
|
||||
checkquat(L, 1, &q);
|
||||
checkquat(L, 2, &q1);
|
||||
ccdQuatMul(&q, &q1);
|
||||
pushquat(L, &q);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int QuatInvert(lua_State *L)
|
||||
{
|
||||
int rc;
|
||||
quat_t q;
|
||||
checkquat(L, 1, &q);
|
||||
rc = ccdQuatInvert(&q);
|
||||
if (rc != 0)
|
||||
return argerror(L, 1, ERR_LENGTH);
|
||||
pushquat(L, &q);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int QuatRotVec(lua_State *L)
|
||||
{
|
||||
vec3_t v;
|
||||
quat_t q;
|
||||
checkvec3(L, 1, &v);
|
||||
checkquat(L, 2, &q);
|
||||
ccdQuatRotVec(&v, &q);
|
||||
pushvec3(L, &v);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg Functions[] =
|
||||
{
|
||||
{"origin", Origin},
|
||||
{"points_on_sphere", Points_on_sphere},
|
||||
{"sign", Sign},
|
||||
{"is_zero", IsZero},
|
||||
{"eq", Eq},
|
||||
// { "vx", Vec3X }, --> x, y, z = v[1], v[2], v[3]
|
||||
// { "vy", Vec3Y },
|
||||
// { "vz", Vec3Z },
|
||||
{"veq", Vec3Eq},
|
||||
{"vlen2", Vec3Len2},
|
||||
{"vdist2", Vec3Dist2},
|
||||
// { "vset", Vec3Set }, --> v = { x, y, z }
|
||||
{"vcopy", Vec3Copy},
|
||||
{"vsub", Vec3Sub},
|
||||
{"vadd", Vec3Add},
|
||||
// { "vsub2", Vec3Sub2 },
|
||||
{"vscale", Vec3Scale},
|
||||
{"vnormalize", Vec3Normalize},
|
||||
{"vdot", Vec3Dot},
|
||||
{"vcross", Vec3Cross},
|
||||
{"point_segment_dist2", Vec3PointSegmentDist2},
|
||||
{"point_triangle_dist2", Vec3PointTriDist2},
|
||||
{"qlen2", QuatLen2},
|
||||
{"qlen", QuatLen},
|
||||
// { "qset", QuatSet }, --> q = { w, x, y, z }
|
||||
{"qcopy", QuatCopy},
|
||||
{"qnormalize", QuatNormalize},
|
||||
{"qset_angle_axis", QuatSetAngleAxis},
|
||||
{"qscale", QuatScale},
|
||||
{"qmul", QuatMul},
|
||||
// { "qmul2", QuatMul2 },
|
||||
{"qinvert", QuatInvert},
|
||||
// { "qinvert2", QuatInvert2 },
|
||||
{"vrotate", QuatRotVec},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
void moonccd_open_misc(lua_State *L)
|
||||
{
|
||||
luaL_setfuncs(L, Functions, 0);
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef moonccdDEFINED
|
||||
#define moonccdDEFINED
|
||||
|
||||
#include <lua.h>
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include <ccd/config.h>
|
||||
#include <ccd/ccd.h>
|
||||
#include <ccd/vec3.h>
|
||||
#include <ccd/quat.h>
|
||||
|
||||
#ifndef CCD_DOUBLE
|
||||
#error("MoonCCD requires CCD_DOUBLE")
|
||||
#endif
|
||||
|
||||
#define MOONCCD_VERSION "0.1"
|
||||
#ifndef VERSION /* @@ defined in ccd/config.h, but not n the version installed on Ubuntu */
|
||||
#define VERSION "2.0"
|
||||
#endif
|
||||
|
||||
#endif /* moonccdDEFINED */
|
||||
@ -0,0 +1,216 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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 "internal.h"
|
||||
|
||||
ud_t *newuserdata(lua_State *L, void *handle, const char *mt, const char *tracename)
|
||||
{
|
||||
ud_t *ud;
|
||||
/* we use handle as search key */
|
||||
ud = (ud_t *)udata_new(L, sizeof(ud_t), (uint64_t)(uintptr_t)handle, mt);
|
||||
memset(ud, 0, sizeof(ud_t));
|
||||
ud->handle = handle;
|
||||
MarkValid(ud);
|
||||
if (trace_objects)
|
||||
printf("create %s %p (%p)\n", tracename, (void *)ud, handle);
|
||||
return ud;
|
||||
}
|
||||
|
||||
int freeuserdata(lua_State *L, ud_t *ud, const char *tracename)
|
||||
{
|
||||
/* The 'Valid' mark prevents double calls when an object is explicitly destroyed,
|
||||
* and subsequently deleted also by the GC (the ud sticks around until the GC
|
||||
* collects it, so we mark it as invalid when the object is explicitly destroyed
|
||||
* by the script, or implicitly destroyed because child of a destroyed object). */
|
||||
int i;
|
||||
if (!IsValid(ud))
|
||||
return 0;
|
||||
CancelValid(ud);
|
||||
if (ud->info)
|
||||
Free(L, ud->info);
|
||||
for (i = 0; i < 6; i++)
|
||||
if (ud->ref[i] != LUA_NOREF)
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, ud->ref[i]);
|
||||
if (trace_objects)
|
||||
printf("delete %s %p (%p)\n", tracename, (void *)ud, ud->handle);
|
||||
udata_free(L, (uint64_t)(uintptr_t)ud->handle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int freeifchild(lua_State *L, const void *mem, const char *mt, const void *parent_ud)
|
||||
/* callback for udata_scan */
|
||||
{
|
||||
ud_t *ud = (ud_t *)mem;
|
||||
(void)mt;
|
||||
if (IsValid(ud) && (ud->parent_ud == parent_ud))
|
||||
ud->destructor(L, ud);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int freechildren(lua_State *L, const char *mt, ud_t *parent_ud)
|
||||
/* calls the self destructor for all 'mt' objects that are children of the given parent_ud */
|
||||
{
|
||||
return udata_scan(L, mt, parent_ud, freeifchild);
|
||||
}
|
||||
|
||||
int pushuserdata(lua_State *L, ud_t *ud)
|
||||
{
|
||||
if (!IsValid(ud))
|
||||
return unexpected(L);
|
||||
return udata_push(L, (uint64_t)(uintptr_t)ud->handle);
|
||||
}
|
||||
|
||||
ud_t *userdata(const void *handle)
|
||||
{
|
||||
ud_t *ud = (ud_t *)udata_mem((uint64_t)(uintptr_t)handle);
|
||||
if (ud && IsValid(ud))
|
||||
return ud;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *testxxx(lua_State *L, int arg, ud_t **udp, const char *mt)
|
||||
{
|
||||
ud_t *ud = (ud_t *)udata_test(L, arg, mt);
|
||||
if (ud && IsValid(ud))
|
||||
{
|
||||
if (udp)
|
||||
*udp = ud;
|
||||
return ud->handle;
|
||||
}
|
||||
if (udp)
|
||||
*udp = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void *testoneofxxx(lua_State *L, int arg, ud_t **udp, char **mtp)
|
||||
{
|
||||
void *handle = NULL;
|
||||
int i = 0;
|
||||
char *mt = NULL;
|
||||
while((mt = mtp[i++]) != NULL)
|
||||
{
|
||||
handle = testxxx(L, arg, udp, mt);
|
||||
if(handle) return handle;
|
||||
}
|
||||
if(udp) *udp = NULL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void *checkxxx(lua_State *L, int arg, ud_t **udp, const char *mt)
|
||||
{
|
||||
ud_t *ud = (ud_t *)udata_test(L, arg, mt);
|
||||
if (ud && IsValid(ud))
|
||||
{
|
||||
if (udp)
|
||||
*udp = ud;
|
||||
return ud->handle;
|
||||
}
|
||||
lua_pushfstring(L, "not a %s", mt);
|
||||
luaL_argerror(L, arg, lua_tostring(L, -1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *optxxx(lua_State *L, int arg, ud_t **udp, const char *mt)
|
||||
/* This differs from testxxx in that it fails in case the argument is
|
||||
* something different than either none/nil or the expected userdata type
|
||||
*/
|
||||
{
|
||||
if (lua_isnoneornil(L, arg))
|
||||
{
|
||||
if (udp)
|
||||
*udp = NULL;
|
||||
return 0;
|
||||
}
|
||||
return checkxxx(L, arg, udp, mt);
|
||||
}
|
||||
|
||||
int pushxxx(lua_State *L, void *handle)
|
||||
{
|
||||
return udata_push(L, (uint64_t)(uintptr_t)handle);
|
||||
}
|
||||
|
||||
void **checkxxxlist(lua_State *L, int arg, int *count, int *err, const char *mt)
|
||||
/* xxx* checkxxxlist(lua_State *L, int arg, int *count, int *err)
|
||||
* Checks if the variable at arg on the Lua stack is a list of xxx objects.
|
||||
* On success, returns an array of xxx handles and sets its length in *count.
|
||||
* The array s Malloc'd and must be released by the caller using Free(L, ...).
|
||||
* On error, sets *err to ERR_XXX, *count to 0, and returns NULL.
|
||||
*/
|
||||
{
|
||||
void **list;
|
||||
int i;
|
||||
|
||||
*count = 0;
|
||||
*err = 0;
|
||||
if (lua_isnoneornil(L, arg))
|
||||
{
|
||||
*err = ERR_NOTPRESENT;
|
||||
return NULL;
|
||||
}
|
||||
if (lua_type(L, arg) != LUA_TTABLE)
|
||||
{
|
||||
*err = ERR_TABLE;
|
||||
return NULL;
|
||||
}
|
||||
*count = luaL_len(L, arg);
|
||||
if (*count == 0)
|
||||
{
|
||||
*err = ERR_EMPTY;
|
||||
return NULL;
|
||||
}
|
||||
list = (void **)MallocNoErr(L, sizeof(void *) * (*count));
|
||||
|
||||
if (!list)
|
||||
{
|
||||
*count = 0;
|
||||
*err = ERR_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < *count; i++)
|
||||
{
|
||||
lua_rawgeti(L, arg, i + 1);
|
||||
list[i] = (void *)(uintptr_t)testxxx(L, -1, NULL, mt);
|
||||
if (!list[i])
|
||||
{
|
||||
Free(L, list);
|
||||
*count = 0;
|
||||
*err = ERR_TYPE;
|
||||
return NULL;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
int setmetatable(lua_State *L, const char *mt)
|
||||
/* Sets the metatable of the table on top of the stack */
|
||||
{
|
||||
luaL_getmetatable(L, mt);
|
||||
lua_setmetatable(L, -2);
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef objectsDEFINED
|
||||
#define objectsDEFINED
|
||||
|
||||
#include "tree.h"
|
||||
#include "udata.h"
|
||||
|
||||
/* libccd renaming (for internal use) */
|
||||
#define vec3_t ccd_vec3_t
|
||||
#define quat_t ccd_quat_t
|
||||
#define real_t ccd_real_t
|
||||
|
||||
/* Objects' metatable names */
|
||||
#define CCDPAR_MT "moonccd_ccdpar" /* ccd_t */
|
||||
|
||||
/* Userdata memory associated with objects */
|
||||
#define ud_t moonccd_ud_t
|
||||
typedef struct moonccd_ud_s ud_t;
|
||||
|
||||
struct moonccd_ud_s
|
||||
{
|
||||
void *handle; /* the object handle bound to this userdata */
|
||||
int (*destructor)(lua_State *L, ud_t *ud); /* self destructor */
|
||||
ud_t *parent_ud; /* the ud of the parent object */
|
||||
uint32_t marks;
|
||||
int ref[6]; /* refs for callbacks, automatically unreferenced at destruction */
|
||||
void *info; /* object specific info (ud_info_t, subject to Free() at destruction, if not NULL) */
|
||||
};
|
||||
|
||||
/* Marks. m_ = marks word (uint32_t) , i_ = bit number (0 .. 31) */
|
||||
#define MarkGet(m_, i_) (((m_) & ((uint32_t)1 << (i_))) == ((uint32_t)1 << (i_)))
|
||||
#define MarkSet(m_, i_) \
|
||||
do \
|
||||
{ \
|
||||
(m_) = ((m_) | ((uint32_t)1 << (i_))); \
|
||||
} while (0)
|
||||
#define MarkReset(m_, i_) \
|
||||
do \
|
||||
{ \
|
||||
(m_) = ((m_) & (~((uint32_t)1 << (i_)))); \
|
||||
} while (0)
|
||||
|
||||
#define IsValid(ud) MarkGet((ud)->marks, 0)
|
||||
#define MarkValid(ud) MarkSet((ud)->marks, 0)
|
||||
#define CancelValid(ud) MarkReset((ud)->marks, 0)
|
||||
|
||||
#if 0
|
||||
/* .c */
|
||||
#define moonccd_
|
||||
#endif
|
||||
|
||||
#define setmetatable moonccd_setmetatable
|
||||
int setmetatable(lua_State *L, const char *mt);
|
||||
|
||||
#define newuserdata moonccd_newuserdata
|
||||
ud_t *newuserdata(lua_State *L, void *handle, const char *mt, const char *tracename);
|
||||
#define freeuserdata moonccd_freeuserdata
|
||||
int freeuserdata(lua_State *L, ud_t *ud, const char *tracename);
|
||||
#define pushuserdata moonccd_pushuserdata
|
||||
int pushuserdata(lua_State *L, ud_t *ud);
|
||||
|
||||
#define freechildren moonccd_freechildren
|
||||
int freechildren(lua_State *L, const char *mt, ud_t *parent_ud);
|
||||
|
||||
#define userdata_unref(L, handle) udata_unref((L), (handle))
|
||||
|
||||
#define UD(handle) userdata((handle)) /* dispatchable objects only */
|
||||
#define userdata moonccd_userdata
|
||||
ud_t *userdata(const void *handle);
|
||||
#define testxxx moonccd_testxxx
|
||||
void *testxxx(lua_State *L, int arg, ud_t **udp, const char *mt);
|
||||
#define checkxxx moonccd_checkxxx
|
||||
void *checkxxx(lua_State *L, int arg, ud_t **udp, const char *mt);
|
||||
#define optxxx moonccd_optxxx
|
||||
void *optxxx(lua_State *L, int arg, ud_t **udp, const char *mt);
|
||||
#define pushxxx moonccd_pushxxx
|
||||
int pushxxx(lua_State *L, void *handle);
|
||||
#define checkxxxlist moonccd_checkxxxlist
|
||||
void **checkxxxlist(lua_State *L, int arg, int *count, int *err, const char *mt);
|
||||
|
||||
#if 0 // 7yy
|
||||
/* zzz.c */
|
||||
#define checkzzz(L, arg, udp) (zzz_t *)checkxxx((L), (arg), (udp), ZZZ_MT)
|
||||
#define testzzz(L, arg, udp) (zzz_t *)testxxx((L), (arg), (udp), ZZZ_MT)
|
||||
#define optzzz(L, arg, udp) (zzz_t *)optxxx((L), (arg), (udp), ZZZ_MT)
|
||||
#define pushzzz(L, handle) pushxxx((L), (void *)(handle))
|
||||
#define checkzzzlist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), ZZZ_MT)
|
||||
|
||||
#endif
|
||||
|
||||
/* ccd.c */
|
||||
#define checkccd(L, arg, udp) (ccd_t *)checkxxx((L), (arg), (udp), CCDPAR_MT)
|
||||
#define testccd(L, arg, udp) (ccd_t *)testxxx((L), (arg), (udp), CCDPAR_MT)
|
||||
#define optccd(L, arg, udp) (ccd_t *)optxxx((L), (arg), (udp), CCDPAR_MT)
|
||||
#define pushccd(L, handle) pushxxx((L), (void *)(handle))
|
||||
#define checkccdlist(L, arg, count, err) checkxxxlist((L), (arg), (count), (err), CCDPAR_MT)
|
||||
|
||||
#define RAW_FUNC(xxx) \
|
||||
static int Raw(lua_State *L) \
|
||||
{ \
|
||||
lua_pushinteger(L, (uintptr_t)check##xxx(L, 1, NULL)); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
#define TYPE_FUNC(xxx) /* NONCL */ \
|
||||
static int Type(lua_State *L) \
|
||||
{ \
|
||||
(void)check##xxx(L, 1, NULL); \
|
||||
lua_pushstring(L, "" #xxx); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
#define DESTROY_FUNC(xxx) \
|
||||
static int Destroy(lua_State *L) \
|
||||
{ \
|
||||
ud_t *ud; \
|
||||
(void)test##xxx(L, 1, &ud); \
|
||||
if (!ud) \
|
||||
return 0; /* already deleted */ \
|
||||
return ud->destructor(L, ud); \
|
||||
}
|
||||
|
||||
#define PARENT_FUNC(xxx) \
|
||||
static int Parent(lua_State *L) \
|
||||
{ \
|
||||
ud_t *ud; \
|
||||
(void)check##xxx(L, 1, &ud); \
|
||||
if (!ud->parent_ud) \
|
||||
return 0; \
|
||||
return pushuserdata(L, ud->parent_ud); \
|
||||
}
|
||||
|
||||
#endif /* objectsDEFINED */
|
||||
@ -0,0 +1,54 @@
|
||||
-- MoonCCD example: hello.lua
|
||||
local ccd = require("moonccd")
|
||||
|
||||
-- Support function for box objects:
|
||||
local function support(obj, dir)
|
||||
-- Assume obj is a user defined table containing info about the object,
|
||||
-- in this case a box: obj = { pos, quat, x, y, z } where pos is the
|
||||
-- position, quat is the rotation, and x, y, z are the dimensions.
|
||||
-- Apply the rotation on direction vector:
|
||||
local qinv = ccd.qinvert(obj.quat)
|
||||
dir = ccd.vrotate(dir, qinv)
|
||||
-- Compute the support point in the specified direction:
|
||||
local v = {0.5 * ccd.sign(dir[1]) * obj.x, 0.5 * ccd.sign(dir[2]) * obj.y, 0.5 * ccd.sign(dir[3]) * obj.z}
|
||||
-- Transform support point according to the rotation of the object and return it
|
||||
v = ccd.vrotate(v, obj.quat)
|
||||
v = ccd.vadd(v, obj.pos)
|
||||
return v
|
||||
end
|
||||
|
||||
-- Create two box objects:
|
||||
local box1 = {
|
||||
pos = {-5, 0, 0},
|
||||
quat = {1, 0, 0, 0},
|
||||
x = 1,
|
||||
y = 2,
|
||||
z = 1,
|
||||
}
|
||||
local box2 = {
|
||||
pos = {0, 0, 0},
|
||||
quat = {1, 0, 0, 0},
|
||||
x = 2,
|
||||
y = 1,
|
||||
z = 2,
|
||||
}
|
||||
|
||||
-- Create and initialize the ccd parameters:
|
||||
local ccdpar = ccd.new({
|
||||
support1 = support, -- support function for first object
|
||||
support2 = support, -- support function for second object
|
||||
max_iterations = 100, -- maximum number of iterations
|
||||
})
|
||||
|
||||
for i = 0, 99 do
|
||||
local intersect = ccd.gjk_intersect(ccdpar, box1, box2)
|
||||
-- now intersect is true if the two boxes intersect, false otherwise
|
||||
-- print(i, intersect, box1.pos[1], box2.pos[1])
|
||||
if i < 35 or i > 65 then
|
||||
assert(not intersect)
|
||||
elseif i ~= 35 and i ~= 65 then
|
||||
assert(intersect)
|
||||
end
|
||||
-- move first box along the x axis:
|
||||
box1.pos[1] = box1.pos[1] + 0.1
|
||||
end
|
||||
@ -0,0 +1,62 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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 "internal.h"
|
||||
|
||||
int trace_objects = 0;
|
||||
|
||||
static int TraceObjects(lua_State *L)
|
||||
{
|
||||
trace_objects = checkboolean(L, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Now(lua_State *L)
|
||||
{
|
||||
lua_pushnumber(L, now());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Since(lua_State *L)
|
||||
{
|
||||
double t = luaL_checknumber(L, 1);
|
||||
lua_pushnumber(L, since(t));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static const struct luaL_Reg Functions[] =
|
||||
{
|
||||
{"trace_objects", TraceObjects},
|
||||
{"now", Now},
|
||||
{"since", Since},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
void moonccd_open_tracing(lua_State *L)
|
||||
{
|
||||
luaL_setfuncs(L, Functions, 0);
|
||||
}
|
||||
@ -0,0 +1,858 @@
|
||||
/* $OpenBSD: tree.h,v 1.14 2015/05/25 03:07:49 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* 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 THE AUTHOR ``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 AUTHOR 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.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_TREE_H_
|
||||
#define _SYS_TREE_H_
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
* same number of black nodes,
|
||||
* - each red node (except for the root) has a black parent,
|
||||
* - each leaf node is black.
|
||||
*
|
||||
* Every operation on a red-black tree is bounded as O(lg n).
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name \
|
||||
{ \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ \
|
||||
NULL \
|
||||
}
|
||||
|
||||
#define SPLAY_INIT(root) \
|
||||
do \
|
||||
{ \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct \
|
||||
{ \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) \
|
||||
do \
|
||||
{ \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) \
|
||||
do \
|
||||
{ \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) \
|
||||
do \
|
||||
{ \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) \
|
||||
do \
|
||||
{ \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) \
|
||||
do \
|
||||
{ \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) \
|
||||
{ \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) \
|
||||
{ \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
{ \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if (__comp < 0) \
|
||||
{ \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} \
|
||||
else if (__comp > 0) \
|
||||
{ \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} \
|
||||
else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
{ \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
{ \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root))) \
|
||||
{ \
|
||||
if (__comp < 0) \
|
||||
{ \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0) \
|
||||
{ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} \
|
||||
else if (__comp > 0) \
|
||||
{ \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0) \
|
||||
{ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while (1) \
|
||||
{ \
|
||||
if (__comp < 0) \
|
||||
{ \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0) \
|
||||
{ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} \
|
||||
else if (__comp > 0) \
|
||||
{ \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) \
|
||||
{ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-black tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name \
|
||||
{ \
|
||||
struct type *rbh_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define RB_INITIALIZER(root) \
|
||||
{ \
|
||||
NULL \
|
||||
}
|
||||
|
||||
#define RB_INIT(root) \
|
||||
do \
|
||||
{ \
|
||||
(root)->rbh_root = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define RB_BLACK 0
|
||||
#define RB_RED 1
|
||||
#define RB_ENTRY(type) \
|
||||
struct \
|
||||
{ \
|
||||
struct type *rbe_left; /* left element */ \
|
||||
struct type *rbe_right; /* right element */ \
|
||||
struct type *rbe_parent; /* parent element */ \
|
||||
int rbe_color; /* node color */ \
|
||||
}
|
||||
|
||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
||||
#define RB_ROOT(head) (head)->rbh_root
|
||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
||||
|
||||
#define RB_SET(elm, parent, field) \
|
||||
do \
|
||||
{ \
|
||||
RB_PARENT(elm, field) = parent; \
|
||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
||||
RB_COLOR(elm, field) = RB_RED; \
|
||||
} while (0)
|
||||
|
||||
#define RB_SET_BLACKRED(black, red, field) \
|
||||
do \
|
||||
{ \
|
||||
RB_COLOR(black, field) = RB_BLACK; \
|
||||
RB_COLOR(red, field) = RB_RED; \
|
||||
} while (0)
|
||||
|
||||
#ifndef RB_AUGMENT
|
||||
#define RB_AUGMENT(x) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) \
|
||||
do \
|
||||
{ \
|
||||
(tmp) = RB_RIGHT(elm, field); \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) \
|
||||
{ \
|
||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) \
|
||||
{ \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} \
|
||||
else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_LEFT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (0)
|
||||
|
||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) \
|
||||
do \
|
||||
{ \
|
||||
(tmp) = RB_LEFT(elm, field); \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) \
|
||||
{ \
|
||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) \
|
||||
{ \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} \
|
||||
else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_RIGHT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, )
|
||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
|
||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \
|
||||
attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_INSERT(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_FIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NFIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NEXT(struct type *); \
|
||||
attr struct type *name##_RB_PREV(struct type *); \
|
||||
attr struct type *name##_RB_MINMAX(struct name *, int);
|
||||
|
||||
/* Main rb operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define RB_GENERATE(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, )
|
||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
|
||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void \
|
||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *parent, *gparent, *tmp; \
|
||||
while ((parent = RB_PARENT(elm, field)) && \
|
||||
RB_COLOR(parent, field) == RB_RED) \
|
||||
{ \
|
||||
gparent = RB_PARENT(parent, field); \
|
||||
if (parent == RB_LEFT(gparent, field)) \
|
||||
{ \
|
||||
tmp = RB_RIGHT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) \
|
||||
{ \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_RIGHT(parent, field) == elm) \
|
||||
{ \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
tmp = RB_LEFT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) \
|
||||
{ \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
{ \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
||||
} \
|
||||
} \
|
||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr void \
|
||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
||||
elm != RB_ROOT(head)) \
|
||||
{ \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
{ \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) \
|
||||
{ \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) \
|
||||
{ \
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) \
|
||||
{ \
|
||||
struct type *oleft; \
|
||||
if ((oleft = RB_LEFT(tmp, field))) \
|
||||
RB_COLOR(oleft, field) = RB_BLACK; \
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft, field); \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field); \
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_RIGHT(tmp, field)) \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field); \
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) \
|
||||
{ \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) \
|
||||
{ \
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) \
|
||||
{ \
|
||||
struct type *oright; \
|
||||
if ((oright = RB_RIGHT(tmp, field))) \
|
||||
RB_COLOR(oright, field) = RB_BLACK; \
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_LEFT(head, tmp, oright, field); \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field); \
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_LEFT(tmp, field)) \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field); \
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (elm) \
|
||||
RB_COLOR(elm, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *child, *parent, *old = elm; \
|
||||
int color; \
|
||||
if (RB_LEFT(elm, field) == NULL) \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
else if (RB_RIGHT(elm, field) == NULL) \
|
||||
child = RB_LEFT(elm, field); \
|
||||
else \
|
||||
{ \
|
||||
struct type *left; \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while ((left = RB_LEFT(elm, field))) \
|
||||
elm = left; \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) \
|
||||
{ \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} \
|
||||
else \
|
||||
RB_ROOT(head) = child; \
|
||||
if (RB_PARENT(elm, field) == old) \
|
||||
parent = elm; \
|
||||
(elm)->field = (old)->field; \
|
||||
if (RB_PARENT(old, field)) \
|
||||
{ \
|
||||
if (RB_LEFT(RB_PARENT(old, field), field) == old) \
|
||||
RB_LEFT(RB_PARENT(old, field), field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(old, field), field) = elm; \
|
||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
||||
} \
|
||||
else \
|
||||
RB_ROOT(head) = elm; \
|
||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
||||
if (RB_RIGHT(old, field)) \
|
||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
||||
if (parent) \
|
||||
{ \
|
||||
left = parent; \
|
||||
do \
|
||||
{ \
|
||||
RB_AUGMENT(left); \
|
||||
} while ((left = RB_PARENT(left, field))); \
|
||||
} \
|
||||
goto color; \
|
||||
} \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) \
|
||||
{ \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} \
|
||||
else \
|
||||
RB_ROOT(head) = child; \
|
||||
color: \
|
||||
if (color == RB_BLACK) \
|
||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
||||
return (old); \
|
||||
} \
|
||||
\
|
||||
/* Inserts a node into the RB tree */ \
|
||||
attr struct type * \
|
||||
name##_RB_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
struct type *parent = NULL; \
|
||||
int comp = 0; \
|
||||
tmp = RB_ROOT(head); \
|
||||
while (tmp) \
|
||||
{ \
|
||||
parent = tmp; \
|
||||
comp = (cmp)(elm, parent); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
RB_SET(elm, parent, field); \
|
||||
if (parent != NULL) \
|
||||
{ \
|
||||
if (comp < 0) \
|
||||
RB_LEFT(parent, field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = elm; \
|
||||
RB_AUGMENT(parent); \
|
||||
} \
|
||||
else \
|
||||
RB_ROOT(head) = elm; \
|
||||
name##_RB_INSERT_COLOR(head, elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
attr struct type * \
|
||||
name##_RB_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
int comp; \
|
||||
while (tmp) \
|
||||
{ \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the first node greater than or equal to the search key */ \
|
||||
attr struct type * \
|
||||
name##_RB_NFIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *res = NULL; \
|
||||
int comp; \
|
||||
while (tmp) \
|
||||
{ \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) \
|
||||
{ \
|
||||
res = tmp; \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
} \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (res); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_NEXT(struct type *elm) \
|
||||
{ \
|
||||
if (RB_RIGHT(elm, field)) \
|
||||
{ \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while (RB_LEFT(elm, field)) \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else \
|
||||
{ \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_PREV(struct type *elm) \
|
||||
{ \
|
||||
if (RB_LEFT(elm, field)) \
|
||||
{ \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
while (RB_RIGHT(elm, field)) \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else \
|
||||
{ \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_MINMAX(struct name *head, int val) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *parent = NULL; \
|
||||
while (tmp) \
|
||||
{ \
|
||||
parent = tmp; \
|
||||
if (val < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
} \
|
||||
return (parent); \
|
||||
}
|
||||
|
||||
#define RB_NEGINF -1
|
||||
#define RB_INF 1
|
||||
|
||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
#define RB_FOREACH(x, name, head) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_NEXT(x))
|
||||
|
||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_PREV(x))
|
||||
|
||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \
|
||||
(x) = (y))
|
||||
|
||||
#endif /* _SYS_TREE_H_ */
|
||||
@ -0,0 +1,315 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "tree.h"
|
||||
#include "udata.h"
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
struct moonccd_udata_s
|
||||
{
|
||||
RB_ENTRY(moonccd_udata_s)
|
||||
entry;
|
||||
uint64_t id; /* object id (search key) */
|
||||
/* references on the Lua registry */
|
||||
int ref; /* the correspoding userdata */
|
||||
void *mem; /* userdata memory area allocated and released by Lua */
|
||||
const char *mt;
|
||||
};
|
||||
|
||||
#define UNEXPECTED_ERROR "unexpected error (%s, %d)", __FILE__, __LINE__
|
||||
|
||||
static int cmp(udata_t *udata1, udata_t *udata2) /* the compare function */
|
||||
{
|
||||
return (udata1->id < udata2->id ? -1 : udata1->id > udata2->id);
|
||||
}
|
||||
|
||||
static RB_HEAD(udatatree_s, udata_s) Head = RB_INITIALIZER(&Head);
|
||||
|
||||
RB_PROTOTYPE_STATIC(udatatree_s, udata_s, entry, cmp)
|
||||
RB_GENERATE_STATIC(udatatree_s, udata_s, entry, cmp)
|
||||
|
||||
static udata_t *udata_remove(udata_t *udata)
|
||||
{
|
||||
return RB_REMOVE(udatatree_s, &Head, udata);
|
||||
}
|
||||
static udata_t *udata_insert(udata_t *udata)
|
||||
{
|
||||
return RB_INSERT(udatatree_s, &Head, udata);
|
||||
}
|
||||
static udata_t *udata_search(uint64_t id)
|
||||
{
|
||||
udata_t tmp;
|
||||
tmp.id = id;
|
||||
return RB_FIND(udatatree_s, &Head, &tmp);
|
||||
}
|
||||
static udata_t *udata_first(uint64_t id)
|
||||
{
|
||||
udata_t tmp;
|
||||
tmp.id = id;
|
||||
return RB_NFIND(udatatree_s, &Head, &tmp);
|
||||
}
|
||||
#if 0
|
||||
static udata_t *udata_next(udata_t *udata)
|
||||
{ return RB_NEXT(udatatree_s, &Head, udata); }
|
||||
static udata_t *udata_prev(udata_t *udata)
|
||||
{ return RB_PREV(udatatree_s, &Head, udata); }
|
||||
static udata_t *udata_min(void)
|
||||
{ return RB_MIN(udatatree_s, &Head); }
|
||||
static udata_t *udata_max(void)
|
||||
{ return RB_MAX(udatatree_s, &Head); }
|
||||
static udata_t *udata_root(void)
|
||||
{ return RB_ROOT(&Head); }
|
||||
#endif
|
||||
|
||||
void *udata_new(lua_State *L, size_t size, uint64_t id_, const char *mt)
|
||||
/* Creates a new Lua userdata, optionally sets its metatable to mt (if != NULL),
|
||||
* associates the userdata with the passed id and pushes the userdata on the stack.
|
||||
*
|
||||
* The userdata can be subsquently pushed on the Lua stack with udata_push(L, id).
|
||||
* (This is useful to bind C/C++ objects to Lua userdata).
|
||||
*
|
||||
* If id=0, the pointer to the memory area allocated by Lua is used as identifier
|
||||
* (this function returnes it).
|
||||
*/
|
||||
{
|
||||
udata_t *udata;
|
||||
if ((udata = (udata_t *)Malloc(L, sizeof(udata_t))) == NULL)
|
||||
{
|
||||
luaL_error(L, "cannot allocate memory");
|
||||
return NULL;
|
||||
}
|
||||
memset(udata, 0, sizeof(udata_t));
|
||||
udata->mem = lua_newuserdata(L, size);
|
||||
if (!udata->mem)
|
||||
{
|
||||
Free(L, udata);
|
||||
luaL_error(L, "lua_newuserdata error");
|
||||
return NULL;
|
||||
}
|
||||
udata->id = id_ != 0 ? id_ : (uint64_t)(uintptr_t)(udata->mem);
|
||||
if (udata_search(udata->id))
|
||||
{
|
||||
Free(L, udata);
|
||||
luaL_error(L, "duplicated object %I", id_);
|
||||
return NULL;
|
||||
}
|
||||
/* create a reference for later push's */
|
||||
lua_pushvalue(L, -1); /* the newly created userdata */
|
||||
udata->ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
udata_insert(udata);
|
||||
if (mt)
|
||||
{
|
||||
udata->mt = mt;
|
||||
luaL_getmetatable(L, mt);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
return udata->mem;
|
||||
}
|
||||
|
||||
void *udata_mem(uint64_t id)
|
||||
{
|
||||
udata_t *udata = udata_search(id);
|
||||
return udata ? udata->mem : NULL;
|
||||
}
|
||||
|
||||
int udata_unref(lua_State *L, uint64_t id)
|
||||
/* unreference udata so that it will be garbage collected */
|
||||
{
|
||||
// printf("unref object %lu\n", id);
|
||||
udata_t *udata = udata_search(id);
|
||||
if (!udata)
|
||||
return luaL_error(L, "invalid object identifier %p", id);
|
||||
if (udata->ref != LUA_NOREF)
|
||||
{
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, udata->ref);
|
||||
udata->ref = LUA_NOREF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udata_free(lua_State *L, uint64_t id)
|
||||
/* this should be called in the __gc metamethod
|
||||
*/
|
||||
{
|
||||
udata_t *udata = udata_search(id);
|
||||
// printf("free object %lu\n", id);
|
||||
if (!udata)
|
||||
return luaL_error(L, "invalid object identifier %p", id);
|
||||
/* release all references */
|
||||
if (udata->ref != LUA_NOREF)
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, udata->ref);
|
||||
udata_remove(udata);
|
||||
Free(L, udata);
|
||||
/* mem is released by Lua at garbage collection */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udata_push(lua_State *L, uint64_t id)
|
||||
{
|
||||
udata_t *udata = udata_search(id);
|
||||
if (!udata)
|
||||
return luaL_error(L, "invalid object identifier %p", id);
|
||||
if (udata->ref == LUA_NOREF)
|
||||
return luaL_error(L, "unreferenced object");
|
||||
if (lua_rawgeti(L, LUA_REGISTRYINDEX, udata->ref) != LUA_TUSERDATA)
|
||||
return luaL_error(L, UNEXPECTED_ERROR);
|
||||
return 1; /* one value pushed */
|
||||
}
|
||||
|
||||
void udata_free_all(lua_State *L)
|
||||
/* free all without unreferencing (for atexit()) */
|
||||
{
|
||||
udata_t *udata;
|
||||
while ((udata = udata_first(0)))
|
||||
{
|
||||
udata_remove(udata);
|
||||
Free(L, udata);
|
||||
}
|
||||
}
|
||||
|
||||
int udata_scan(lua_State *L, const char *mt,
|
||||
void *info, int (*func)(lua_State *L, const void *mem, const char *mt, const void *info))
|
||||
/* scans the udata database, and calls the func callback for every 'mt' object found
|
||||
* (the object may be deleted in the callback).
|
||||
* func must return 0 to continue the scan, !=0 to interrupt it.
|
||||
* returns 1 if interrupted, 0 otherwise
|
||||
*/
|
||||
{
|
||||
int stop = 0;
|
||||
uint64_t id = 0;
|
||||
udata_t *udata;
|
||||
while ((udata = udata_first(id)))
|
||||
{
|
||||
id = udata->id + 1;
|
||||
if (mt == udata->mt)
|
||||
{
|
||||
stop = func(L, (const void *)(udata->mem), mt, info);
|
||||
if (stop)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_subclass(lua_State *L, int arg, int mt_index)
|
||||
{
|
||||
int ok;
|
||||
if (luaL_getmetafield(L, arg, "__index") == LUA_TNIL)
|
||||
return 0;
|
||||
if (lua_rawequal(L, mt_index, lua_gettop(L)))
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
ok = is_subclass(L, lua_gettop(L), mt_index);
|
||||
lua_pop(L, 1);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void *udata_test(lua_State *L, int arg, const char *mt)
|
||||
/* same as luaL_testudata(), but succeeds also if the userdata has not
|
||||
* mt as metatable but as ancestor
|
||||
*/
|
||||
{
|
||||
void *mem;
|
||||
int ok;
|
||||
if ((mem = luaL_testudata(L, arg, mt)))
|
||||
return mem;
|
||||
|
||||
if ((mem = lua_touserdata(L, arg)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (luaL_getmetatable(L, mt) != LUA_TTABLE)
|
||||
{
|
||||
luaL_error(L, "cannot find metatable '%s'", mt);
|
||||
return NULL;
|
||||
}
|
||||
ok = is_subclass(L, arg, lua_gettop(L));
|
||||
lua_pop(L, 1);
|
||||
return ok ? mem : NULL;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| newmetatable for 'class' userdata |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
int udata_define(lua_State *L, const char *mt, const luaL_Reg *methods, const luaL_Reg *metamethods)
|
||||
{
|
||||
/* create the metatable */
|
||||
if (!luaL_newmetatable(L, mt))
|
||||
return luaL_error(L, "cannot create metatable '%s'", mt);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
if (metamethods)
|
||||
/* add metamethods */
|
||||
luaL_setfuncs(L, metamethods, 0);
|
||||
if (methods)
|
||||
{
|
||||
/* add methods */
|
||||
luaL_getsubtable(L, -1, "__index");
|
||||
luaL_setfuncs(L, methods, 0);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udata_inherit(lua_State *L, const char *sub, const char *super)
|
||||
/* Sets metatable(sub).__index = metatable(super)
|
||||
*
|
||||
* This way, if one accesses a field/method of a 'sub' object which is not defined in
|
||||
* the 'sub' metatable, Lua searches for it in the 'super' metatable (i.e., the 'sub'
|
||||
* type inherits from 'super').
|
||||
*/
|
||||
{
|
||||
if (luaL_getmetatable(L, sub) != LUA_TTABLE)
|
||||
return luaL_error(L, "cannot find metatable '%s'", sub);
|
||||
luaL_getsubtable(L, -1, "__index");
|
||||
if (luaL_getmetatable(L, super) != LUA_TTABLE)
|
||||
return luaL_error(L, "cannot find metatable '%s'", super);
|
||||
lua_setmetatable(L, -2);
|
||||
lua_pop(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udata_addmethods(lua_State *L, const char *mt, const luaL_Reg *methods)
|
||||
/* Adds methods to the metatable mt */
|
||||
{
|
||||
if (luaL_getmetatable(L, mt) != LUA_TTABLE)
|
||||
return luaL_error(L, "cannot find metatable '%s'", mt);
|
||||
if (methods)
|
||||
{
|
||||
/* add methods */
|
||||
luaL_getsubtable(L, -1, "__index");
|
||||
luaL_setfuncs(L, methods, 0);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#ifndef Malloc
|
||||
#define Malloc moonccd_Malloc
|
||||
void *Malloc(lua_State *L, size_t size);
|
||||
#define Free moonccd_Free
|
||||
void Free(lua_State *L, void *ptr);
|
||||
#endif
|
||||
|
||||
#define udata_t moonccd_udata_t
|
||||
#define udata_s moonccd_udata_s
|
||||
#define moonccd_udata_t struct moonccd_udata_s
|
||||
|
||||
#define udata_new moonccd_udata_new
|
||||
void *udata_new(lua_State *, size_t, uint64_t, const char *);
|
||||
#define udata_unref moonccd_udata_unref
|
||||
int udata_unref(lua_State *L, uint64_t);
|
||||
#define udata_free moonccd_udata_free
|
||||
int udata_free(lua_State *, uint64_t);
|
||||
#define udata_mem moonccd_udata_mem
|
||||
void *udata_mem(uint64_t);
|
||||
#define udata_push moonccd_udata_push
|
||||
int udata_push(lua_State *, uint64_t);
|
||||
#define udata_free_all moonccd_udata_free_all
|
||||
void udata_free_all(lua_State *L);
|
||||
#define udata_scan moonccd_udata_scan
|
||||
int udata_scan(lua_State *L, const char *mt,
|
||||
void *info, int (*func)(lua_State *L, const void *mem, const char *mt, const void *info));
|
||||
#define udata_define moonccd_udata_define
|
||||
int udata_define(lua_State *, const char *, const luaL_Reg *, const luaL_Reg *);
|
||||
#define udata_inherit moonccd_udata_inherit
|
||||
int udata_inherit(lua_State *, const char *, const char *);
|
||||
#define udata_test moonccd_udata_test
|
||||
void *udata_test(lua_State *, int, const char *);
|
||||
#define udata_addmethods moonccd_udata_addmethods
|
||||
int udata_addmethods(lua_State *, const char *, const luaL_Reg *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,410 @@
|
||||
/* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 Stefano Trettel
|
||||
*
|
||||
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
||||
*
|
||||
* 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 "internal.h"
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Misc utilities |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
int noprintf(const char *fmt, ...)
|
||||
{
|
||||
(void)fmt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int notavailable(lua_State *L, ...)
|
||||
{
|
||||
return luaL_error(L, "function not available in this CL version");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Malloc |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
/* We do not use malloc(), free() etc directly. Instead, we inherit the memory
|
||||
* allocator from the main Lua state instead (see lua_getallocf in the Lua manual)
|
||||
* and use that.
|
||||
*
|
||||
* By doing so, we can use an alternative malloc() implementation without recompiling
|
||||
* this library (we have needs to recompile lua only, or execute it with LD_PRELOAD
|
||||
* set to the path to the malloc library we want to use).
|
||||
*/
|
||||
static lua_Alloc Alloc = NULL;
|
||||
static void *AllocUd = NULL;
|
||||
|
||||
static void malloc_init(lua_State *L)
|
||||
{
|
||||
if (Alloc)
|
||||
unexpected(L);
|
||||
Alloc = lua_getallocf(L, &AllocUd);
|
||||
}
|
||||
|
||||
static void *Malloc_(size_t size)
|
||||
{
|
||||
return Alloc ? Alloc(AllocUd, NULL, 0, size) : NULL;
|
||||
}
|
||||
|
||||
static void Free_(void *ptr)
|
||||
{
|
||||
if (Alloc)
|
||||
Alloc(AllocUd, ptr, 0, 0);
|
||||
}
|
||||
|
||||
void *Malloc(lua_State *L, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
if (size == 0)
|
||||
{
|
||||
luaL_error(L, errstring(ERR_MALLOC_ZERO));
|
||||
return NULL;
|
||||
}
|
||||
ptr = Malloc_(size);
|
||||
if (ptr == NULL)
|
||||
{
|
||||
luaL_error(L, errstring(ERR_MEMORY));
|
||||
return NULL;
|
||||
}
|
||||
memset(ptr, 0, size);
|
||||
//DBG("Malloc %p\n", ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *MallocNoErr(lua_State *L, size_t size) /* do not raise errors (check the retval) */
|
||||
{
|
||||
void *ptr = Malloc_(size);
|
||||
(void)L;
|
||||
if (ptr == NULL)
|
||||
return NULL;
|
||||
memset(ptr, 0, size);
|
||||
//DBG("MallocNoErr %p\n", ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
char *Strdup(lua_State *L, const char *s)
|
||||
{
|
||||
size_t len = strnlen(s, 256);
|
||||
char *ptr = (char *)Malloc(L, len + 1);
|
||||
if (len > 0)
|
||||
memcpy(ptr, s, len);
|
||||
ptr[len] = '\0';
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Free(lua_State *L, void *ptr)
|
||||
{
|
||||
(void)L;
|
||||
//DBG("Free %p\n", ptr);
|
||||
if (ptr)
|
||||
Free_(ptr);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Time utilities |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#if defined(LINUX)
|
||||
|
||||
#if 0
|
||||
static double tstosec(const struct timespec *ts)
|
||||
{
|
||||
return ts->tv_sec*1.0+ts->tv_nsec*1.0e-9;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void sectots(struct timespec *ts, double seconds)
|
||||
{
|
||||
ts->tv_sec = (time_t)seconds;
|
||||
ts->tv_nsec = (long)((seconds - ((double)ts->tv_sec)) * 1.0e9);
|
||||
}
|
||||
|
||||
double now(void)
|
||||
{
|
||||
#if _POSIX_C_SOURCE >= 199309L
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
|
||||
{
|
||||
printf("clock_gettime error\n");
|
||||
return -1;
|
||||
}
|
||||
return ts.tv_sec + ts.tv_nsec * 1.0e-9;
|
||||
#else
|
||||
struct timeval tv;
|
||||
if (gettimeofday(&tv, NULL) != 0)
|
||||
{
|
||||
printf("gettimeofday error\n");
|
||||
return -1;
|
||||
}
|
||||
return tv.tv_sec + tv.tv_usec * 1.0e-6;
|
||||
#endif
|
||||
}
|
||||
|
||||
void sleeep(double seconds)
|
||||
{
|
||||
#if _POSIX_C_SOURCE >= 199309L
|
||||
struct timespec ts, ts1;
|
||||
struct timespec *req, *rem, *tmp;
|
||||
sectots(&ts, seconds);
|
||||
req = &ts;
|
||||
rem = &ts1;
|
||||
while (1)
|
||||
{
|
||||
if (nanosleep(req, rem) == 0)
|
||||
return;
|
||||
tmp = req;
|
||||
req = rem;
|
||||
rem = tmp;
|
||||
}
|
||||
#else
|
||||
usleep((useconds_t)(seconds * 1.0e6));
|
||||
#endif
|
||||
}
|
||||
|
||||
#define time_init(L) \
|
||||
do \
|
||||
{ \
|
||||
(void)L; /* do nothing */ \
|
||||
} while (0)
|
||||
|
||||
#elif defined(MINGW)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static LARGE_INTEGER Frequency;
|
||||
double now(void)
|
||||
{
|
||||
LARGE_INTEGER ts;
|
||||
QueryPerformanceCounter(&ts);
|
||||
return ((double)(ts.QuadPart)) / Frequency.QuadPart;
|
||||
}
|
||||
|
||||
void sleeep(double seconds)
|
||||
{
|
||||
DWORD msec = (DWORD)seconds * 1000;
|
||||
//if(msec < 0) return; DWORD seems to be unsigned
|
||||
Sleep(msec);
|
||||
}
|
||||
|
||||
static void time_init(lua_State *L)
|
||||
{
|
||||
(void)L;
|
||||
QueryPerformanceFrequency(&Frequency);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Light userdata |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
void *checklightuserdata(lua_State *L, int arg)
|
||||
{
|
||||
if (lua_type(L, arg) != LUA_TLIGHTUSERDATA)
|
||||
{
|
||||
luaL_argerror(L, arg, "expected lightuserdata");
|
||||
return NULL;
|
||||
}
|
||||
return lua_touserdata(L, arg);
|
||||
}
|
||||
|
||||
void *optlightuserdata(lua_State *L, int arg)
|
||||
{
|
||||
if (lua_isnoneornil(L, arg))
|
||||
return NULL;
|
||||
return checklightuserdata(L, arg);
|
||||
}
|
||||
|
||||
void *checklightuserdataorzero(lua_State *L, int arg)
|
||||
{
|
||||
int val, isnum;
|
||||
val = lua_tointegerx(L, arg, &isnum);
|
||||
if (!isnum)
|
||||
return checklightuserdata(L, arg);
|
||||
if (val != 0)
|
||||
luaL_argerror(L, arg, "expected lightuserdata or 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Deep copy of a table |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
int copytable(lua_State *L)
|
||||
/* Deep-copies the contents of the table at arg=-1 to the table at arg=-2
|
||||
* Leaves them on the stack.
|
||||
*/
|
||||
{
|
||||
int src = lua_gettop(L);
|
||||
int dst = src - 1;
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, src))
|
||||
{
|
||||
lua_pushvalue(L, -2); // key
|
||||
if (lua_type(L, -1) == LUA_TTABLE) /* nested */
|
||||
{
|
||||
lua_newtable(L); //dst
|
||||
lua_pushvalue(L, -3); // value
|
||||
copytable(L);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
else
|
||||
lua_pushvalue(L, -2); // value
|
||||
lua_rawset(L, dst);
|
||||
lua_pop(L, 1); // value
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Custom luaL_checkxxx() style functions |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
int checkboolean(lua_State *L, int arg)
|
||||
{
|
||||
if (!lua_isboolean(L, arg))
|
||||
return (int)luaL_argerror(L, arg, "boolean expected");
|
||||
return lua_toboolean(L, arg); // ? 1 : 0;
|
||||
}
|
||||
|
||||
int testboolean(lua_State *L, int arg, int *err)
|
||||
{
|
||||
if (!lua_isboolean(L, arg))
|
||||
{
|
||||
*err = ERR_TYPE;
|
||||
return 0;
|
||||
}
|
||||
*err = 0;
|
||||
return lua_toboolean(L, arg); // ? 1 : 0;
|
||||
}
|
||||
|
||||
int optboolean(lua_State *L, int arg, int d)
|
||||
{
|
||||
if (!lua_isboolean(L, arg))
|
||||
return d;
|
||||
return lua_toboolean(L, arg);
|
||||
}
|
||||
|
||||
/* 1-based index to 0-based ------------------------------------------*/
|
||||
|
||||
int testindex(lua_State *L, int arg, int *err)
|
||||
{
|
||||
int val;
|
||||
if (!lua_isinteger(L, arg))
|
||||
{
|
||||
*err = ERR_TYPE;
|
||||
return 0;
|
||||
}
|
||||
val = lua_tointeger(L, arg);
|
||||
if (val < 1)
|
||||
{
|
||||
*err = ERR_GENERIC;
|
||||
return 0;
|
||||
}
|
||||
*err = 0;
|
||||
return val - 1;
|
||||
}
|
||||
|
||||
int checkindex(lua_State *L, int arg)
|
||||
{
|
||||
int val = luaL_checkinteger(L, arg);
|
||||
if (val < 1)
|
||||
return luaL_argerror(L, arg, "positive integer expected");
|
||||
return val - 1;
|
||||
}
|
||||
|
||||
int optindex(lua_State *L, int arg, int optval /* 0-based */)
|
||||
{
|
||||
int val = luaL_optinteger(L, arg, optval + 1);
|
||||
if (val < 1)
|
||||
return luaL_argerror(L, arg, "positive integer expected");
|
||||
return val - 1;
|
||||
}
|
||||
|
||||
void pushindex(lua_State *L, int val)
|
||||
{
|
||||
lua_pushinteger((L), (val) + 1);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Internal error codes |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
const char *errstring(int err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case ERR_NOTPRESENT:
|
||||
return "missing";
|
||||
case ERR_SUCCESS:
|
||||
return "success";
|
||||
case ERR_GENERIC:
|
||||
return "generic error";
|
||||
case ERR_TABLE:
|
||||
return "not a table";
|
||||
case ERR_FUNCTION:
|
||||
return "not a function";
|
||||
case ERR_EMPTY:
|
||||
return "empty list";
|
||||
case ERR_TYPE:
|
||||
return "invalid type";
|
||||
case ERR_ELEMTYPE:
|
||||
return "invalid element type";
|
||||
case ERR_VALUE:
|
||||
return "invalid value";
|
||||
case ERR_ELEMVALUE:
|
||||
return "invalid element value";
|
||||
case ERR_MEMORY:
|
||||
return "out of memory";
|
||||
case ERR_MALLOC_ZERO:
|
||||
return "zero bytes malloc";
|
||||
case ERR_LENGTH:
|
||||
return "invalid length";
|
||||
case ERR_POOL:
|
||||
return "elements are not from the same pool";
|
||||
case ERR_BOUNDARIES:
|
||||
return "invalid boundaries";
|
||||
case ERR_RANGE:
|
||||
return "out of range";
|
||||
case ERR_FOPEN:
|
||||
return "cannot open file";
|
||||
case ERR_OPERATION:
|
||||
return "operation failed";
|
||||
case ERR_UNKNOWN:
|
||||
return "unknown field name";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
return NULL; /* unreachable */
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------*
|
||||
| Inits |
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
void moonccd_utils_init(lua_State *L)
|
||||
{
|
||||
malloc_init(L);
|
||||
time_init(L);
|
||||
}
|
||||
Loading…
Reference in New Issue