1
0
mirror of git://projects.qi-hardware.com/cae-tools.git synced 2024-12-22 23:59:34 +02:00

poly2d/: Yet another 2D polygon library (WIP)

This commit is contained in:
Werner Almesberger 2012-05-04 21:40:55 -03:00
parent c2bfbd5a5e
commit dfa85075e8
24 changed files with 1793 additions and 0 deletions

105
poly2d/Makefile Normal file
View File

@ -0,0 +1,105 @@
#
# Makefile - Makefile of libpoly2d
#
# Written 2012 by Werner Almesberger
# Copyright 2012 by Werner Almesberger
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
PREFIX ?= /usr/local
SHELL = /bin/bash
LIB = libpoly2d.a
OBJS = v2d_intersect.o v2d_line_distance.o \
p2d_area.o p2d_area_holes.o \
p2d_attrib.o p2d_contains_point.o p2d_contains_poly.o \
p2d_copy.o p2d_free.o p2d_gnuplot.o p2d_make.o p2d_offset.o p2d_hsort.o
CFLAGS_WARN = -Wall -Wshadow -Wmissing-prototypes \
-Wmissing-declarations -Wno-format-zero-length
CFLAGS = $(CFLAGS_WARN) -g
CXXFLAGS = -Wall -frounding-math
LDFLAGS =
LDLIBS = -lm
# ----- Verbosity control -----------------------------------------------------
CC_normal := $(CC)
CXX_normal := $(CXX)
AR_normal := $(AR)
DEPEND_normal := $(CPP) $(CFLAGS) -MM -MG
CC_quiet = @echo " CC " $@ && $(CC_normal)
CXX_quiet = @echo " CXX " $@ && $(CXX_normal)
AR_quiet = @echo " AR " $@ && $(AR_normal)
DEPEND_quiet = @$(DEPEND_normal)
ifeq ($(V),1)
CC = $(CC_normal)
CXX = $(CXX_normal)
AR = $(AR_normal)
DEPEND = $(DEPEND_normal)
else
CC = $(CC_quiet)
CXX = $(CXX_quiet)
AR = $(AR_quiet)
DEPEND = $(DEPEND_quiet)
endif
# ----- Rules -----------------------------------------------------------------
.PHONY: all clean spotless
.PHONY: test tests valgrind
all: $(LIB)
$(LIB): $(OBJS)
$(AR) cr $@ $^
clean:
rm -f $(OBJS) $(OBJS:.o=.d)
spotless: clean
rm -f $(LIB)
# ----- Install / uninstall ---------------------------------------------------
install: all
mkdir -p $(DESTDIR)/$(PREFIX)/bin/
install -m 755 $(MAIN) $(DESTDIR)/$(PREFIX)/bin/
uninstall:
rm -f $(DESTDIR)/$(PREFIX)/bin/$(MAIN)
# ----- Dependencies ----------------------------------------------------------
# compile and generate dependencies, from fped, based on
# http://scottmcpeak.com/autodepend/autodepend.html
%.o: %.c
$(CC) -c $(CFLAGS) $*.c -o $*.o
$(DEPEND) $*.c | \
sed -e \
'/^\(.*:\)\? */{p;s///;s/ *\\\?$$/ /;s/ */:\n/g;H;}' \
-e '$${g;p;}' -e d >$*.d; \
[ "$${PIPESTATUS[*]}" = "0 0" ] || { rm -f $*.d; exit 1; }
-include $(OBJS:.o=.d)
# ----- Tests -----------------------------------------------------------------
test tests: all
LANG= sh -c \
'passed=0 && cd test && \
for n in [a-z]*; do \
[ $$n != core ] && SCRIPT=$$n . ./$$n; done; \
echo "Passed all $$passed tests"'
valgrind:
VALGRIND="valgrind -q" $(MAKE) tests

54
poly2d/README Normal file
View File

@ -0,0 +1,54 @@
poly2d - Yet another 2D polygon library
=======================================
Why do we need another 2D polygon library, if there are already CGAL,
Boost, Clipper, GPC, ... ?
All the above are either written in a weird language, are under a
non-Free license, or simply don't provide the feature set we need
here. poly2d is written in C, doesn't depend on non-standard
libraries, and is licensed under the GPL (will change to LGPL).
poly2d serves itself liberally from code already in cameo but
provides a simpler and cleaner interface. The first objective is
to provide the tools to replace the (badly broken) area filling
operation in cameo. Later, poly2d could replace more parts of
cameo.
poly2d puts more emphasis on simplicity than on performance. Some
functions expect clockwise closed polygons that don't self-intersect.
The table below shows the capabilities
Open Counter-clockwise
| Concave Min. vertices
| | Self-intersect
| | | | |
Y Y Y Y 0 p2d_contains_poly(b), p2d_free, p2d_free_all,
p2d_is_closed, p2d_copy, p2d_reverse, p2d_vertices,
p2d_write_gnuplog, p2d_write_gnuplot_all
Y Y Y Y 1 p2d_read_gnuplot
- Y Y Y 0 p2d_simplify
- Y - Y 3 p2d_is_cw
- Y - - 3 p2d_contains_point, p2d_contains_poly (a),
- Y - # 3 p2d_area*, p2d_offset*
# CGAL uses ccw, poly2d uses cw. Need to switch.
Not yet implemented:
- p2d_simplify (low priority - the offsetting from CGAL already covers
the main use case)
Not yet specified:
- subtraction (do we actually need it ?)
Other:
- change the license from GPL to LGPL
- change ccw to cw
- make sure CGAL is only fed ccw ploygons and cw holes
- transform CGAL's idea of outer polygons into something we can use
- use more meaningful offset/overlap model for area fill
- check for memory leaks
- try to generate dependencies also for *.cpp
Prerequisite:
libcgal-dev

