mirror of
git://projects.qi-hardware.com/cae-tools.git
synced 2024-12-23 05:08:59 +02:00
poly2d/: Yet another 2D polygon library (WIP)
This commit is contained in:
parent
c2bfbd5a5e
commit
dfa85075e8
105
poly2d/Makefile
Normal file
105
poly2d/Makefile
Normal 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
54
poly2d/README
Normal 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
65
poly2d/cgal_helper.h
Normal 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
44
poly2d/p2d_area.c
Normal 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
101
poly2d/p2d_area_holes.cpp
Normal 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
122
poly2d/p2d_attrib.c
Normal 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;
|
||||
}
|
56
poly2d/p2d_contains_point.c
Normal file
56
poly2d/p2d_contains_point.c
Normal 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;
|
||||
}
|
38
poly2d/p2d_contains_poly.c
Normal file
38
poly2d/p2d_contains_poly.c
Normal 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
95
poly2d/p2d_copy.c
Normal 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
44
poly2d/p2d_free.c
Normal 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
97
poly2d/p2d_gnuplot.c
Normal 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
107
poly2d/p2d_hsort.c
Normal 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 = ⊂
|
||||
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 = ⊂ *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
32
poly2d/p2d_hsort.h
Normal 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
73
poly2d/p2d_make.c
Normal 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
78
poly2d/p2d_offset.cpp
Normal 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
140
poly2d/poly2d.h
Normal 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
99
poly2d/test/Common
Executable 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
103
poly2d/test/area
Executable 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
96
poly2d/test/hsort
Executable 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
61
poly2d/test/make
Executable 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
52
poly2d/test/offset
Executable 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
28
poly2d/util.h
Normal 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
69
poly2d/v2d_intersect.c
Normal 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;
|
||||
}
|
34
poly2d/v2d_line_distance.c
Normal file
34
poly2d/v2d_line_distance.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user