65
poly2d/cgal_helper.h Normal file
View File

@ -0,0 +1,65 @@
/*
* cgal_helper.h - Conversions between poly2d and CGAL
*
* Written 2012 by Werner Almesberger
* Copyright 2012 by Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef CGAL_HELPER_H
#define CGAL_HELPER_H
/*
* References:
* http://www.cgal.org/Manual/latest/examples/Straight_skeleton_2/
* Create_saop_from_polygon_with_holes_2.cpp
* http://www.cgal.org/Manual/latest/examples/Straight_skeleton_2/print.h
*/
extern "C" {
#include "poly2d.h"
}
#include <vector>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_with_holes_2.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Polygon_2<K> Polygon_2;
static inline Polygon_2 p2d_to_P2(const struct p2d *p)
{
const struct v2d *v;
Polygon_2 np;
v = p->v;
do {
np.push_back(K::Point_2(v->x, v->y));
v = v->next;
}
while (v != p->v);
return np;
}
static inline struct p2d *P2_to_p2d(Polygon_2 p)
{
struct p2d *np;
np = p2d_new();
for (Polygon_2::Vertex_iterator vit = p.vertices_begin();
vit != p.vertices_end(); ++vit)
p2d_append(np, v2d_new(vit->x(), vit->y()));
p2d_close(np);
return np;
}
#endif /* !CGAL_HELPER_H */

44
poly2d/p2d_area.c Normal file
View File

@ -0,0 +1,44 @@
/*
* p2d_area.c - Fill a set of nested polygons
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "poly2d.h"
#include "p2d_hsort.h"
static void recurse_area(struct p2d_hier *t, double offset, double overlap,
struct p2d ***last)
{
const struct p2d *p, *h;
for (p = &t->p; p; p = p->next) {
h = &p2d_to_hier(p)->holes->p;
p2d_area_holes_append(p, h, offset, overlap, last);
while (h) {
recurse_area(p2d_to_hier(h)->holes, offset, overlap,
last);
h = h->next;
}
}
}
struct p2d *p2d_area(const struct p2d *p, double offset, double overlap)
{
struct p2d_hier *t;
struct p2d *res = NULL, **last = &res;
t = p2d_hsort(p);
recurse_area(t, offset, overlap, &last);
p2d_hier_free(t);
return res;
}

101
poly2d/p2d_area_holes.cpp Normal file
View File

@ -0,0 +1,101 @@
/*
* p2d_area_holes.cpp - Fill an area with holes
*
* Written 2012 by Werner Almesberger
* Copyright 2012 by Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
/*
* References:
* http://www.cgal.org/Manual/latest/examples/Straight_skeleton_2/
* Create_skeleton_and_offset_polygons_with_holes_2.cpp
* http://www.cgal.org/Manual/latest/examples/Straight_skeleton_2/print.h
*/
extern "C" {
#include <assert.h>
#include "poly2d.h"
}
#include "cgal_helper.h"
#include <vector>
#include <boost/shared_ptr.hpp>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_with_holes_2.h>
#include <CGAL/create_offset_polygons_from_polygon_with_holes_2.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Polygon_2<K> Polygon_2;
typedef CGAL::Polygon_with_holes_2<K> Polygon_with_holes;
typedef boost::shared_ptr<Polygon_with_holes> PolygonPtr;
typedef std::vector<PolygonPtr> PolygonPtrVector;
struct p2d *res = NULL, **last = &res, *np;
static void append_poly(Polygon_2 poly, struct p2d ***last)
{
**last = P2_to_p2d(poly);
*last = &(**last)->next;
}
static void recurse_area(Polygon_with_holes poly, double curr_off,
double next_off, struct p2d ***last)
{
PolygonPtrVector tmp =
CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(
curr_off, poly);
for (PolygonPtrVector::const_iterator pit = tmp.begin();
pit != tmp.end(); ++pit) {
append_poly((*pit)->outer_boundary(), last);
recurse_area(**pit, next_off, next_off, last);
for (Polygon_with_holes::Hole_const_iterator
hit = (*pit)->holes_begin();
hit != (*pit)->holes_end(); ++hit) {
append_poly(*hit, last);
}
}
}
extern "C" void p2d_area_holes_append(const struct p2d *p,
const struct p2d *holes, double offset, double overlap,
struct p2d ***last)
{
const struct p2d *h;
assert(p2d_is_closed(p));
Polygon_with_holes poly(p2d_to_P2(p));
for (h = holes; h; h = h->next) {
assert(p2d_is_closed(h));
poly.add_hole(p2d_to_P2(h));
}
recurse_area(poly, offset, offset-overlap, last);
}
extern "C" struct p2d *p2d_area_holes(const struct p2d *p,
const struct p2d *holes, double offset, double overlap)
{
struct p2d *res = NULL, **last = &res;
p2d_area_holes_append(p, holes, offset, overlap, &last);
return res;
}

122
poly2d/p2d_attrib.c Normal file
View File

@ -0,0 +1,122 @@
/*
* p2d_attrib.c - Determine various polygon attributes
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <math.h>
#include <assert.h>
#include "poly2d.h"
/*
* Angle in counter-clockwise direction to turn at point B when coming from A
* in order to face towards C.
*/
static double angle_3(const struct v2d *a, const struct v2d *b,
const struct v2d *c)
{
double ax, ay, bx, by;
double aa, bb;
double angle;
ax = b->x-a->x;
ay = b->y-a->y;
bx = c->x-b->x;
by = c->y-b->y;
aa = hypot(ax, ay);
bb = hypot(bx, by);
angle = acos((ax*bx+ay*by)/aa/bb)/M_PI*180.0;
return (ax*by-ay*bx) >= 0 ? angle : -angle;
}
/*
* If we predominantly turn to the right, then the path must be clockwise.
*/
int p2d_is_cw(const struct p2d *p)
{
const struct v2d *v;
double a = 0;
assert(p2d_vertices(p) >= 3);
assert(p2d_is_closed(p));
assert(p2d_no_intersect(p));
v = p->v;
do {
a += angle_3(v, v->next, v->next->next);
v = v->next;
}
while (v != p->v);
return a < 0;
}
int p2d_is_closed(const struct p2d *p)
{
return p->v == p->last || p->last->next;
}
/*
* Known bug: if the polygon intersects on a vertex, the intersection may
* go unnoticed.
*/
int p2d_no_intersect(const struct p2d *p)
{
const struct v2d *v, *u;
v = p->v;
while (v) {
u = v->next;
if (!u || u == p->v)
return 1;
u = u->next;
if (!u || u == p->v)
return 1;
while (u && u->next) {
if (v2d_intersect(v, v->next, u, u->next,
NULL, NULL) > 0)
return 0;
u = u->next;
if (u == p->v)
break;
if (u->next == v)
break;
}
v = v->next;
if (v == p->v)
break;
}
return 1;
}
int p2d_vertices(const struct p2d *p)
{
const struct v2d *v;
int n = 0;
v = p->v;
while (v) {
n++;
v = v->next;
if (v == p->v)
break;
}
return n;
}

View File

@ -0,0 +1,56 @@
/*
* p2d_contains_point.c - Determine whether polygon contains point/polygon
*
* Based on the algorithm by W. Randolph Franklin
* http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
* which is distributed under the following license, similar to the 3-clause
* BSD license:
*
* Copyright (c) 1970-2003, Wm. Randolph Franklin
*
* 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:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
* 2. Redistributions in binary form must reproduce the above copyright
* notice in the documentation and/or other materials provided with the
* distribution.
* 3. The name of W. Randolph Franklin may not be used to endorse or promote
* products derived from this Software without specific prior written
* permission.
*
* 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 "poly2d.h"
int p2d_contains_point(const struct p2d *p, const struct v2d *v)
{
const struct v2d *j, *i;
int in = 0;
j = p->v;
do {
i = j->next;
if (((i->y > v->y) != (j->y > v->y)) &&
(v->x < (j->x-i->x)*(v->y-i->y)/
(j->y-i->y)+i->x))
in = !in;
j = i;
}
while (j != p->v);
return in;
}

View File

@ -0,0 +1,38 @@
/*
* p2d_contains_poly.c - Determine whether polygon contains other polygon
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <assert.h>
#include "poly2d.h"
int p2d_contains_poly(const struct p2d *a, const struct p2d *b)
{
const struct v2d *v;
int in = 0, out = 0;
assert(p2d_is_closed(a));
v = b->v;
while (v) {
if (p2d_contains_point(a, v))
in++;
else
out++;
v = v->next;
if (v == b->v)
break;
}
if (in && out)
return -1;
return !out;
}

95
poly2d/p2d_copy.c Normal file
View File

@ -0,0 +1,95 @@
/*
* p2d_copy.c - Copy a polygon, with or without reversing it
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <math.h>
#include "util.h"
#include "poly2d.h"
struct p2d *p2d_copy(const struct p2d *p)
{
struct p2d *np;
const struct v2d *v;
struct v2d *nv, **last;
np = alloc_type(struct p2d);
np->v = NULL;
np->last = NULL;
np->next = NULL;
last = &np->v;
v = p->v;
while (v) {
nv = alloc_type(struct v2d);
nv->x = v->x;
nv->y = v->y;
nv->next = NULL;
*last = nv;
last = &nv->next;
np->last = nv;
v = v->next;
if (v == p->v)
break;
}
if (v)
*last = np->v;
return np;
}
struct p2d *p2d_copy_all(const struct p2d *p)
{
struct p2d *res = NULL, **last = &res;
while (p) {
*last = p2d_copy(p);
last = &(*last)->next;
p = p->next;
}
return res;
}
struct p2d *p2d_reverse(const struct p2d *p)
{
struct p2d *np;
const struct v2d *v;
struct v2d *nv;
np = alloc_type(struct p2d);
np->v = NULL;
np->last = NULL;
np->next = NULL;
v = p->v;
while (v) {
nv = alloc_type(struct v2d);
nv->x = v->x;
nv->y = v->y;
nv->next = np->v;
np->v = nv;
if (!np->last)
np->last= nv;
v = v->next;
if (v == p->v)
break;
}
if (v && np->last)
np->last->next = np->v;
return np;
}

44
poly2d/p2d_free.c Normal file
View File

@ -0,0 +1,44 @@
/*
* p2d_free.c - Deallocate polygons
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <stdlib.h>
#include "poly2d.h"
void p2d_free(struct p2d *p)
{
struct v2d *v, *next;
v = p->v;
while (v) {
next = v->next;
free(v);
v = next;
if (v == p->v)
break;
}
free(p);
}
void p2d_free_all(struct p2d *p)
{
struct p2d *next;
while (p) {
next = p->next;
p2d_free(p);
p = next;
}
}

97
poly2d/p2d_gnuplot.c Normal file
View File

@ -0,0 +1,97 @@
/*
* p2d_gnuplot.c - File I/O in gnuplot format
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <errno.h>
#include "poly2d.h"
#define EPSILON 1e-6
static void check_closed(struct p2d *p)
{
if (!p)
return;
if (hypot(p->v->x-p->last->x, p->v->y-p->last->y) > EPSILON)
return;
free(p->last);
p->last = p->v;
}
struct p2d *p2d_read_gnuplot(FILE *file)
{
struct p2d *res = NULL, **last = &res, *p = NULL;
char buf[1024];
double x, y;
int n;
while (fgets(buf, sizeof(buf), file)) {
if (*buf == '#')
continue;
n = sscanf(buf, "%lf %lf\n", &x, &y);
switch (n) {
case -1:
check_closed(p);
p = NULL;
break;
case 2:
break;
default:
errno = EINVAL;
return NULL;
}
if (!p) {
p = p2d_new();
*last = p;
last = &p->next;
}
p2d_append(p, v2d_new(x, y));
}
check_closed(p);
return res;
}
int p2d_write_gnuplot(FILE *file, const struct p2d *p)
{
const struct v2d *v;
v = p->v;
while (v) {
if (fprintf(file, "%g %g\n", v->x, v->y) < 0)
return 0;
v = v->next;
if (v == p->v) {
if (fprintf(file, "%g %g\n", v->x, v->y) < 0)
return 0;
break;
}
}
return fprintf(file, "\n") >= 0;
}
int p2d_write_gnuplot_all(FILE *file, const struct p2d *p)
{
while (p) {
if (!p2d_write_gnuplot(file, p))
return 0;
p = p->next;
}
return 1;
}

107
poly2d/p2d_hsort.c Normal file
View File

@ -0,0 +1,107 @@
/*
* p2d_hsort.c - Hierarchical polygon sort
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "util.h"
#include "poly2d.h"
#include "p2d_hsort.h"
static struct p2d_hier *recurse_hsort(struct p2d *p)
{
struct p2d *sub = NULL, *sub2 = NULL, **last = &sub;
struct p2d **a, *b, **next;
struct p2d_hier *res = NULL, *t;
struct p2d **res_last = (struct p2d **) &res;
/*
* Move all polygons that are inside some other polygon to "sub".
*/
for (a = &p; *a; a = next) {
next = &(*a)->next;
for (b = p; b; b = b->next)
if (*a != b && p2d_contains_poly(b, *a)) {
*last = *a;
last = &(*last)->next;
*a = *next;
next = a;
*last = NULL;
break;
}
}
while (p) {
/*
* Begin transplanting "p" into t->p.
*/
t = alloc_type(struct p2d_hier);
t->p = *p;
/*
* Move all polygons inside the current one from "sub" to
* "sub2". (Direct and indirect subordinates.)
*/
sub2 = NULL;
last = &sub2;
for (a = &sub; *a; a = next) {
next = &(*a)->next;
if (p2d_contains_poly(p, *a)) {
*last = *a;
last = &(*last)->next;
*a = *next;
next = a;
*last = NULL;
}
}
/*
* Sort the subordinates.
*/
t->holes = recurse_hsort(sub2);
/*
* End transplanting "p" into t->p.
*/
free(p);
p = t->p.next;
/*
* Append "t" to "res".
*/
*res_last = &t->p;
res_last = &t->p.next;
t->p.next = NULL;
}
return res;
}
struct p2d_hier *p2d_hsort(const struct p2d *p)
{
return recurse_hsort(p2d_copy_all(p));
}
void p2d_hier_free(struct p2d_hier *t)
{
struct p2d_hier *next;
struct p2d *p;
while (t) {
p2d_hier_free(t->holes);
p = &t->p;
next = p2d_to_hier(p->next);
p2d_free_all(p);
t = next;
}
}

32
poly2d/p2d_hsort.h Normal file
View File

@ -0,0 +1,32 @@
/*
* p2d_hsort.h - Hierarchical polygon sort
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef P2D_HSORT_H
#define P2D_HSORT_H
#include "poly2d.h"
#define p2d_to_hier(p) ((struct p2d_hier *) (p))
struct p2d_hier {
struct p2d p; /* "next" link for siblings */
struct p2d_hier *holes; /* children */
};
struct p2d_hier *p2d_hsort(const struct p2d *p);
void p2d_hier_free(struct p2d_hier *t);
#endif /* !P2D_HSORT_H */

73
poly2d/p2d_make.c Normal file
View File

@ -0,0 +1,73 @@
/*
* p2d_make.c - Polygon creation
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <assert.h>
#include "util.h"
#include "poly2d.h"
struct p2d *p2d_new(void)
{
struct p2d *np;
np = alloc_type(struct p2d);
np->v = NULL;
np->last = NULL;
np->next = NULL;
return np;
}
struct v2d *v2d_new(double x, double y)
{
struct v2d *nv;
nv = alloc_type(struct v2d);
nv->x = x;
nv->y = y;
nv->next = NULL;
return nv;
}
void p2d_append(struct p2d *p, struct v2d *v)
{
if (p->last && p->last->next)
v->next = p->v;
if (p->last)
p->last->next = v;
else
p->v = v;
p->last = v;
}
void p2d_prepend(struct p2d *p, struct v2d *v)
{
v->next = p->v;
p->v = v;
if (p->last) {
if (p->last->next)
p->last->next = v;
} else {
p->last = v;
}
}
void p2d_close(struct p2d *p)
{
assert(!p->v || !p->last->next);
p->last->next = p->v;
}

78
poly2d/p2d_offset.cpp Normal file
View File

@ -0,0 +1,78 @@
/*
* p2d_offset.cpp - Simple offsetting (without dogbones)
*
* Written 2012 by Werner Almesberger
* Copyright 2012 by Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
/*
* References:
* http://www.cgal.org/Manual/latest/examples/Straight_skeleton_2/
* Create_saop_from_polygon_with_holes_2.cpp
* http://www.cgal.org/Manual/latest/examples/Straight_skeleton_2/print.h
*/
extern "C" {
#include <assert.h>
#include "poly2d.h"
}
#include "cgal_helper.h"
#include <vector>
#include <boost/shared_ptr.hpp>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_with_holes_2.h>
#include <CGAL/create_offset_polygons_from_polygon_with_holes_2.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Polygon_2<K> Polygon_2;
typedef CGAL::Polygon_with_holes_2<K> Polygon_with_holes;
typedef boost::shared_ptr<Polygon_2> PolygonPtr;
typedef std::vector<PolygonPtr> PolygonPtrVector;
extern "C" struct p2d *p2d_offset_holes(const struct p2d *p,
const struct p2d *holes, double off)
{
const struct p2d *h;
struct p2d *res = NULL, **last = &res;
assert(p2d_is_closed(p));
Polygon_with_holes poly(p2d_to_P2(p));
for (h = holes; h; h = h->next) {
assert(p2d_is_closed(h));
poly.add_hole(p2d_to_P2(h));
}
PolygonPtrVector tmp = off > 0 ?
CGAL::create_exterior_skeleton_and_offset_polygons_2(off,
poly.outer_boundary()) :
CGAL::create_interior_skeleton_and_offset_polygons_2(-off, poly);
for (PolygonPtrVector::const_iterator pit = tmp.begin();
pit != tmp.end(); ++pit) {
*last = P2_to_p2d(**pit);
last = &(*last)->next;
}
return res;
}
extern "C" struct p2d *p2d_offset(const struct p2d *p, double off)
{
return p2d_offset_holes(p, NULL, off);
}

140
poly2d/poly2d.h Normal file
View File

@ -0,0 +1,140 @@
/*
* poly2d.h - The public face of the 2D Polygon library
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef POLY2D_H
#define POLY2D_H
#include <stdio.h>
struct v2d {
double x, y;
struct v2d *next; /* may end in NULL or may be cyclic */
};
struct p2d {
struct v2d *v; /* vertices */
struct v2d *last; /* last vertex or vertex preceding first */
struct p2d *next;
};
/*
* Polygon creation
*/
struct p2d *p2d_new(void);
struct v2d *v2d_new(double x, double y);
void p2d_append(struct p2d *p, struct v2d *v);
void p2d_prepend(struct p2d *p, struct v2d *v);
void p2d_close(struct p2d *p);
/*
* Intersect line from A0 to A1 with line from B0 to B1.
*
* Returns:
* 0 if the lines are parallel,
* 1 if the lines intersect between A0-A1 and B0-B1,
* -1 if the lines intersect outside A0-A1 or B0-B1.
*
* If v2d_intersect returns non-zero, the intersection P is at
*
* P = A0+(A1-A0)*na = B0+(B1-B0)*nb
*/
int v2d_intersect(const struct v2d *a0, const struct v2d *a1,
const struct v2d *b0, const struct v2d *b1,
double *na, double *nb);
/*
* Calculate the distance between point P and the line from A to B.
* The result is negative if P is on the "right" side of A->B.
*/
double v2d_line_distance(const struct v2d *a, const struct v2d *b,
const struct v2d *p);
/*
* Duplicate a polygon
*/
struct p2d *p2d_copy(const struct p2d *p);
struct p2d *p2d_copy_all(const struct p2d *p);
/*
* Change a polygon from clockwise to counter-clockwise and vice versa.
*/
struct p2d *p2d_reverse(const struct p2d *p);
/*
* p2d_is_cw determine whether a polygon is clockwise.
* p2d_is_closed determines whether a polygon is closed.
* p2d_no_intersect determines whether a polygon does't self-intersect.
* p2d_vertices counts the number of vertices in a polygon.
*/
int p2d_is_cw(const struct p2d *p);
int p2d_is_closed(const struct p2d *p);
int p2d_no_intersect(const struct p2d *p);
int p2d_vertices(const struct p2d *p);
/*
* Convert a possibly self-intersecting polygon into one or more simple
* polygons. [1]
*
* http://en.wikipedia.org/wiki/Simple_polygon
*/
struct p2d *p2d_simplify(const struct p2d *p);
/*
* p2d_free deallocates a single polygon and its vertices.
* p2d_free_all deallocates all polygons in a list.
*/
void p2d_free(struct p2d *p);
void p2d_free_all(struct p2d *p);
/*
* Returns non-zero if the point is inside or on the simple polygon.
*/
int p2d_contains_point(const struct p2d *p, const struct v2d *v);
/*
* Returns:
* 0 if polygon "b" is outside of polygon "a"
* 1 if polygon "b" is inside of polygon "a"
* -1 if the two polygons intersect
*/
int p2d_contains_poly(const struct p2d *a, const struct p2d *b);
struct p2d *p2d_offset_holes(const struct p2d *p, const struct p2d *holes,
double off);
struct p2d *p2d_offset(const struct p2d *p, double off);
void p2d_area_holes_append(const struct p2d *p,
const struct p2d *holes, double offset, double overlap,
struct p2d ***last);
struct p2d *p2d_area_holes(const struct p2d *p, const struct p2d *holes,
double offset, double overlap);
struct p2d *p2d_area(const struct p2d *p, double offset, double overlap);
struct p2d *p2d_read_gnuplot(FILE *file);
int p2d_write_gnuplot(FILE *file, const struct p2d *p);
int p2d_write_gnuplot_all(FILE *file, const struct p2d *p);
#endif /* !POLY2D_H */

99
poly2d/test/Common Executable file
View File

@ -0,0 +1,99 @@
#!/bin/sh
#
# Common - Elements shared by all regression tests for poly2d
#
# Written 2010, 2011 by Werner Almesberger
# Copyright 2010, 2011 Werner Almesberger
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
compile_and_run()
{
LIBS="-lpoly2d -lCGAL -lCGAL_Core -lboost_thread"
LIBS="$LIBS -lstdc++ -lmpfr -lgmp -lm"
cat <<EOF >_.c
#include <poly2d.h>
#include "p2d_hsort.h"
static void recurse_hier(const struct p2d_hier *h, int level)
{
const struct v2d *v;
while (h) {
printf("%*s", level*2, "");
v = h->p.v;
while (v) {
printf("%s%g %g", v == h->p.v ? "" : " ", v->x, v->y);
v = v->next;
if (v == h->p.v)
break;
}
printf("\n");
recurse_hier(h->holes, level+1);
h = p2d_to_hier(h->p.next);
}
}
static void __attribute__((unused)) print_hier(const struct p2d_hier *h)
{
recurse_hier(h, 0);
}
int main(void)
{
`cat _in`
return 0;
}
EOF
gcc -Wall -Werror -g -I.. _.c -L.. $LIBS || return
$VALGRIND ./a.out
}
tst()
{
echo -n "$1: " 1>&2
shift
cat >_in
compile_and_run "$@" >_out 2>&1 || {
echo FAILED "($SCRIPT)" 1>&2
cat _out
exit 1
}
}
tst_fail()
{
echo -n "$1: " 1>&2
shift
cat >_in
compile_and_run "$@" >_out 2>&1 && {
echo FAILED "($SCRIPT)" 1>&2
cat _out
exit 1
}
rm -f _in _.c a.out
}
expect()
{
diff -u - "$@" _out >_diff || {
echo FAILED "($SCRIPT)" 1>&2
cat _diff 1>&2
exit 1
}
echo PASSED 1>&2
rm -f _in _out _diff _.c a.out
passed=`expr ${passed:-0} + 1`
}

103
poly2d/test/area Executable file
View File

@ -0,0 +1,103 @@
#!/bin/sh
. ./Common
###############################################################################
tst "area without holes, constant offset" <<EOF
struct p2d *p = p2d_new();
struct p2d *q;
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(5.5, 0));
p2d_append(p, v2d_new(5.5, 4.5));
p2d_append(p, v2d_new(0, 4.5));
p2d_close(p);
q = p2d_area(p, 1, 0);
p2d_write_gnuplot_all(stdout, q);
EOF
expect <<EOF
1 1
4.5 1
4.5 3.5
1 3.5
1 1
2 2
3.5 2
3.5 2.5
2 2.5
2 2
EOF
#------------------------------------------------------------------------------
tst "area without holes, offset with overlap" <<EOF
struct p2d *p = p2d_new();
struct p2d *q;
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(5.5, 0));
p2d_append(p, v2d_new(5.5, 4.5));
p2d_append(p, v2d_new(0, 4.5));
p2d_close(p);
q = p2d_area(p, 1, 0.3);
p2d_write_gnuplot_all(stdout, q);
EOF
expect <<EOF
1 1
4.5 1
4.5 3.5
1 3.5
1 1
1.7 1.7
3.8 1.7
3.8 2.8
1.7 2.8
1.7 1.7
EOF
#------------------------------------------------------------------------------
tst "area with one symmetric hole" <<EOF
struct p2d *pl, *p;
struct p2d *q;
pl = p = p2d_new();
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(20, 0));
p2d_append(p, v2d_new(20, 10));
p2d_append(p, v2d_new(0, 10));
p2d_close(p);
p = p->next = p2d_new();
p2d_append(p, v2d_new(2, 2));
p2d_append(p, v2d_new(2, 8));
p2d_append(p, v2d_new(18, 8));
p2d_append(p, v2d_new(18, 2));
p2d_close(p);
q = p2d_area(pl, 0.7, 0);
p2d_write_gnuplot_all(stdout, q);
EOF
expect <<EOF
0.7 0.7
19.3 0.7
19.3 9.3
0.7 9.3
0.7 0.7
1.3 1.3
1.3 8.7
18.7 8.7
18.7 1.3
1.3 1.3
EOF
###############################################################################

96
poly2d/test/hsort Executable file
View File

@ -0,0 +1,96 @@
#!/bin/sh
. ./Common
###############################################################################
tst "hierarchical sort with one hole" <<EOF
struct p2d *pl, *p;
pl = p = p2d_new();
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(20, 0));
p2d_append(p, v2d_new(20, 10));
p2d_append(p, v2d_new(0, 10));
p2d_close(p);
p = p->next = p2d_new();
p2d_append(p, v2d_new(2, 2));
p2d_append(p, v2d_new(2, 8));
p2d_append(p, v2d_new(18, 8));
p2d_append(p, v2d_new(18, 2));
p2d_close(p);
print_hier(p2d_hsort(pl));
EOF
expect <<EOF
0 0 20 0 20 10 0 10
2 2 2 8 18 8 18 2
EOF
#------------------------------------------------------------------------------
tst "hierarchical sort with two holes" <<EOF
struct p2d *pl, *p;
pl = p = p2d_new();
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(10, 0));
p2d_append(p, v2d_new(10, 10));
p2d_close(p);
p = p->next = p2d_new();
p2d_append(p, v2d_new(2, 2));
p2d_append(p, v2d_new(4, 2));
p2d_append(p, v2d_new(4, 4));
p2d_close(p);
p = p->next = p2d_new();
p2d_append(p, v2d_new(6, 2));
p2d_append(p, v2d_new(8, 2));
p2d_append(p, v2d_new(8, 8));
p2d_close(p);
print_hier(p2d_hsort(pl));
EOF
expect <<EOF
0 0 10 0 10 10
2 2 4 2 4 4
6 2 8 2 8 8
EOF
#------------------------------------------------------------------------------
tst "hierarchical sort with nested holes" <<EOF
struct p2d *pl, *p;
pl = p = p2d_new();
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(10, 0));
p2d_append(p, v2d_new(10, 10));
p2d_close(p);
p = p->next = p2d_new();
p2d_append(p, v2d_new(2, 2));
p2d_append(p, v2d_new(8, 2));
p2d_append(p, v2d_new(8, 8));
p2d_close(p);
p = p->next = p2d_new();
p2d_append(p, v2d_new(3, 3));
p2d_append(p, v2d_new(7, 3));
p2d_append(p, v2d_new(7, 7));
p2d_close(p);
print_hier(p2d_hsort(pl));
EOF
expect <<EOF
0 0 10 0 10 10
2 2 8 2 8 8
3 3 7 3 7 7
EOF
###############################################################################

61
poly2d/test/make Executable file
View File

@ -0,0 +1,61 @@
#!/bin/sh
. ./Common
###############################################################################
tst "make an open polygon" <<EOF
struct p2d *p = p2d_new();
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(1, 2));
p2d_write_gnuplot(stdout, p);
EOF
expect <<EOF
0 0
1 2
EOF
#------------------------------------------------------------------------------
tst "make a closed polygon" <<EOF
struct p2d *p = p2d_new();
p2d_append(p, v2d_new(-1, 1));
p2d_append(p, v2d_new(3, 7));
p2d_close(p);
p2d_write_gnuplot(stdout, p);
EOF
expect <<EOF
-1 1
3 7
-1 1
EOF
#------------------------------------------------------------------------------
tst "make two open polygons" <<EOF
struct p2d *p = p2d_new();
struct p2d *q = p2d_new();
p2d_append(p, v2d_new(1, 4));
p2d_append(p, v2d_new(2, 8));
p->next = q;
p2d_append(q, v2d_new(3, 15));
p2d_append(q, v2d_new(4, 16));
p2d_write_gnuplot_all(stdout, p);
EOF
expect <<EOF
1 4
2 8
3 15
4 16
EOF
###############################################################################

52
poly2d/test/offset Executable file
View File

@ -0,0 +1,52 @@
#!/bin/sh
. ./Common
###############################################################################
tst "outer offset" <<EOF
struct p2d *p = p2d_new();
struct p2d *q;
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(2, 0));
p2d_append(p, v2d_new(2, 1));
p2d_append(p, v2d_new(0, 1));
p2d_close(p);
q = p2d_offset(p, 0.5);
p2d_write_gnuplot(stdout, p2d_reverse(q->next));
EOF
expect <<EOF
-0.5 -0.5
2.5 -0.5
2.5 1.5
-0.5 1.5
-0.5 -0.5
EOF
#------------------------------------------------------------------------------
tst "inner offset" <<EOF
struct p2d *p = p2d_new();
struct p2d *q;
p2d_append(p, v2d_new(0, 0));
p2d_append(p, v2d_new(2, 0));
p2d_append(p, v2d_new(2, 1));
p2d_append(p, v2d_new(0, 1));
p2d_close(p);
q = p2d_offset(p, -0.1);
p2d_write_gnuplot(stdout, q);
EOF
expect <<EOF
0.1 0.1
1.9 0.1
1.9 0.9
0.1 0.9
0.1 0.1
EOF
###############################################################################

28
poly2d/util.h Normal file
View File

@ -0,0 +1,28 @@
/*
* util.h - Common utility functions
*
* Written 2009 by Werner Almesberger
* Copyright 2009 by Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef UTIL_H
#define UTIL_H
#include <stdlib.h>
#include <string.h>
#define alloc_size(s) \
({ void *alloc_size_tmp = malloc(s); \
if (!alloc_size_tmp) \
abort(); \
alloc_size_tmp; })
#define alloc_type(t) ((t *) alloc_size(sizeof(t)))
#endif /* !UTIL_H */

69
poly2d/v2d_intersect.c Normal file
View File

@ -0,0 +1,69 @@
/*
* v2d_intersect.c - Intersect two lines
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <math.h>
#include "poly2d.h"
#define EPSILON 1e-6
/*
* Solve
*
* ax+by = e
* cx+dy = f
*
* with Cramer's rule:
* http://en.wikipedia.org/wiki/Cramer's_rule
*/
static int cramer2(double a, double b, double c, double d, double e, double f,
double *x, double *y)
{
double det;
det = a*d-b*c;
if (fabs(det) < EPSILON)
return 0;
*x = (e*d-b*f)/det;
*y = (a*f-e*c)/det;
return 1;
}
int v2d_intersect(const struct v2d *a0, const struct v2d *a1,
const struct v2d *b0, const struct v2d *b1,
double *na, double *nb)
{
double ax, ay, bx, by, dx, dy;
double a, b;
ax = a1->x-a0->x;
ay = a1->y-a0->y;
bx = b1->x-b0->x;
by = b1->y-b0->y;
dx = b0->x-a0->x;
dy = b0->y-a0->y;
if (!cramer2(ax, -bx, ay, -by, dx, dy, &a, &b))
return 0;
if (na)
*na = a;
if (nb)
*nb = b;
return a >= 0 && a <= 1 && b >= 0 && b <= 1 ? 1 : -1;
}

View File

@ -0,0 +1,34 @@
/*
* v2d_line_distance.c - Calculate the distance between a point and a line
*
* Written 2012 by Werner Almesberger
* Copyright 2012 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <math.h>
#include "poly2d.h"
/*
* We use formula (14) from
* http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
* but keep the sign.
*/
double v2d_line_distance(const struct v2d *a, const struct v2d *b,
const struct v2d *p)
{
double ax, ay;
ax = b->x-a->x;
ay = b->y-a->y;
return (ax*(a->y-p->y)-ay*(a->x-p->x))/hypot(ax, ay);
}