Merge branch 'master' into examples-cleanup

master
Marius Kintel 2015-01-21 18:09:27 -05:00
commit 6fc671c961
106 changed files with 6452 additions and 192 deletions

View File

@ -0,0 +1,4 @@
-76.547562539078825239,38.705110299157155396,5.353092412611622386
-76.547562541749158527,38.705110304107662955,5.3530924135499766692
-76.5475616455078125,38.705108642578125,0
-76.5475616455078125,38.705108642578125,0.020000000000000000416

View File

@ -0,0 +1,4 @@
0,0,0
0,1,0
1,1,1
0,1,0

View File

@ -0,0 +1,4 @@
0,1,0
0,0,0
0,1,0
1,1,0

View File

@ -0,0 +1,4 @@
0 0 0
1 0 0
1 1 0
1 1 0

View File

@ -0,0 +1,4 @@
0 0 0
1 0 0
0 1 0
1 1 0

View File

@ -0,0 +1,5 @@
50.9504,53.0377,9.35305
50.2251,53.0492,9.4122
50.9537,53.0376,9.35278
55.2851,53.5142,9.35278
50.9537,53.0376,9.35278

View File

@ -0,0 +1,5 @@
50.95040130615234375,53.037654876708984375,9.35305023193359375
50.225086212158203125,53.049152374267578125,9.4121952056884765625
50.953746795654296875,53.037601470947265625,9.3527774810791015625
55.285125732421875,53.514209747314453125,9.3527774810791015625
50.953746795654296875,53.037601470947265625,9.3527774810791015625

View File

@ -0,0 +1,159 @@
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <sstream>
#include <fstream>
#include <iostream>
#include <locale.h>
#include "GeometryUtils.h"
#include "Reindexer.h"
#include "linalg.h"
#include "grid.h"
#include "printutils.h"
static void export_stl(const IndexedTriangleMesh &trimesh, std::ostream &output)
{
setlocale(LC_NUMERIC, "C"); // Ensure radix is . (not ,) in output
output << "solid OpenSCAD_Model\n";
const Vector3f *verts = &trimesh.vertices.front();
BOOST_FOREACH(const IndexedTriangle &t, trimesh.triangles) {
assert(t[0] < trimesh.vertices.size());
assert(t[1] < trimesh.vertices.size());
assert(t[2] < trimesh.vertices.size());
Vector3f p[3];
p[0] = verts[t[0]];
p[1] = verts[t[1]];
p[2] = verts[t[2]];
std::stringstream stream;
stream << p[0][0] << " " << p[0][1] << " " << p[0][2];
std::string vs1 = stream.str();
stream.str("");
stream << p[1][0] << " " << p[1][1] << " " << p[1][2];
std::string vs2 = stream.str();
stream.str("");
stream << p[2][0] << " " << p[2][1] << " " << p[2][2];
std::string vs3 = stream.str();
// if (vs1 != vs2 && vs1 != vs3 && vs2 != vs3) {
// The above condition ensures that there are 3 distinct vertices, but
// they may be collinear. If they are, the unit normal is meaningless
// so the default value of "1 0 0" can be used. If the vertices are not
// collinear then the unit normal must be calculated from the
// components.
Vector3f normal = (p[1] - p[0]).cross(p[2] - p[0]);
normal.normalize();
output << " facet normal " << normal[0] << " " << normal[1] << " " << normal[2] << "\n";
output << " outer loop\n";
for (int i=0;i<3;i++) {
output << " vertex " << p[i][0] << " " << p[i][1] << " " << p[i][2] << "\n";
}
output << " endloop\n";
output << " endfacet\n";
// }
}
output << "endsolid OpenSCAD_Model\n";
setlocale(LC_NUMERIC, ""); // Set default locale
}
/*!
file format:
1. polygon coordinates (x,y,z) are comma separated (+/- spaces) and
each coordinate is on a separate line
2. each polygon is separated by one or more blank lines
*/
bool import_polygon(IndexedPolygons &polyhole, const std::string &filename)
{
Reindexer<Vector3f> uniqueVertices;
std::ifstream ifs(filename.c_str());
if (!ifs) return false;
std::string line;
IndexedFace polygon;
while (std::getline(ifs, line)) {
std::stringstream ss(line);
double X = 0.0, Y = 0.0, Z = 0.0;
if (!(ss >> X)) {
//ie blank lines => flag start of next polygon
if (polygon.size() > 0) polyhole.faces.push_back(polygon);
polygon.clear();
continue;
}
char c = ss.peek();
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma
if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma
if (!(ss >> Y)) {
std::cerr << "Y error\n";
return false;
}
c = ss.peek();
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces before comma
if (c == ',') {ss.read(&c, 1); c = ss.peek();} //gobble comma
while (c == ' ') {ss.read(&c, 1); c = ss.peek();} //gobble spaces after comma
if (!(ss >> Z)) {
std::cerr << "Z error\n";
return false;
}
polygon.push_back(uniqueVertices.lookup(Vector3f(X, Y, Z)));
}
if (polygon.size() > 0) polyhole.faces.push_back(polygon);
ifs.close();
uniqueVertices.copy(std::back_inserter(polyhole.vertices));
return true;
}
int main(int argc, char *argv[])
{
OpenSCAD::debug = "GeometryUtils";
IndexedPolygons polyhole;
Vector3f *normal = NULL;
if (argc >= 2) {
if (!import_polygon(polyhole, argv[1])) {
std::cerr << "Error importing polygon" << std::endl;
exit(1);
}
std::cerr << "Imported " << polyhole.faces.size() << " polygons" << std::endl;
if (argc == 3) {
std::vector<std::string> strs;
std::vector<double> normalvec;
std::string arg(argv[2]);
boost::split(strs, arg, boost::is_any_of(","));
assert(strs.size() == 3);
BOOST_FOREACH(const std::string &s, strs) normalvec.push_back(boost::lexical_cast<double>(s));
normal = new Vector3f(normalvec[0], normalvec[1], normalvec[2]);
}
}
else {
//construct two non-intersecting nested polygons
Reindexer<Vector3f> uniqueVertices;
IndexedFace polygon1;
polygon1.push_back(uniqueVertices.lookup(Vector3f(0,0,0)));
polygon1.push_back(uniqueVertices.lookup(Vector3f(2,0,0)));
polygon1.push_back(uniqueVertices.lookup(Vector3f(2,2,0)));
polygon1.push_back(uniqueVertices.lookup(Vector3f(0,2,0)));
IndexedFace polygon2;
polygon2.push_back(uniqueVertices.lookup(Vector3f(0.5,0.5,0)));
polygon2.push_back(uniqueVertices.lookup(Vector3f(1.5,0.5,0)));
polygon2.push_back(uniqueVertices.lookup(Vector3f(1.5,1.5,0)));
polygon2.push_back(uniqueVertices.lookup(Vector3f(0.5,1.5,0)));
polyhole.faces.push_back(polygon1);
polyhole.faces.push_back(polygon2);
uniqueVertices.copy(std::back_inserter(polyhole.vertices));
}
std::vector<IndexedTriangle> triangles;
bool ok = GeometryUtils::tessellatePolygonWithHoles(polyhole, triangles, normal);
std::cerr << "Tessellated into " << triangles.size() << " triangles" << std::endl;
IndexedTriangleMesh trimesh;
trimesh.vertices = polyhole.vertices;
trimesh.triangles = triangles;
export_stl(trimesh, std::cout);
}

View File

@ -0,0 +1,106 @@
debug: DEFINES += DEBUG
TEMPLATE = app
INCLUDEPATH += ../src ../src/libtess2/Include
DEPENDPATH += ../src
# Handle custom library location.
# Used when manually installing 3rd party libraries
isEmpty(OPENSCAD_LIBDIR) OPENSCAD_LIBDIR = $$(OPENSCAD_LIBRARIES)
macx:isEmpty(OPENSCAD_LIBDIR) {
exists(/opt/local):exists(/usr/local/Cellar) {
error("It seems you might have libraries in both /opt/local and /usr/local. Please specify which one to use with qmake OPENSCAD_LIBDIR=<prefix>")
} else {
exists(/opt/local) {
#Default to MacPorts on Mac OS X
message("Automatically searching for libraries in /opt/local. To override, use qmake OPENSCAD_LIBDIR=<prefix>")
OPENSCAD_LIBDIR = /opt/local
} else:exists(/usr/local/Cellar) {
message("Automatically searching for libraries in /usr/local. To override, use qmake OPENSCAD_LIBDIR=<prefix>")
OPENSCAD_LIBDIR = /usr/local
}
}
}
!isEmpty(OPENSCAD_LIBDIR) {
QMAKE_INCDIR = $$OPENSCAD_LIBDIR/include
QMAKE_LIBDIR = $$OPENSCAD_LIBDIR/lib
}
TARGET = polyhole-tessellator-libtess2
mac {
CONFIG -= app_bundle
}
macx {
# Mac needs special care to link against the correct C++ library
# We attempt to auto-detect it by inspecting Boost
dirs = $${BOOSTDIR} $${QMAKE_LIBDIR}
for(dir, dirs) {
system(grep -q __112basic_string $${dir}/libboost_thread* >& /dev/null) {
message("Detected libc++-linked boost in $${dir}")
CONFIG += libc++
}
}
libc++ {
QMAKE_CXXFLAGS += -stdlib=libc++
QMAKE_LFLAGS += -stdlib=libc++
QMAKE_OBJECTIVE_CFLAGS += -stdlib=libc++
# libc++ on requires Mac OS X 10.7+
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7
}
}
# See Dec 2011 OpenSCAD mailing list, re: CGAL/GCC bugs.
*g++* {
QMAKE_CXXFLAGS *= -fno-strict-aliasing
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-local-typedefs # ignored before 4.8
}
*clang* {
# http://llvm.org/bugs/show_bug.cgi?id=9182
QMAKE_CXXFLAGS_WARN_ON += -Wno-overloaded-virtual
# disable enormous amount of warnings about CGAL / boost / etc
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-variable
QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-function
QMAKE_CXXFLAGS_WARN_ON += -Wno-c++11-extensions
# might want to actually turn this on once in a while
QMAKE_CXXFLAGS_WARN_ON += -Wno-format-security
}
# Application configuration
CONFIG += cgal
CONFIG += boost
CONFIG += eigen
CONFIG += gettext
include(../common.pri)
HEADERS += ../src/GeometryUtils.cc \
../src/linalg.h \
../src/printutils.h \
../src/grid.h \
../src/libtess2/Include/tesselator.h \
../src/libtess2/Source/bucketalloc.h \
../src/libtess2/Source/dict.h \
../src/libtess2/Source/geom.h \
../src/libtess2/Source/mesh.h \
../src/libtess2/Source/priorityq.h \
../src/libtess2/Source/sweep.h \
../src/libtess2/Source/tess.h
SOURCES += polyhole-tessellator-libtess2.cpp \
../src/GeometryUtils.cc \
../src/printutils.cc \
../src/grid.cc \
../src/libtess2/Source/bucketalloc.c \
../src/libtess2/Source/dict.c \
../src/libtess2/Source/geom.c \
../src/libtess2/Source/mesh.c \
../src/libtess2/Source/priorityq.c \
../src/libtess2/Source/sweep.c \
../src/libtess2/Source/tess.c

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 43 KiB

BIN
icons/openscad_doc.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

BIN
images/Command-Redo-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
images/Command-Reset-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

BIN
images/Command-Undo-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

BIN
images/zoom-text-in.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

BIN
images/zoom-text-out.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

View File

@ -297,6 +297,7 @@ HEADERS += src/typedefs.h \
src/Geometry.h \
src/Polygon2d.h \
src/clipper-utils.h \
src/GeometryUtils.h \
src/polyset-utils.h \
src/polyset.h \
src/printutils.h \
@ -365,6 +366,7 @@ SOURCES += src/version_check.cc \
src/Polygon2d.cc \
src/clipper-utils.cc \
src/polyset-utils.cc \
src/GeometryUtils.cc \
src/polyset.cc \
src/csgops.cc \
src/transform.cc \
@ -441,6 +443,24 @@ SOURCES += src/version_check.cc \
SOURCES += src/polyclipping/clipper.cpp
HEADERS += src/polyclipping/clipper.hpp
# libtess2
INCLUDEPATH += src/libtess2/Include
SOURCES += src/libtess2/Source/bucketalloc.c \
src/libtess2/Source/dict.c \
src/libtess2/Source/geom.c \
src/libtess2/Source/mesh.c \
src/libtess2/Source/priorityq.c \
src/libtess2/Source/sweep.c \
src/libtess2/Source/tess.c
HEADERS += src/libtess2/Include/tesselator.h \
src/libtess2/Source/bucketalloc.h \
src/libtess2/Source/dict.h \
src/libtess2/Source/geom.h \
src/libtess2/Source/mesh.h \
src/libtess2/Source/priorityq.h \
src/libtess2/Source/sweep.h \
src/libtess2/Source/tess.h
unix:!macx {
SOURCES += src/imageutils-lodepng.cc
SOURCES += src/OffscreenContextGLX.cc
@ -475,7 +495,6 @@ HEADERS += src/cgal.h \
SOURCES += src/cgalutils.cc \
src/cgalutils-tess.cc \
src/cgalutils-polyhedron.cc \
src/cgalutils-tess-old.cc \
src/CGALCache.cc \
src/CGALRenderer.cc \
src/CGAL_Nef_polyhedron.cc \

View File

@ -1,5 +1,19 @@
<RCC>
<qresource prefix="/">
<file>images/zoom-text-in-white.png</file>
<file>images/zoom-text-in.png</file>
<file>images/zoom-text-out-white.png</file>
<file>images/zoom-text-out.png</file>
<file>images/Command-Redo-32-white.png</file>
<file>images/Command-Reset-32-white.png</file>
<file>images/Command-Undo-32-white.png</file>
<file>images/Decrease-Indent-32-white.png</file>
<file>images/Increase-Indent-32-white.png</file>
<file>images/Command-Redo-32.png</file>
<file>images/Command-Reset-32.png</file>
<file>images/Command-Undo-32.png</file>
<file>images/Decrease-Indent-32.png</file>
<file>images/Increase-Indent-32.png</file>
<file>icons/close.png</file>
<file>icons/information-icons-error.png</file>
<file>icons/information-icons-info.png</file>
@ -59,11 +73,12 @@
<file>images/perspective1.png</file>
<file>images/Preview-32.png</file>
<file>images/animate.png</file>
<file>images/Preview-32 (1).png</file>
<file>images/orthogonalwhite.png</file>
<file>images/perspective1white.png</file>
<file>images/Preview-32-white.png</file>
<file>images/crosswhite.png</file>
<file>icons/background.png</file>
<file>images/scalemarkers.png</file>
<file>images/scalemarkers-white.png</file>
</qresource>
</RCC>

View File

@ -33,5 +33,6 @@ VS_VERSION_INFO VERSIONINFO
/* End of Version info */
IDI_ICON1 ICON DISCARDABLE "icons/openscad.ico"
IDI_ICON2 ICON DISCARDABLE "icons/openscad_doc.ico"

View File

@ -13,8 +13,8 @@
* MDI
* FIXME: Windows cmd-line fixes
* Qt5, retina
* SVG import/export
* AMF import/export
* SVG export
* AMF export
* Color schemes for viewer and editor can be user-edited
* Improved editor
* Splash screen

View File

@ -26,8 +26,7 @@ void Camera::setup(std::vector<double> params)
type = Camera::GIMBAL;
object_trans << params[0], params[1], params[2];
object_rot << params[3], params[4], params[5];
viewer_distance = params[6];
height = params[6];
height = viewer_distance = params[6];
} else if (params.size() == 6) {
type = Camera::VECTOR;
eye << params[0], params[1], params[2];
@ -74,13 +73,13 @@ void Camera::viewAll(const BoundingBox &bbox, float scalefactor)
switch (this->projection) {
case Camera::ORTHOGONAL:
this->height = bbox.diagonal().norm();
this->height = this->viewer_distance = bbox.diagonal().norm();
break;
case Camera::PERSPECTIVE: {
double radius = bbox.diagonal().norm()/2;
switch (this->type) {
case Camera::GIMBAL:
this->viewer_distance = radius / tan(this->fov*M_PI/360);
this->height = this->viewer_distance = radius / tan(this->fov*M_PI/360);
break;
case Camera::VECTOR: {
Vector3d cameradir = (this->center - this->eye).normalized();
@ -101,8 +100,7 @@ void Camera::viewAll(const BoundingBox &bbox, float scalefactor)
void Camera::zoom(int delta)
{
this->viewer_distance *= pow(0.9, delta / 120.0);
this->height = this->viewer_distance;
this->height = this->viewer_distance *= pow(0.9, delta / 120.0);
}
void Camera::setProjection(ProjectionType type)
@ -115,8 +113,7 @@ void Camera::resetView()
type = Camera::GIMBAL;
object_rot << 35, 0, -25;
object_trans << 0, 0, 0;
height = 140;
viewer_distance = 140;
height = viewer_distance = 140;
}
double Camera::zoomValue()

207
src/GeometryUtils.cc Normal file
View File

@ -0,0 +1,207 @@
#include "GeometryUtils.h"
#include "tesselator.h"
#include "printutils.h"
#include "Reindexer.h"
#include "grid.h"
#include <boost/foreach.hpp>
static void *stdAlloc(void* userData, unsigned int size) {
TESS_NOTUSED(userData);
return malloc(size);
}
static void stdFree(void* userData, void* ptr) {
TESS_NOTUSED(userData);
free(ptr);
}
/*!
Tessellates input contours into a triangle mesh.
The input contours may consist of positive (CCW) and negative (CW)
contours. These define holes, and possibly islands.
The output will be written as indices into the input vertex vector.
This function should be robust wrt. malformed input.
It will only use existing vertices and is guaranteed use all
existing vertices, i.e. it will maintain connectivity if the input
polygon is part of a polygon mesh.
Returns true on error, false on success.
*/
bool GeometryUtils::tessellatePolygonWithHoles(const IndexedPolygons &polygons,
std::vector<IndexedTriangle> &triangles,
const Vector3f *normal)
{
// No polygon. FIXME: Will this ever happen or can we assert here?
if (polygons.faces.empty()) return false;
if (polygons.faces.size() == 1 && polygons.faces[0].size() == 3) {
// Input polygon has 3 points. shortcut tessellation.
triangles.push_back(IndexedTriangle(polygons.faces[0][0], polygons.faces[0][1], polygons.faces[0][2]));
return false;
}
TESSreal *normalvec = NULL;
TESSreal passednormal[3];
if (normal) {
passednormal[0] = (*normal)[0];
passednormal[1] = (*normal)[1];
passednormal[2] = (*normal)[2];
normalvec = passednormal;
}
TESSalloc ma;
TESStesselator* tess = 0;
memset(&ma, 0, sizeof(ma));
ma.memalloc = stdAlloc;
ma.memfree = stdFree;
ma.extraVertices = 256; // realloc not provided, allow 256 extra vertices.
if (!(tess = tessNewTess(&ma))) return true;
int numContours = 0;
std::vector<TESSreal> contour;
// Since libtess2's indices is based on the running number of points added, we need to map back
// to our indices. allindices does the mapping.
std::vector<int> allindices;
BOOST_FOREACH(const IndexedFace &face, polygons.faces) {
const Vector3f *verts = &polygons.vertices.front();
contour.clear();
BOOST_FOREACH(int idx, face) {
const Vector3f &v = verts[idx];
contour.push_back(v[0]);
contour.push_back(v[1]);
contour.push_back(v[2]);
allindices.push_back(idx);
}
assert(face.size() >= 3);
PRINTDB("Contour: %d\n", face.size());
tessAddContour(tess, 3, &contour.front(), sizeof(TESSreal) * 3, face.size());
numContours++;
}
if (!tessTesselate(tess, TESS_WINDING_ODD, TESS_CONSTRAINED_DELAUNAY_TRIANGLES, 3, 3, normalvec)) return -1;
const TESSindex *vindices = tessGetVertexIndices(tess);
const TESSindex *elements = tessGetElements(tess);
int numelems = tessGetElementCount(tess);
/*
At this point, we have a delaunay triangle mesh.
However, as libtess2 might merge vertices, as well as insert new
vertices for intersecting edges, we need to detect these and
insert dummy triangles to maintain external connectivity.
FIXME: This currently only works for polygons without holes.
*/
if (polygons.faces.size() == 1) { // Only works for polygons without holes
/*
Algorithm:
A) Collect all triangles using _only_ existing vertices -> triangles
B) Locate all unused vertices
C) For each unused vertex, create a triangle connecting it to the existing mesh
*/
const IndexedFace &face = polygons.faces.front();
int inputSize = allindices.size(); // inputSize is number of points added to libtess2
std::vector<int> vflags(inputSize); // Inits with 0's
IndexedTriangle tri;
for (int t=0;t<numelems;t++) {
bool err = false;
for (int i=0;i<3;i++) {
int vidx = vindices[elements[t*3 + i]];
if (vidx == TESS_UNDEF) err = true;
else tri[i] = vidx; // A)
}
PRINTDB("%d (%d) %d (%d) %d (%d)",
elements[t*3 + 0] % allindices[vindices[elements[t*3 + 0]]] %
elements[t*3 + 1] % allindices[vindices[elements[t*3 + 1]]] %
elements[t*3 + 2] % allindices[vindices[elements[t*3 + 2]]]);
// FIXME: We ignore self-intersecting triangles rather than detecting and handling this
if (!err) {
vflags[tri[0]]++; // B)
vflags[tri[1]]++;
vflags[tri[2]]++;
triangles.push_back(IndexedTriangle(allindices[tri[0]], allindices[tri[1]], allindices[tri[2]]));
}
}
for (int i=0;i<inputSize;i++) {
if (!vflags[i]) { // vertex missing in output: C)
int starti = (i+inputSize-1)%inputSize;
int j;
for (j = i; j < inputSize && !vflags[j]; j++) {
// Create triangle fan from vertex i-1 to the first existing vertex
PRINTDB("(%d) (%d) (%d)\n", allindices[starti] % allindices[j] % allindices[((j+1)%inputSize)]);
tri[0] = allindices[starti];
tri[1] = allindices[j];
tri[2] = allindices[(j+1)%inputSize];
triangles.push_back(tri);
}
i = j;
}
}
}
else {
IndexedTriangle tri;
for (int t=0;t<numelems;t++) {
bool err = false;
for (int i=0;i<3;i++) {
int vidx = vindices[elements[t*3 + i]];
if (vidx == TESS_UNDEF) err = true;
else tri[i] = allindices[vidx];
}
PRINTDB("%d (%d) %d (%d) %d (%d)",
elements[t*3 + 0] % allindices[vindices[elements[t*3 + 0]]] %
elements[t*3 + 1] % allindices[vindices[elements[t*3 + 1]]] %
elements[t*3 + 2] % allindices[vindices[elements[t*3 + 2]]]);
// FIXME: We ignore self-intersecting triangles rather than detecting and handling this
if (!err) {
triangles.push_back(tri);
}
else PRINT("WARNING: Self-intersecting polygon encountered - ignoring");
}
}
tessDeleteTess(tess);
return false;
}
/*!
Tessellates a single contour. Non-indexed version.
*/
bool GeometryUtils::tessellatePolygon(const Polygon &polygon, Polygons &triangles,
const Vector3f *normal)
{
bool err = false;
Reindexer<Vector3f> uniqueVertices;
IndexedPolygons indexedpolygons;
indexedpolygons.faces.push_back(IndexedFace());
IndexedFace &currface = indexedpolygons.faces.back();
BOOST_FOREACH (const Vector3d &v, polygon) {
int idx = uniqueVertices.lookup(v.cast<float>());
if (currface.empty() || idx != currface.back()) currface.push_back(idx);
}
if (currface.front() == currface.back()) currface.pop_back();
if (currface.size() >= 3) { // Cull empty triangles
uniqueVertices.copy(std::back_inserter(indexedpolygons.vertices));
std::vector<IndexedTriangle> indexedtriangles;
err = tessellatePolygonWithHoles(indexedpolygons, indexedtriangles, normal);
Vector3f *verts = &indexedpolygons.vertices.front();
BOOST_FOREACH(const IndexedTriangle &t, indexedtriangles) {
triangles.push_back(Polygon());
Polygon &p = triangles.back();
p.push_back(verts[t[0]].cast<double>());
p.push_back(verts[t[1]].cast<double>());
p.push_back(verts[t[2]].cast<double>());
}
}
return err;
}

27
src/GeometryUtils.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "linalg.h"
#include <vector>
typedef std::vector<Vector3d> Polygon;
typedef std::vector<Polygon> Polygons;
typedef std::vector<int> IndexedFace;
typedef Vector3i IndexedTriangle;
struct IndexedPolygons {
std::vector<Vector3f> vertices;
std::vector<IndexedFace> faces;
};
struct IndexedTriangleMesh {
std::vector<Vector3f> vertices;
std::vector<IndexedTriangle> triangles;
};
namespace GeometryUtils {
bool tessellatePolygon(const Polygon &polygon, Polygons &triangles,
const Vector3f *normal = NULL);
bool tessellatePolygonWithHoles(const IndexedPolygons &polygons, std::vector<IndexedTriangle> &triangles,
const Vector3f *normal = NULL);
}

View File

@ -99,6 +99,7 @@ private slots:
void showProgress();
void openCSGSettingsChanged();
private:
void initActionIcon(QAction *action, const char *darkResource, const char *lightResource);
void openFile(const QString &filename);
void handleFileDrop(const QString &filename);
void refreshDocument();
@ -205,7 +206,7 @@ public slots:
void actionReloadRenderPreview();
void on_editorDock_visibilityChanged(bool);
void on_consoleDock_visibilityChanged(bool);
void on_pushButtonCompileResultClose_clicked();
void on_toolButtonCompileResultClose_clicked();
void editorTopLevelChanged(bool);
void consoleTopLevelChanged(bool);
#ifdef ENABLE_OPENCSG
@ -240,6 +241,7 @@ public slots:
void helpAbout();
void helpHomepage();
void helpManual();
void helpCheatSheet();
void helpLibrary();
void helpFontInfo();
void quit();

View File

@ -43,39 +43,20 @@
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>9</number>
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>2</number>
<number>6</number>
</property>
<property name="bottomMargin">
<number>2</number>
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>4</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelCompileResultIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>26</width>
<height>26</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../openscad.qrc">:/icons/information-icons-error.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelCompileResultMessage">
<property name="sizePolicy">
@ -86,7 +67,6 @@
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
@ -94,10 +74,16 @@
<property name="text">
<string>Message</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="pushButtonCompileResultClose">
<widget class="QToolButton" name="toolButtonCompileResultClose">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
@ -105,11 +91,43 @@
<iconset resource="../openscad.qrc">
<normaloff>:/icons/close.png</normaloff>:/icons/close.png</iconset>
</property>
<property name="flat">
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QToolButton" name="toolButtonCompileResultIcon">
<property name="styleSheet">
<string notr="true">QToolButton { border: none; }</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../openscad.qrc">
<normaloff>:/icons/information-icons-error.png</normaloff>:/icons/information-icons-error.png</iconset>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="autoRaise">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -195,6 +213,7 @@
<addaction name="viewActionViewAll"/>
<addaction name="viewActionZoomIn"/>
<addaction name="viewActionZoomOut"/>
<addaction name="viewActionResetView"/>
<addaction name="separator"/>
<addaction name="viewActionRight"/>
<addaction name="viewActionTop"/>
@ -205,7 +224,9 @@
<addaction name="separator"/>
<addaction name="viewActionPerspective"/>
<addaction name="viewActionOrthogonal"/>
<addaction name="separator"/>
<addaction name="viewActionShowAxes"/>
<addaction name="viewActionShowScaleProportional"/>
<addaction name="viewActionShowEdges"/>
</widget>
</item>
@ -296,8 +317,8 @@
<addaction name="editActionFindPrevious"/>
<addaction name="editActionUseSelectionForFind"/>
<addaction name="separator"/>
<addaction name="editActionZoomIn"/>
<addaction name="editActionZoomOut"/>
<addaction name="editActionZoomTextIn"/>
<addaction name="editActionZoomTextOut"/>
<addaction name="editActionPreferences"/>
</widget>
<widget class="QMenu" name="menu_Design">
@ -360,6 +381,7 @@
<addaction name="helpActionAbout"/>
<addaction name="helpActionHomepage"/>
<addaction name="helpActionManual"/>
<addaction name="helpActionCheatSheet"/>
<addaction name="helpActionLibraryInfo"/>
<addaction name="helpActionFontInfo"/>
</widget>
@ -388,6 +410,11 @@
<addaction name="fileActionOpen"/>
<addaction name="fileActionSave"/>
<addaction name="separator"/>
<addaction name="editActionUndo"/>
<addaction name="editActionRedo"/>
<addaction name="editActionUnindent"/>
<addaction name="editActionIndent"/>
<addaction name="separator"/>
<addaction name="designActionPreview"/>
<addaction name="designActionRender"/>
<addaction name="separator"/>
@ -585,6 +612,10 @@
</property>
</action>
<action name="editActionUndo">
<property name="icon">
<iconset resource="../openscad.qrc">
<normaloff>:/images/Command-Undo-32.png</normaloff>:/images/Command-Undo-32.png</iconset>
</property>
<property name="text">
<string>&amp;Undo</string>
</property>
@ -596,6 +627,10 @@
</property>
</action>
<action name="editActionRedo">
<property name="icon">
<iconset resource="../openscad.qrc">
<normaloff>:/images/Command-Redo-32.png</normaloff>:/images/Command-Redo-32.png</iconset>
</property>
<property name="text">
<string>&amp;Redo</string>
</property>
@ -640,6 +675,10 @@
</property>
</action>
<action name="editActionIndent">
<property name="icon">
<iconset resource="../openscad.qrc">
<normaloff>:/images/Increase-Indent-32.png</normaloff>:/images/Increase-Indent-32.png</iconset>
</property>
<property name="text">
<string>&amp;Indent</string>
</property>
@ -647,14 +686,6 @@
<string>Ctrl+I</string>
</property>
</action>
<action name="editActionUnindent">
<property name="text">
<string>U&amp;nindent</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+I</string>
</property>
</action>
<action name="editActionComment">
<property name="text">
<string>C&amp;omment</string>
@ -684,17 +715,17 @@
<string>Paste viewport rotation</string>
</property>
</action>
<action name="editActionZoomIn">
<action name="editActionZoomTextIn">
<property name="text">
<string>Zoom In</string>
<string>Increase Font Size</string>
</property>
<property name="shortcut">
<string>Ctrl++</string>
</property>
</action>
<action name="editActionZoomOut">
<action name="editActionZoomTextOut">
<property name="text">
<string>Zoom Out</string>
<string>Decrease Font Size</string>
</property>
<property name="shortcut">
<string>Ctrl+-</string>
@ -874,9 +905,6 @@
<property name="text">
<string>Show Scale Markers</string>
</property>
<!--<property name="shortcut">
<string></string>
</property>-->
</action>
<action name="viewActionAnimate">
<property name="checkable">
@ -1124,6 +1152,10 @@
</property>
</action>
<action name="viewActionResetView">
<property name="icon">
<iconset resource="../openscad.qrc">
<normaloff>:/images/Command-Reset-32.png</normaloff>:/images/Command-Reset-32.png</iconset>
</property>
<property name="text">
<string>Reset View</string>
</property>
@ -1189,6 +1221,23 @@
<string>Hide toolbars</string>
</property>
</action>
<action name="editActionUnindent">
<property name="icon">
<iconset resource="../openscad.qrc">
<normaloff>:/images/Decrease-Indent-32.png</normaloff>:/images/Decrease-Indent-32.png</iconset>
</property>
<property name="text">
<string>U&amp;nindent</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+I</string>
</property>
</action>
<action name="helpActionCheatSheet">
<property name="text">
<string>Cheat Sheet</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -31,6 +31,7 @@
#include <QKeyEvent>
#include <QSettings>
#include <QStatusBar>
#include <boost/algorithm/string.hpp>
#include "GeometryCache.h"
#include "AutoUpdater.h"
#include "feature.h"
@ -49,18 +50,27 @@ class SettingsReader : public Settings::Visitor
{
QSettings settings;
const Value getValue(const Settings::SettingsEntry& entry, const std::string& value) const {
if (value.empty()) {
std::string trimmed_value(value);
boost::trim(trimmed_value);
if (trimmed_value.empty()) {
return entry.defaultValue();
}
try {
switch (entry.defaultValue().type()) {
case Value::STRING:
return Value(value);
return Value(trimmed_value);
case Value::NUMBER:
return Value(boost::lexical_cast<int>(value));
return Value(boost::lexical_cast<int>(trimmed_value));
case Value::BOOL:
return Value(boost::lexical_cast<bool>(value));
boost::to_lower(trimmed_value);
if ("false" == trimmed_value) {
return Value(false);
} else if ("true" == trimmed_value) {
return Value(true);
}
return Value(boost::lexical_cast<bool>(trimmed_value));
default:
assert(false && "invalid value type for settings");
}
@ -74,7 +84,9 @@ class SettingsReader : public Settings::Visitor
std::string key = entry.category() + "/" + entry.name();
std::string value = settings.value(QString::fromStdString(key)).toString().toStdString();
s->set(entry, getValue(entry, value));
const Value v = getValue(entry, value);
PRINTDB("SettingsReader R: %s = '%s' => '%s'", key.c_str() % value.c_str() % v.toString());
s->set(entry, v);
}
};
@ -87,9 +99,11 @@ class SettingsWriter : public Settings::Visitor
QString key = QString::fromStdString(entry.category() + "/" + entry.name());
if (entry.is_default()) {
settings.remove(key);
PRINTDB("SettingsWriter D: %s", key.toStdString().c_str());
} else {
Value value = s->get(entry);
settings.setValue(key, QString::fromStdString(value.toString()));
PRINTDB("SettingsWriter W: %s = '%s'", key.toStdString().c_str() % value.toString().c_str());
}
}
};
@ -471,6 +485,12 @@ void Preferences::on_launcherBox_toggled(bool state)
settings.setValue("launcher/showOnStartup", state);
}
void Preferences::on_checkBoxShowWarningsIn3dView_toggled(bool val)
{
Settings::Settings::inst()->set(Settings::Settings::showWarningsIn3dView, Value(val));
writeSettings();
}
void Preferences::on_spinBoxIndentationWidth_valueChanged(int val)
{
Settings::Settings::inst()->set(Settings::Settings::indentationWidth, Value(val));
@ -669,6 +689,7 @@ void Preferences::updateGUI()
this->checkBoxAutoIndent->setChecked(s->get(Settings::Settings::autoIndent).toBool());
this->checkBoxHighlightCurrentLine->setChecked(s->get(Settings::Settings::highlightCurrentLine).toBool());
this->checkBoxEnableBraceMatching->setChecked(s->get(Settings::Settings::enableBraceMatching).toBool());
this->checkBoxShowWarningsIn3dView->setChecked(s->get(Settings::Settings::showWarningsIn3dView).toBool());
}
void Preferences::initComboBox(QComboBox *comboBox, const Settings::SettingsEntry& entry)

View File

@ -46,6 +46,7 @@ public slots:
void on_launcherBox_toggled(bool);
void on_editorType_editTextChanged(const QString &);
void on_checkBoxShowWarningsIn3dView_toggled(bool);
//
// editor settings
//

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>621</width>
<height>413</height>
<width>675</width>
<height>476</height>
</rect>
</property>
<property name="sizePolicy">
@ -31,6 +31,9 @@
</property>
<widget class="QWidget" name="page3DView">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,3">
<item>
@ -75,6 +78,16 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkBoxShowWarningsIn3dView">
<property name="text">
<string>Show Warnings and Errors in 3D View</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageEditor">

View File

@ -51,10 +51,9 @@ public:
/*!
Copies the internal vector to the given destination
*/
void copy(std::vector<T> &dest) {
template <class OutputIterator> void copy(OutputIterator dest) {
this->getArray();
dest.resize(this->vec.size());
std::copy(this->vec.begin(), this->vec.end(), dest.begin());
std::copy(this->vec.begin(), this->vec.end(), dest);
}
private:

View File

@ -34,6 +34,7 @@
#include "qtgettext.h"
#include "UIUtils.h"
#include "PlatformUtils.h"
#include "openscad.h"
QFileInfo UIUtils::openFile(QWidget *parent)
{
@ -103,7 +104,17 @@ void UIUtils::openHomepageURL()
QDesktopServices::openUrl(QUrl("http://openscad.org/"));
}
static void openVersionedURL(QString url)
{
QDesktopServices::openUrl(QUrl(url.arg(versionnumber.c_str())));
}
void UIUtils::openUserManualURL()
{
QDesktopServices::openUrl(QUrl("http://www.openscad.org/documentation.html"));
openVersionedURL("http://www.openscad.org/documentation.html?version=%1");
}
void UIUtils::openCheatSheetURL()
{
openVersionedURL("http://www.openscad.org/cheatsheet/index.html?version=%1");
}

View File

@ -42,4 +42,6 @@ namespace UIUtils {
void openHomepageURL();
void openUserManualURL();
void openCheatSheetURL();
}

View File

@ -177,7 +177,7 @@ void Cache<Key,T>::trim(int m)
Node *u = n;
n = n->p;
#ifdef DEBUG
PRINTB("Trimming cache: %1% (%2% bytes)", *u->keyPtr % u->c);
PRINTB("Trimming cache: %1% (%2% bytes)", u->keyPtr->substr(0, 40) % u->c);
#endif
unlink(*u);
}

View File

@ -25,6 +25,7 @@
#include "svg.h"
#include "Reindexer.h"
#include "GeometryUtils.h"
#include <map>
#include <queue>
@ -888,7 +889,7 @@ namespace CGALUtils {
return err;
}
#endif
#if 1
#if 0
bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps)
{
bool err = false;
@ -936,7 +937,7 @@ namespace CGALUtils {
// CGAL::Vector_3<CGAL_Kernel3> nvec = plane.orthogonal_vector();
// K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z()));
std::vector<Polygon> triangles;
bool err = CGALUtils::tessellatePolygonWithHoles(polyholes, triangles, NULL);
bool err = CGALUtils::tessellatePolygonWithHolesNew(polyholes, triangles, NULL);
if (!err) {
BOOST_FOREACH(const Polygon &p, triangles) {
if (p.size() != 3) {
@ -952,6 +953,141 @@ namespace CGALUtils {
}
return err;
}
#endif
#if 0
bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps)
{
bool err = false;
CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti;
CGAL_forall_halffacets(hfaceti, N) {
CGAL::Plane_3<CGAL_Kernel3> plane(hfaceti->plane());
PolyholeK polyholes;
// the 0-mark-volume is the 'empty' volume of space. skip it.
if (hfaceti->incident_volume()->mark()) continue;
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator cyclei;
CGAL_forall_facet_cycles_of(cyclei, hfaceti) {
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(cyclei);
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c2(c1);
PolygonK polygon;
CGAL_For_all(c1, c2) {
CGAL_Point_3 p = c1->source()->center_vertex()->point();
float v[3] = { CGAL::to_double(p.x()), CGAL::to_double(p.y()), CGAL::to_double(p.z()) };
polygon.push_back(Vertex3K(v[0], v[1], v[2]));
}
polyholes.push_back(polygon);
}
std::cout << "---\n";
BOOST_FOREACH(const PolygonK &poly, polyholes) {
BOOST_FOREACH(const Vertex3K &v, poly) {
std::cout << v.x() << "," << v.y() << "," << v.z() << "\n";
}
std::cout << "\n";
}
std::cout << "-\n";
/* at this stage, we have a sequence of polygons. the first
is the "outside edge' or 'body' or 'border', and the rest of the
polygons are 'holes' within the first. there are several
options here to get rid of the holes. we choose to go ahead
and let the tessellater deal with the holes, and then
just output the resulting 3d triangles*/
// We cannot trust the plane from Nef polyhedron to be correct.
// Passing an incorrect normal vector can cause a crash in the constrained delaunay triangulator
// See http://cgal-discuss.949826.n4.nabble.com/Nef3-Wrong-normal-vector-reported-causes-triangulator-crash-tt4660282.html
// CGAL::Vector_3<CGAL_Kernel3> nvec = plane.orthogonal_vector();
// K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z()));
std::vector<Polygon> triangles;
bool err = CGALUtils::tessellatePolygonWithHolesNew(polyholes, triangles, NULL);
if (!err) {
BOOST_FOREACH(const Polygon &p, triangles) {
if (p.size() != 3) {
PRINT("WARNING: triangle doesn't have 3 points. skipping");
continue;
}
ps.append_poly();
ps.append_vertex(p[0].x(), p[0].y(), p[0].z());
ps.append_vertex(p[1].x(), p[1].y(), p[1].z());
ps.append_vertex(p[2].x(), p[2].y(), p[2].z());
// std::cout << p[0].x() << "," << p[0].y() << "," << p[0].z() << "\n";
// std::cout << p[1].x() << "," << p[1].y() << "," << p[1].z() << "\n";
// std::cout << p[2].x() << "," << p[2].y() << "," << p[2].z() << "\n\n";
}
}
}
return err;
}
#endif
#if 1
bool createPolySetFromNefPolyhedron3(const CGAL_Nef_polyhedron3 &N, PolySet &ps)
{
bool err = false;
CGAL_Nef_polyhedron3::Halffacet_const_iterator hfaceti;
CGAL_forall_halffacets(hfaceti, N) {
CGAL::Plane_3<CGAL_Kernel3> plane(hfaceti->plane());
// Since we're downscaling to float, vertices might merge during this conversion.
// To avoid passing equal vertices to the tessellator, we remove consecutively identical
// vertices.
Reindexer<Vector3f> uniqueVertices;
IndexedPolygons polyhole;
// the 0-mark-volume is the 'empty' volume of space. skip it.
if (hfaceti->incident_volume()->mark()) continue;
CGAL_Nef_polyhedron3::Halffacet_cycle_const_iterator cyclei;
CGAL_forall_facet_cycles_of(cyclei, hfaceti) {
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c1(cyclei);
CGAL_Nef_polyhedron3::SHalfedge_around_facet_const_circulator c2(c1);
polyhole.faces.push_back(IndexedFace());
IndexedFace &currface = polyhole.faces.back();
CGAL_For_all(c1, c2) {
CGAL_Point_3 p = c1->source()->center_vertex()->point();
// Create vertex indices and remove consecutive duplicate vertices
int idx = uniqueVertices.lookup(vector_convert<Vector3f>(p));
if (currface.empty() || idx != currface.back()) currface.push_back(idx);
}
if (currface.front() == currface.back()) currface.pop_back();
if (currface.size() < 3) polyhole.faces.pop_back(); // Cull empty triangles
}
uniqueVertices.copy(std::back_inserter(polyhole.vertices));
#if 0 // For debugging
std::cerr << "---\n";
std::cerr.precision(20);
BOOST_FOREACH(const IndexedFace &poly, polyhole.faces) {
BOOST_FOREACH(int i, poly) {
std::cerr << polyhole.vertices[i][0] << "," << polyhole.vertices[i][1] << "," << polyhole.vertices[i][2] << "\n";
}
std::cerr << "\n";
}
std::cerr << "-\n";
#endif
/* at this stage, we have a sequence of polygons. the first
is the "outside edge' or 'body' or 'border', and the rest of the
polygons are 'holes' within the first. there are several
options here to get rid of the holes. we choose to go ahead
and let the tessellater deal with the holes, and then
just output the resulting 3d triangles*/
// We cannot trust the plane from Nef polyhedron to be correct.
// Passing an incorrect normal vector can cause a crash in the constrained delaunay triangulator
// See http://cgal-discuss.949826.n4.nabble.com/Nef3-Wrong-normal-vector-reported-causes-triangulator-crash-tt4660282.html
// CGAL::Vector_3<CGAL_Kernel3> nvec = plane.orthogonal_vector();
// K::Vector_3 normal(CGAL::to_double(nvec.x()), CGAL::to_double(nvec.y()), CGAL::to_double(nvec.z()));
std::vector<IndexedTriangle> triangles;
bool err = GeometryUtils::tessellatePolygonWithHoles(polyhole, triangles, NULL);
const Vector3f *verts = &polyhole.vertices.front();
if (!err) {
BOOST_FOREACH(const Vector3i &t, triangles) {
ps.append_poly();
ps.append_vertex(verts[t[0]]);
ps.append_vertex(verts[t[1]]);
ps.append_vertex(verts[t[2]]);
}
}
}
return err;
}
#endif
CGAL_Nef_polyhedron *createNefPolyhedronFromGeometry(const Geometry &geom)
{

View File

@ -36,9 +36,6 @@ namespace CGALUtils {
bool tessellatePolygonWithHoles(const PolyholeK &polygons,
Polygons &triangles,
const K::Vector_3 *normal = NULL);
bool tessellate3DFaceWithHolesNew(std::vector<CGAL_Polygon_3> &polygons,
Polygons &triangles,
CGAL::Plane_3<CGAL_Kernel3> &plane);
bool tessellate3DFaceWithHoles(std::vector<CGAL_Polygon_3> &polygons,
std::vector<CGAL_Polygon_3> &triangles,
CGAL::Plane_3<CGAL_Kernel3> &plane);

View File

@ -1,6 +1,11 @@
#include "grid.h"
namespace Eigen {
size_t hash_value(Vector3f const &v) {
size_t seed = 0;
for (int i=0;i<3;i++) boost::hash_combine(seed, v[i]);
return seed;
}
size_t hash_value(Vector3d const &v) {
size_t seed = 0;
for (int i=0;i<3;i++) boost::hash_combine(seed, v[i]);

View File

@ -90,6 +90,7 @@ public:
typedef Eigen::Matrix<int64_t, 3, 1> Vector3l;
namespace Eigen {
size_t hash_value(Vector3f const &v);
size_t hash_value(Vector3d const &v);
size_t hash_value(Vector3l const &v);
}

View File

@ -25,8 +25,8 @@ LaunchingScreen::LaunchingScreen(QWidget *parent) : QDialog(parent)
{
LaunchingScreen::inst = this;
setupUi(this);
this->setStyleSheet("QDialog {background-image:url(':/icons/background.png')}"
"QPushButton {color:white;}");
this->setStyleSheet("QDialog {background-image:url(':/icons/background.png')} QPushButton {color:white;}");
this->versionNumberLabel->setText(openscad_version.c_str());

226
src/libtess2/Include/tesselator.h Executable file
View File

@ -0,0 +1,226 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Mikko Mononen, July 2009.
*/
#ifndef TESSELATOR_H
#define TESSELATOR_H
#ifdef __cplusplus
extern "C" {
#endif
// See OpenGL Red Book for description of the winding rules
// http://www.glprogramming.com/red/chapter11.html
enum TessWindingRule
{
TESS_WINDING_ODD,
TESS_WINDING_NONZERO,
TESS_WINDING_POSITIVE,
TESS_WINDING_NEGATIVE,
TESS_WINDING_ABS_GEQ_TWO,
};
// The contents of the tessGetElements() depends on element type being passed to tessTesselate().
// Tesselation result element types:
// TESS_POLYGONS
// Each element in the element array is polygon defined as 'polySize' number of vertex indices.
// If a polygon has than 'polySize' vertices, the remaining indices are stored as TESS_UNDEF.
// Example, drawing a polygon:
// const int nelems = tessGetElementCount(tess);
// const TESSindex* elems = tessGetElements(tess);
// for (int i = 0; i < nelems; i++) {
// const TESSindex* poly = &elems[i * polySize];
// glBegin(GL_POLYGON);
// for (int j = 0; j < polySize; j++) {
// if (poly[j] == TESS_UNDEF) break;
// glVertex2fv(&verts[poly[j]*vertexSize]);
// }
// glEnd();
// }
//
// TESS_CONNECTED_POLYGONS
// Each element in the element array is polygon defined as 'polySize' number of vertex indices,
// followed by 'polySize' indices to neighour polygons, that is each element is 'polySize' * 2 indices.
// If a polygon has than 'polySize' vertices, the remaining indices are stored as TESS_UNDEF.
// If a polygon edge is a boundary, that is, not connected to another polygon, the neighbour index is TESS_UNDEF.
// Example, flood fill based on seed polygon:
// const int nelems = tessGetElementCount(tess);
// const TESSindex* elems = tessGetElements(tess);
// unsigned char* visited = (unsigned char*)calloc(nelems);
// TESSindex stack[50];
// int nstack = 0;
// stack[nstack++] = seedPoly;
// visited[startPoly] = 1;
// while (nstack > 0) {
// TESSindex idx = stack[--nstack];
// const TESSindex* poly = &elems[idx * polySize * 2];
// const TESSindex* nei = &poly[polySize];
// for (int i = 0; i < polySize; i++) {
// if (poly[i] == TESS_UNDEF) break;
// if (nei[i] != TESS_UNDEF && !visited[nei[i]])
// stack[nstack++] = nei[i];
// visited[nei[i]] = 1;
// }
// }
// }
//
// TESS_BOUNDARY_CONTOURS
// Each element in the element array is [base index, count] pair defining a range of vertices for a contour.
// The first value is index to first vertex in contour and the second value is number of vertices in the contour.
// Example, drawing contours:
// const int nelems = tessGetElementCount(tess);
// const TESSindex* elems = tessGetElements(tess);
// for (int i = 0; i < nelems; i++) {
// const TESSindex base = elems[i * 2];
// const TESSindex count = elems[i * 2 + 1];
// glBegin(GL_LINE_LOOP);
// for (int j = 0; j < count; j++) {
// glVertex2fv(&verts[(base+j) * vertexSize]);
// }
// glEnd();
// }
//
// TESS_CONSTRAINED_DELAUNAY_TRIANGLES
// Similar to TESS_POLYGONS, but we output only triangles and we attempt to provide a valid
// Constrained Delaunay triangulation.
enum TessElementType
{
TESS_POLYGONS,
TESS_CONNECTED_POLYGONS,
TESS_BOUNDARY_CONTOURS,
TESS_CONSTRAINED_DELAUNAY_TRIANGLES
};
typedef float TESSreal;
typedef int TESSindex;
typedef struct TESStesselator TESStesselator;
typedef struct TESSalloc TESSalloc;
#define TESS_UNDEF (~(TESSindex)0)
#define TESS_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
// Custom memory allocator interface.
// The internal memory allocator allocates mesh edges, vertices and faces
// as well as dictionary nodes and active regions in buckets and uses simple
// freelist to speed up the allocation. The bucket size should roughly match your
// expected input data. For example if you process only hundreds of vertices,
// a bucket size of 128 might be ok, where as when processing thousands of vertices
// bucket size of 1024 might be approproate. The bucket size is a compromise between
// how often to allocate memory from the system versus how much extra space the system
// should allocate. Reasonable defaults are show in commects below, they will be used if
// the bucket sizes are zero.
//
// The use may left the memrealloc to be null. In that case, the tesselator will not try to
// dynamically grow int's internal arrays. The tesselator only needs the reallocation when it
// has found intersecting segments and needs to add new vertex. This defency can be cured by
// allocating some extra vertices beforehand. The 'extraVertices' variable allows to specify
// number of expected extra vertices.
struct TESSalloc
{
void *(*memalloc)( void *userData, unsigned int size );
void *(*memrealloc)( void *userData, void* ptr, unsigned int size );
void (*memfree)( void *userData, void *ptr );
void* userData; // User data passed to the allocator functions.
int meshEdgeBucketSize; // 512
int meshVertexBucketSize; // 512
int meshFaceBucketSize; // 256
int dictNodeBucketSize; // 512
int regionBucketSize; // 256
int extraVertices; // Number of extra vertices allocated for the priority queue.
};
//
// Example use:
//
//
//
//
// tessNewTess() - Creates a new tesselator.
// Use tessDeleteTess() to delete the tesselator.
// Parameters:
// alloc - pointer to a filled TESSalloc struct or NULL to use default malloc based allocator.
// Returns:
// new tesselator object.
TESStesselator* tessNewTess( TESSalloc* alloc );
// tessDeleteTess() - Deletes a tesselator.
// Parameters:
// tess - pointer to tesselator object to be deleted.
void tessDeleteTess( TESStesselator *tess );
// tessAddContour() - Adds a contour to be tesselated.
// The type of the vertex coordinates is assumed to be TESSreal.
// Parameters:
// tess - pointer to tesselator object.
// size - number of coordinates per vertex. Must be 2 or 3.
// pointer - pointer to the first coordinate of the first vertex in the array.
// stride - defines offset in bytes between consecutive vertices.
// count - number of vertices in contour.
void tessAddContour( TESStesselator *tess, int size, const void* pointer, int stride, int count );
// tessTesselate() - tesselate contours.
// Parameters:
// tess - pointer to tesselator object.
// windingRule - winding rules used for tesselation, must be one of TessWindingRule.
// elementType - defines the tesselation result element type, must be one of TessElementType.
// polySize - defines maximum vertices per polygons if output is polygons. If elementType is TESS_CONSTRAINED_DELAUNAY_TRIANGLES, this parameter is ignored.
// vertexSize - defines the number of coordinates in tesselation result vertex, must be 2 or 3.
// normal - defines the normal of the input contours, of null the normal is calculated automatically.
// Returns:
// 1 if succeed, 0 if failed.
int tessTesselate( TESStesselator *tess, int windingRule, int elementType, int polySize, int vertexSize, const TESSreal* normal );
// tessGetVertexCount() - Returns number of vertices in the tesselated output.
int tessGetVertexCount( TESStesselator *tess );
// tessGetVertices() - Returns pointer to first coordinate of first vertex.
const TESSreal* tessGetVertices( TESStesselator *tess );
// tessGetVertexIndices() - Returns pointer to first vertex index.
// Vertex indices can be used to map the generated vertices to the original vertices.
// Every point added using tessAddContour() will get a new index starting at 0.
// New vertices generated at the intersections of segments are assigned value TESS_UNDEF.
const TESSindex* tessGetVertexIndices( TESStesselator *tess );
// tessGetElementCount() - Returns number of elements in the the tesselated output.
int tessGetElementCount( TESStesselator *tess );
// tessGetElements() - Returns pointer to the first element.
const TESSindex* tessGetElements( TESStesselator *tess );
#ifdef __cplusplus
};
#endif
#endif // TESSELATOR_H

191
src/libtess2/Source/bucketalloc.c Executable file
View File

@ -0,0 +1,191 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Mikko Mononen, July 2009.
*/
#include <stdio.h>
#include <stdlib.h>
#include "../Include/tesselator.h"
//#define CHECK_BOUNDS
typedef struct BucketAlloc BucketAlloc;
typedef struct Bucket Bucket;
struct Bucket
{
Bucket *next;
};
struct BucketAlloc
{
void *freelist;
Bucket *buckets;
unsigned int itemSize;
unsigned int bucketSize;
const char *name;
TESSalloc* alloc;
};
static int CreateBucket( struct BucketAlloc* ba )
{
size_t size;
Bucket* bucket;
void* freelist;
unsigned char* head;
unsigned char* it;
// Allocate memory for the bucket
size = sizeof(Bucket) + ba->itemSize * ba->bucketSize;
bucket = (Bucket*)ba->alloc->memalloc( ba->alloc->userData, size );
if ( !bucket )
return 0;
bucket->next = 0;
// Add the bucket into the list of buckets.
bucket->next = ba->buckets;
ba->buckets = bucket;
// Add new items to the free list.
freelist = ba->freelist;
head = (unsigned char*)bucket + sizeof(Bucket);
it = head + ba->itemSize * ba->bucketSize;
do
{
it -= ba->itemSize;
// Store pointer to next free item.
*((void**)it) = freelist;
// Pointer to next location containing a free item.
freelist = (void*)it;
}
while ( it != head );
// Update pointer to next location containing a free item.
ba->freelist = (void*)it;
return 1;
}
static void *NextFreeItem( struct BucketAlloc *ba )
{
return *(void**)ba->freelist;
}
struct BucketAlloc* createBucketAlloc( TESSalloc* alloc, const char* name,
unsigned int itemSize, unsigned int bucketSize )
{
BucketAlloc* ba = (BucketAlloc*)alloc->memalloc( alloc->userData, sizeof(BucketAlloc) );
ba->alloc = alloc;
ba->name = name;
ba->itemSize = itemSize;
if ( ba->itemSize < sizeof(void*) )
ba->itemSize = sizeof(void*);
ba->bucketSize = bucketSize;
ba->freelist = 0;
ba->buckets = 0;
if ( !CreateBucket( ba ) )
{
alloc->memfree( alloc->userData, ba );
return 0;
}
return ba;
}
void* bucketAlloc( struct BucketAlloc *ba )
{
void *it;
// If running out of memory, allocate new bucket and update the freelist.
if ( !ba->freelist || !NextFreeItem( ba ) )
{
if ( !CreateBucket( ba ) )
return 0;
}
// Pop item from in front of the free list.
it = ba->freelist;
ba->freelist = NextFreeItem( ba );
return it;
}
void bucketFree( struct BucketAlloc *ba, void *ptr )
{
#ifdef CHECK_BOUNDS
int inBounds = 0;
Bucket *bucket;
// Check that the pointer is allocated with this allocator.
bucket = ba->buckets;
while ( bucket )
{
void *bucketMin = (void*)((unsigned char*)bucket + sizeof(Bucket));
void *bucketMax = (void*)((unsigned char*)bucket + sizeof(Bucket) + ba->itemSize * ba->bucketSize);
if ( ptr >= bucketMin && ptr < bucketMax )
{
inBounds = 1;
break;
}
bucket = bucket->next;
}
if ( inBounds )
{
// Add the node in front of the free list.
*(void**)ptr = ba->freelist;
ba->freelist = ptr;
}
else
{
printf("ERROR! pointer 0x%p does not belong to allocator '%s'\n", ba->name);
}
#else
// Add the node in front of the free list.
*(void**)ptr = ba->freelist;
ba->freelist = ptr;
#endif
}
void deleteBucketAlloc( struct BucketAlloc *ba )
{
TESSalloc* alloc = ba->alloc;
Bucket *bucket = ba->buckets;
Bucket *next;
while ( bucket )
{
next = bucket->next;
alloc->memfree( alloc->userData, bucket );
bucket = next;
}
ba->freelist = 0;
ba->buckets = 0;
alloc->memfree( alloc->userData, ba );
}

View File

@ -0,0 +1,51 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Mikko Mononen, July 2009.
*/
#ifndef MEMALLOC_H
#define MEMALLOC_H
#ifdef __cplusplus
extern "C" {
#endif
#include "tesselator.h"
struct BucketAlloc *createBucketAlloc( TESSalloc* alloc, const char *name,
unsigned int itemSize, unsigned int bucketSize );
void *bucketAlloc( struct BucketAlloc *ba);
void bucketFree( struct BucketAlloc *ba, void *ptr );
void deleteBucketAlloc( struct BucketAlloc *ba );
#ifdef __cplusplus
};
#endif
#endif

109
src/libtess2/Source/dict.c Executable file
View File

@ -0,0 +1,109 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
#include <stddef.h>
#include "../Include/tesselator.h"
#include "bucketalloc.h"
#include "dict.h"
/* really tessDictListNewDict */
Dict *dictNewDict( TESSalloc* alloc, void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) )
{
Dict *dict = (Dict *)alloc->memalloc( alloc->userData, sizeof( Dict ));
DictNode *head;
if (dict == NULL) return NULL;
head = &dict->head;
head->key = NULL;
head->next = head;
head->prev = head;
dict->frame = frame;
dict->leq = leq;
if (alloc->dictNodeBucketSize < 16)
alloc->dictNodeBucketSize = 16;
if (alloc->dictNodeBucketSize > 4096)
alloc->dictNodeBucketSize = 4096;
dict->nodePool = createBucketAlloc( alloc, "Dict", sizeof(DictNode), alloc->dictNodeBucketSize );
return dict;
}
/* really tessDictListDeleteDict */
void dictDeleteDict( TESSalloc* alloc, Dict *dict )
{
deleteBucketAlloc( dict->nodePool );
alloc->memfree( alloc->userData, dict );
}
/* really tessDictListInsertBefore */
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key )
{
DictNode *newNode;
do {
node = node->prev;
} while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key));
newNode = (DictNode *)bucketAlloc( dict->nodePool );
if (newNode == NULL) return NULL;
newNode->key = key;
newNode->next = node->next;
node->next->prev = newNode;
newNode->prev = node;
node->next = newNode;
return newNode;
}
/* really tessDictListDelete */
void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/
{
node->next->prev = node->prev;
node->prev->next = node->next;
bucketFree( dict->nodePool, node );
}
/* really tessDictListSearch */
DictNode *dictSearch( Dict *dict, DictKey key )
{
DictNode *node = &dict->head;
do {
node = node->next;
} while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key));
return node;
}

74
src/libtess2/Source/dict.h Executable file
View File

@ -0,0 +1,74 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
#ifndef DICT_LIST_H
#define DICT_LIST_H
typedef void *DictKey;
typedef struct Dict Dict;
typedef struct DictNode DictNode;
Dict *dictNewDict( TESSalloc* alloc, void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) );
void dictDeleteDict( TESSalloc* alloc, Dict *dict );
/* Search returns the node with the smallest key greater than or equal
* to the given key. If there is no such key, returns a node whose
* key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc.
*/
DictNode *dictSearch( Dict *dict, DictKey key );
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
void dictDelete( Dict *dict, DictNode *node );
#define dictKey(n) ((n)->key)
#define dictSucc(n) ((n)->next)
#define dictPred(n) ((n)->prev)
#define dictMin(d) ((d)->head.next)
#define dictMax(d) ((d)->head.prev)
#define dictInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
/*** Private data structures ***/
struct DictNode {
DictKey key;
DictNode *next;
DictNode *prev;
};
struct Dict {
DictNode head;
void *frame;
struct BucketAlloc *nodePool;
int (*leq)(void *frame, DictKey key1, DictKey key2);
};
#endif

292
src/libtess2/Source/geom.c Executable file
View File

@ -0,0 +1,292 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
//#include "tesos.h"
#include <assert.h>
#include "mesh.h"
#include "geom.h"
#include <math.h>
int tesvertLeq( TESSvertex *u, TESSvertex *v )
{
/* Returns TRUE if u is lexicographically <= v. */
return VertLeq( u, v );
}
TESSreal tesedgeEval( TESSvertex *u, TESSvertex *v, TESSvertex *w )
{
/* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
* Returns v->t - (uw)(v->s), ie. the signed distance from uw to v.
* If uw is vertical (and thus passes thru v), the result is zero.
*
* The calculation is extremely accurate and stable, even when v
* is very close to u or w. In particular if we set v->t = 0 and
* let r be the negated result (this evaluates (uw)(v->s)), then
* r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t).
*/
TESSreal gapL, gapR;
assert( VertLeq( u, v ) && VertLeq( v, w ));
gapL = v->s - u->s;
gapR = w->s - v->s;
if( gapL + gapR > 0 ) {
if( gapL < gapR ) {
return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR));
} else {
return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR));
}
}
/* vertical line */
return 0;
}
TESSreal tesedgeSign( TESSvertex *u, TESSvertex *v, TESSvertex *w )
{
/* Returns a number whose sign matches EdgeEval(u,v,w) but which
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
* as v is above, on, or below the edge uw.
*/
TESSreal gapL, gapR;
assert( VertLeq( u, v ) && VertLeq( v, w ));
gapL = v->s - u->s;
gapR = w->s - v->s;
if( gapL + gapR > 0 ) {
return (v->t - w->t) * gapL + (v->t - u->t) * gapR;
}
/* vertical line */
return 0;
}
/***********************************************************************
* Define versions of EdgeSign, EdgeEval with s and t transposed.
*/
TESSreal testransEval( TESSvertex *u, TESSvertex *v, TESSvertex *w )
{
/* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w),
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
* Returns v->s - (uw)(v->t), ie. the signed distance from uw to v.
* If uw is vertical (and thus passes thru v), the result is zero.
*
* The calculation is extremely accurate and stable, even when v
* is very close to u or w. In particular if we set v->s = 0 and
* let r be the negated result (this evaluates (uw)(v->t)), then
* r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s).
*/
TESSreal gapL, gapR;
assert( TransLeq( u, v ) && TransLeq( v, w ));
gapL = v->t - u->t;
gapR = w->t - v->t;
if( gapL + gapR > 0 ) {
if( gapL < gapR ) {
return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR));
} else {
return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR));
}
}
/* vertical line */
return 0;
}
TESSreal testransSign( TESSvertex *u, TESSvertex *v, TESSvertex *w )
{
/* Returns a number whose sign matches TransEval(u,v,w) but which
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
* as v is above, on, or below the edge uw.
*/
TESSreal gapL, gapR;
assert( TransLeq( u, v ) && TransLeq( v, w ));
gapL = v->t - u->t;
gapR = w->t - v->t;
if( gapL + gapR > 0 ) {
return (v->s - w->s) * gapL + (v->s - u->s) * gapR;
}
/* vertical line */
return 0;
}
int tesvertCCW( TESSvertex *u, TESSvertex *v, TESSvertex *w )
{
/* For almost-degenerate situations, the results are not reliable.
* Unless the floating-point arithmetic can be performed without
* rounding errors, *any* implementation will give incorrect results
* on some degenerate inputs, so the client must have some way to
* handle this situation.
*/
return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0;
}
/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
* or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces
* this in the rare case that one argument is slightly negative.
* The implementation is extremely stable numerically.
* In particular it guarantees that the result r satisfies
* MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
* even when a and b differ greatly in magnitude.
*/
#define RealInterpolate(a,x,b,y) \
(a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \
((a <= b) ? ((b == 0) ? ((x+y) / 2) \
: (x + (y-x) * (a/(a+b)))) \
: (y + (x-y) * (b/(a+b)))))
#ifndef FOR_TRITE_TEST_PROGRAM
#define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y)
#else
/* Claim: the ONLY property the sweep algorithm relies on is that
* MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that.
*/
#include <stdlib.h>
extern int RandomInterpolate;
double Interpolate( double a, double x, double b, double y)
{
printf("*********************%d\n",RandomInterpolate);
if( RandomInterpolate ) {
a = 1.2 * drand48() - 0.1;
a = (a < 0) ? 0 : ((a > 1) ? 1 : a);
b = 1.0 - a;
}
return RealInterpolate(a,x,b,y);
}
#endif
#define Swap(a,b) if (1) { TESSvertex *t = a; a = b; b = t; } else
void tesedgeIntersect( TESSvertex *o1, TESSvertex *d1,
TESSvertex *o2, TESSvertex *d2,
TESSvertex *v )
/* Given edges (o1,d1) and (o2,d2), compute their point of intersection.
* The computed point is guaranteed to lie in the intersection of the
* bounding rectangles defined by each edge.
*/
{
TESSreal z1, z2;
/* This is certainly not the most efficient way to find the intersection
* of two line segments, but it is very numerically stable.
*
* Strategy: find the two middle vertices in the VertLeq ordering,
* and interpolate the intersection s-value from these. Then repeat
* using the TransLeq ordering to find the intersection t-value.
*/
if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); }
if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); }
if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
if( ! VertLeq( o2, d1 )) {
/* Technically, no intersection -- do our best */
v->s = (o2->s + d1->s) / 2;
} else if( VertLeq( d1, d2 )) {
/* Interpolate between o2 and d1 */
z1 = EdgeEval( o1, o2, d1 );
z2 = EdgeEval( o2, d1, d2 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->s = Interpolate( z1, o2->s, z2, d1->s );
} else {
/* Interpolate between o2 and d2 */
z1 = EdgeSign( o1, o2, d1 );
z2 = -EdgeSign( o1, d2, d1 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->s = Interpolate( z1, o2->s, z2, d2->s );
}
/* Now repeat the process for t */
if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); }
if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); }
if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
if( ! TransLeq( o2, d1 )) {
/* Technically, no intersection -- do our best */
v->t = (o2->t + d1->t) / 2;
} else if( TransLeq( d1, d2 )) {
/* Interpolate between o2 and d1 */
z1 = TransEval( o1, o2, d1 );
z2 = TransEval( o2, d1, d2 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->t = Interpolate( z1, o2->t, z2, d1->t );
} else {
/* Interpolate between o2 and d2 */
z1 = TransSign( o1, o2, d1 );
z2 = -TransSign( o1, d2, d1 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->t = Interpolate( z1, o2->t, z2, d2->t );
}
}
/*
Calculate the angle between v1-v2 and v1-v0
*/
TESSreal calcAngle( TESSvertex *v0, TESSvertex *v1, TESSvertex *v2 )
{
TESSreal num;
TESSreal den;
TESSreal a[2];
TESSreal b[2];
a[0] = v2->s - v1->s;
a[1] = v2->t - v1->t;
b[0] = v0->s - v1->s;
b[1] = v0->t - v1->t;
num = a[0] * b[0] + a[1] * b[1];
den = sqrt( a[0] * a[0] + a[1] * a[1] ) * sqrt( b[0] * b[0] + b[1] * b[1] );
if ( den > 0.0 ) num /= den;
if ( num < -1.0 ) num = -1.0;
if ( num > 1.0 ) num = 1.0;
return acos( num );
}
/*
Returns 1 is edge is locally delaunay
*/
int tesedgeIsLocallyDelaunay( TESShalfEdge *e )
{
return (calcAngle(e->Lnext->Org, e->Lnext->Lnext->Org, e->Org) +
calcAngle(e->Sym->Lnext->Org, e->Sym->Lnext->Lnext->Org, e->Sym->Org)) < (M_PI + 0.01);
}

78
src/libtess2/Source/geom.h Executable file
View File

@ -0,0 +1,78 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
#ifndef GEOM_H
#define GEOM_H
#include "mesh.h"
#ifdef NO_BRANCH_CONDITIONS
/* MIPS architecture has special instructions to evaluate boolean
* conditions -- more efficient than branching, IF you can get the
* compiler to generate the right instructions (SGI compiler doesn't)
*/
#define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t))
#define VertLeq(u,v) (((u)->s < (v)->s) | \
((u)->s == (v)->s & (u)->t <= (v)->t))
#else
#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t)
#define VertLeq(u,v) (((u)->s < (v)->s) || ((u)->s == (v)->s && (u)->t <= (v)->t))
#endif
#define EdgeEval(u,v,w) tesedgeEval(u,v,w)
#define EdgeSign(u,v,w) tesedgeSign(u,v,w)
/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */
#define TransLeq(u,v) (((u)->t < (v)->t) || ((u)->t == (v)->t && (u)->s <= (v)->s))
#define TransEval(u,v,w) testransEval(u,v,w)
#define TransSign(u,v,w) testransSign(u,v,w)
#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org )
#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst )
#define EdgeIsInternal(e) e->Rface && e->Rface->inside
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t))
#define VertCCW(u,v,w) tesvertCCW(u,v,w)
int tesvertLeq( TESSvertex *u, TESSvertex *v );
TESSreal tesedgeEval( TESSvertex *u, TESSvertex *v, TESSvertex *w );
TESSreal tesedgeSign( TESSvertex *u, TESSvertex *v, TESSvertex *w );
TESSreal testransEval( TESSvertex *u, TESSvertex *v, TESSvertex *w );
TESSreal testransSign( TESSvertex *u, TESSvertex *v, TESSvertex *w );
int tesvertCCW( TESSvertex *u, TESSvertex *v, TESSvertex *w );
void tesedgeIntersect( TESSvertex *o1, TESSvertex *d1, TESSvertex *o2, TESSvertex *d2, TESSvertex *v );
int tesedgeIsLocallyDelaunay( TESShalfEdge *e );
#endif

922
src/libtess2/Source/mesh.c Executable file
View File

@ -0,0 +1,922 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
//#include "tesos.h"
#include <stddef.h>
#include <assert.h>
#include "mesh.h"
#include "geom.h"
#include "bucketalloc.h"
#define TRUE 1
#define FALSE 0
/************************ Utility Routines ************************/
/* Allocate and free half-edges in pairs for efficiency.
* The *only* place that should use this fact is allocation/free.
*/
typedef struct { TESShalfEdge e, eSym; } EdgePair;
/* MakeEdge creates a new pair of half-edges which form their own loop.
* No vertex or face structures are allocated, but these must be assigned
* before the current edge operation is completed.
*/
static TESShalfEdge *MakeEdge( TESSmesh* mesh, TESShalfEdge *eNext )
{
TESShalfEdge *e;
TESShalfEdge *eSym;
TESShalfEdge *ePrev;
EdgePair *pair = (EdgePair *)bucketAlloc( mesh->edgeBucket );
if (pair == NULL) return NULL;
e = &pair->e;
eSym = &pair->eSym;
/* Make sure eNext points to the first edge of the edge pair */
if( eNext->Sym < eNext ) { eNext = eNext->Sym; }
/* Insert in circular doubly-linked list before eNext.
* Note that the prev pointer is stored in Sym->next.
*/
ePrev = eNext->Sym->next;
eSym->next = ePrev;
ePrev->Sym->next = e;
e->next = eNext;
eNext->Sym->next = eSym;
e->Sym = eSym;
e->Onext = e;
e->Lnext = eSym;
e->Org = NULL;
e->Lface = NULL;
e->winding = 0;
e->activeRegion = NULL;
e->mark = 0;
eSym->Sym = e;
eSym->Onext = eSym;
eSym->Lnext = e;
eSym->Org = NULL;
eSym->Lface = NULL;
eSym->winding = 0;
eSym->activeRegion = NULL;
eSym->mark = 0;
return e;
}
/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the
* CS348a notes (see mesh.h). Basically it modifies the mesh so that
* a->Onext and b->Onext are exchanged. This can have various effects
* depending on whether a and b belong to different face or vertex rings.
* For more explanation see tessMeshSplice() below.
*/
static void Splice( TESShalfEdge *a, TESShalfEdge *b )
{
TESShalfEdge *aOnext = a->Onext;
TESShalfEdge *bOnext = b->Onext;
aOnext->Sym->Lnext = b;
bOnext->Sym->Lnext = a;
a->Onext = bOnext;
b->Onext = aOnext;
}
/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the
* origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
* a place to insert the new vertex in the global vertex list. We insert
* the new vertex *before* vNext so that algorithms which walk the vertex
* list will not see the newly created vertices.
*/
static void MakeVertex( TESSvertex *newVertex,
TESShalfEdge *eOrig, TESSvertex *vNext )
{
TESShalfEdge *e;
TESSvertex *vPrev;
TESSvertex *vNew = newVertex;
assert(vNew != NULL);
/* insert in circular doubly-linked list before vNext */
vPrev = vNext->prev;
vNew->prev = vPrev;
vPrev->next = vNew;
vNew->next = vNext;
vNext->prev = vNew;
vNew->anEdge = eOrig;
/* leave coords, s, t undefined */
/* fix other edges on this vertex loop */
e = eOrig;
do {
e->Org = vNew;
e = e->Onext;
} while( e != eOrig );
}
/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left
* face of all edges in the face loop to which eOrig belongs. "fNext" gives
* a place to insert the new face in the global face list. We insert
* the new face *before* fNext so that algorithms which walk the face
* list will not see the newly created faces.
*/
static void MakeFace( TESSface *newFace, TESShalfEdge *eOrig, TESSface *fNext )
{
TESShalfEdge *e;
TESSface *fPrev;
TESSface *fNew = newFace;
assert(fNew != NULL);
/* insert in circular doubly-linked list before fNext */
fPrev = fNext->prev;
fNew->prev = fPrev;
fPrev->next = fNew;
fNew->next = fNext;
fNext->prev = fNew;
fNew->anEdge = eOrig;
fNew->trail = NULL;
fNew->marked = FALSE;
/* The new face is marked "inside" if the old one was. This is a
* convenience for the common case where a face has been split in two.
*/
fNew->inside = fNext->inside;
/* fix other edges on this face loop */
e = eOrig;
do {
e->Lface = fNew;
e = e->Lnext;
} while( e != eOrig );
}
/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym),
* and removes from the global edge list.
*/
static void KillEdge( TESSmesh *mesh, TESShalfEdge *eDel )
{
TESShalfEdge *ePrev, *eNext;
/* Half-edges are allocated in pairs, see EdgePair above */
if( eDel->Sym < eDel ) { eDel = eDel->Sym; }
/* delete from circular doubly-linked list */
eNext = eDel->next;
ePrev = eDel->Sym->next;
eNext->Sym->next = ePrev;
ePrev->Sym->next = eNext;
bucketFree( mesh->edgeBucket, eDel );
}
/* KillVertex( vDel ) destroys a vertex and removes it from the global
* vertex list. It updates the vertex loop to point to a given new vertex.
*/
static void KillVertex( TESSmesh *mesh, TESSvertex *vDel, TESSvertex *newOrg )
{
TESShalfEdge *e, *eStart = vDel->anEdge;
TESSvertex *vPrev, *vNext;
/* change the origin of all affected edges */
e = eStart;
do {
e->Org = newOrg;
e = e->Onext;
} while( e != eStart );
/* delete from circular doubly-linked list */
vPrev = vDel->prev;
vNext = vDel->next;
vNext->prev = vPrev;
vPrev->next = vNext;
bucketFree( mesh->vertexBucket, vDel );
}
/* KillFace( fDel ) destroys a face and removes it from the global face
* list. It updates the face loop to point to a given new face.
*/
static void KillFace( TESSmesh *mesh, TESSface *fDel, TESSface *newLface )
{
TESShalfEdge *e, *eStart = fDel->anEdge;
TESSface *fPrev, *fNext;
/* change the left face of all affected edges */
e = eStart;
do {
e->Lface = newLface;
e = e->Lnext;
} while( e != eStart );
/* delete from circular doubly-linked list */
fPrev = fDel->prev;
fNext = fDel->next;
fNext->prev = fPrev;
fPrev->next = fNext;
bucketFree( mesh->faceBucket, fDel );
}
/****************** Basic Edge Operations **********************/
/* tessMeshMakeEdge creates one edge, two vertices, and a loop (face).
* The loop consists of the two new half-edges.
*/
TESShalfEdge *tessMeshMakeEdge( TESSmesh *mesh )
{
TESSvertex *newVertex1 = (TESSvertex*)bucketAlloc(mesh->vertexBucket);
TESSvertex *newVertex2 = (TESSvertex*)bucketAlloc(mesh->vertexBucket);
TESSface *newFace = (TESSface*)bucketAlloc(mesh->faceBucket);
TESShalfEdge *e;
/* if any one is null then all get freed */
if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) {
if (newVertex1 != NULL) bucketFree( mesh->vertexBucket, newVertex1 );
if (newVertex2 != NULL) bucketFree( mesh->vertexBucket, newVertex2 );
if (newFace != NULL) bucketFree( mesh->faceBucket, newFace );
return NULL;
}
e = MakeEdge( mesh, &mesh->eHead );
if (e == NULL) return NULL;
MakeVertex( newVertex1, e, &mesh->vHead );
MakeVertex( newVertex2, e->Sym, &mesh->vHead );
MakeFace( newFace, e, &mesh->fHead );
return e;
}
/* tessMeshSplice( eOrg, eDst ) is the basic operation for changing the
* mesh connectivity and topology. It changes the mesh so that
* eOrg->Onext <- OLD( eDst->Onext )
* eDst->Onext <- OLD( eOrg->Onext )
* where OLD(...) means the value before the meshSplice operation.
*
* This can have two effects on the vertex structure:
* - if eOrg->Org != eDst->Org, the two vertices are merged together
* - if eOrg->Org == eDst->Org, the origin is split into two vertices
* In both cases, eDst->Org is changed and eOrg->Org is untouched.
*
* Similarly (and independently) for the face structure,
* - if eOrg->Lface == eDst->Lface, one loop is split into two
* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
*
* Some special cases:
* If eDst == eOrg, the operation has no effect.
* If eDst == eOrg->Lnext, the new face will have a single edge.
* If eDst == eOrg->Lprev, the old face will have a single edge.
* If eDst == eOrg->Onext, the new vertex will have a single edge.
* If eDst == eOrg->Oprev, the old vertex will have a single edge.
*/
int tessMeshSplice( TESSmesh* mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst )
{
int joiningLoops = FALSE;
int joiningVertices = FALSE;
if( eOrg == eDst ) return 1;
if( eDst->Org != eOrg->Org ) {
/* We are merging two disjoint vertices -- destroy eDst->Org */
joiningVertices = TRUE;
KillVertex( mesh, eDst->Org, eOrg->Org );
}
if( eDst->Lface != eOrg->Lface ) {
/* We are connecting two disjoint loops -- destroy eDst->Lface */
joiningLoops = TRUE;
KillFace( mesh, eDst->Lface, eOrg->Lface );
}
/* Change the edge structure */
Splice( eDst, eOrg );
if( ! joiningVertices ) {
TESSvertex *newVertex = (TESSvertex*)bucketAlloc( mesh->vertexBucket );
if (newVertex == NULL) return 0;
/* We split one vertex into two -- the new vertex is eDst->Org.
* Make sure the old vertex points to a valid half-edge.
*/
MakeVertex( newVertex, eDst, eOrg->Org );
eOrg->Org->anEdge = eOrg;
}
if( ! joiningLoops ) {
TESSface *newFace = (TESSface*)bucketAlloc( mesh->faceBucket );
if (newFace == NULL) return 0;
/* We split one loop into two -- the new loop is eDst->Lface.
* Make sure the old face points to a valid half-edge.
*/
MakeFace( newFace, eDst, eOrg->Lface );
eOrg->Lface->anEdge = eOrg;
}
return 1;
}
/* tessMeshDelete( eDel ) removes the edge eDel. There are several cases:
* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
* the newly created loop will contain eDel->Dst. If the deletion of eDel
* would create isolated vertices, those are deleted as well.
*
* This function could be implemented as two calls to tessMeshSplice
* plus a few calls to memFree, but this would allocate and delete
* unnecessary vertices and faces.
*/
int tessMeshDelete( TESSmesh *mesh, TESShalfEdge *eDel )
{
TESShalfEdge *eDelSym = eDel->Sym;
int joiningLoops = FALSE;
/* First step: disconnect the origin vertex eDel->Org. We make all
* changes to get a consistent mesh in this "intermediate" state.
*/
if( eDel->Lface != eDel->Rface ) {
/* We are joining two loops into one -- remove the left face */
joiningLoops = TRUE;
KillFace( mesh, eDel->Lface, eDel->Rface );
}
if( eDel->Onext == eDel ) {
KillVertex( mesh, eDel->Org, NULL );
} else {
/* Make sure that eDel->Org and eDel->Rface point to valid half-edges */
eDel->Rface->anEdge = eDel->Oprev;
eDel->Org->anEdge = eDel->Onext;
Splice( eDel, eDel->Oprev );
if( ! joiningLoops ) {
TESSface *newFace= (TESSface*)bucketAlloc( mesh->faceBucket );
if (newFace == NULL) return 0;
/* We are splitting one loop into two -- create a new loop for eDel. */
MakeFace( newFace, eDel, eDel->Lface );
}
}
/* Claim: the mesh is now in a consistent state, except that eDel->Org
* may have been deleted. Now we disconnect eDel->Dst.
*/
if( eDelSym->Onext == eDelSym ) {
KillVertex( mesh, eDelSym->Org, NULL );
KillFace( mesh, eDelSym->Lface, NULL );
} else {
/* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */
eDel->Lface->anEdge = eDelSym->Oprev;
eDelSym->Org->anEdge = eDelSym->Onext;
Splice( eDelSym, eDelSym->Oprev );
}
/* Any isolated vertices or faces have already been freed. */
KillEdge( mesh, eDel );
return 1;
}
/******************** Other Edge Operations **********************/
/* All these routines can be implemented with the basic edge
* operations above. They are provided for convenience and efficiency.
*/
/* tessMeshAddEdgeVertex( eOrg ) creates a new edge eNew such that
* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
* eOrg and eNew will have the same left face.
*/
TESShalfEdge *tessMeshAddEdgeVertex( TESSmesh *mesh, TESShalfEdge *eOrg )
{
TESShalfEdge *eNewSym;
TESShalfEdge *eNew = MakeEdge( mesh, eOrg );
if (eNew == NULL) return NULL;
eNewSym = eNew->Sym;
/* Connect the new edge appropriately */
Splice( eNew, eOrg->Lnext );
/* Set the vertex and face information */
eNew->Org = eOrg->Dst;
{
TESSvertex *newVertex= (TESSvertex*)bucketAlloc( mesh->vertexBucket );
if (newVertex == NULL) return NULL;
MakeVertex( newVertex, eNewSym, eNew->Org );
}
eNew->Lface = eNewSym->Lface = eOrg->Lface;
return eNew;
}
/* tessMeshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
* eOrg and eNew will have the same left face.
*/
TESShalfEdge *tessMeshSplitEdge( TESSmesh *mesh, TESShalfEdge *eOrg )
{
TESShalfEdge *eNew;
TESShalfEdge *tempHalfEdge= tessMeshAddEdgeVertex( mesh, eOrg );
if (tempHalfEdge == NULL) return NULL;
eNew = tempHalfEdge->Sym;
/* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */
Splice( eOrg->Sym, eOrg->Sym->Oprev );
Splice( eOrg->Sym, eNew );
/* Set the vertex and face information */
eOrg->Dst = eNew->Org;
eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */
eNew->Rface = eOrg->Rface;
eNew->winding = eOrg->winding; /* copy old winding information */
eNew->Sym->winding = eOrg->Sym->winding;
return eNew;
}
/* tessMeshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
* to eDst->Org, and returns the corresponding half-edge eNew.
* If eOrg->Lface == eDst->Lface, this splits one loop into two,
* and the newly created loop is eNew->Lface. Otherwise, two disjoint
* loops are merged into one, and the loop eDst->Lface is destroyed.
*
* If (eOrg == eDst), the new face will have only two edges.
* If (eOrg->Lnext == eDst), the old face is reduced to a single edge.
* If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges.
*/
TESShalfEdge *tessMeshConnect( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst )
{
TESShalfEdge *eNewSym;
int joiningLoops = FALSE;
TESShalfEdge *eNew = MakeEdge( mesh, eOrg );
if (eNew == NULL) return NULL;
eNewSym = eNew->Sym;
if( eDst->Lface != eOrg->Lface ) {
/* We are connecting two disjoint loops -- destroy eDst->Lface */
joiningLoops = TRUE;
KillFace( mesh, eDst->Lface, eOrg->Lface );
}
/* Connect the new edge appropriately */
Splice( eNew, eOrg->Lnext );
Splice( eNewSym, eDst );
/* Set the vertex and face information */
eNew->Org = eOrg->Dst;
eNewSym->Org = eDst->Org;
eNew->Lface = eNewSym->Lface = eOrg->Lface;
/* Make sure the old face points to a valid half-edge */
eOrg->Lface->anEdge = eNewSym;
if( ! joiningLoops ) {
TESSface *newFace= (TESSface*)bucketAlloc( mesh->faceBucket );
if (newFace == NULL) return NULL;
/* We split one loop into two -- the new loop is eNew->Lface */
MakeFace( newFace, eNew, eOrg->Lface );
}
return eNew;
}
/******************** Other Operations **********************/
/* tessMeshZapFace( fZap ) destroys a face and removes it from the
* global face list. All edges of fZap will have a NULL pointer as their
* left face. Any edges which also have a NULL pointer as their right face
* are deleted entirely (along with any isolated vertices this produces).
* An entire mesh can be deleted by zapping its faces, one at a time,
* in any order. Zapped faces cannot be used in further mesh operations!
*/
void tessMeshZapFace( TESSmesh *mesh, TESSface *fZap )
{
TESShalfEdge *eStart = fZap->anEdge;
TESShalfEdge *e, *eNext, *eSym;
TESSface *fPrev, *fNext;
/* walk around face, deleting edges whose right face is also NULL */
eNext = eStart->Lnext;
do {
e = eNext;
eNext = e->Lnext;
e->Lface = NULL;
if( e->Rface == NULL ) {
/* delete the edge -- see TESSmeshDelete above */
if( e->Onext == e ) {
KillVertex( mesh, e->Org, NULL );
} else {
/* Make sure that e->Org points to a valid half-edge */
e->Org->anEdge = e->Onext;
Splice( e, e->Oprev );
}
eSym = e->Sym;
if( eSym->Onext == eSym ) {
KillVertex( mesh, eSym->Org, NULL );
} else {
/* Make sure that eSym->Org points to a valid half-edge */
eSym->Org->anEdge = eSym->Onext;
Splice( eSym, eSym->Oprev );
}
KillEdge( mesh, e );
}
} while( e != eStart );
/* delete from circular doubly-linked list */
fPrev = fZap->prev;
fNext = fZap->next;
fNext->prev = fPrev;
fPrev->next = fNext;
bucketFree( mesh->faceBucket, fZap );
}
/* tessMeshNewMesh() creates a new mesh with no edges, no vertices,
* and no loops (what we usually call a "face").
*/
TESSmesh *tessMeshNewMesh( TESSalloc* alloc )
{
TESSvertex *v;
TESSface *f;
TESShalfEdge *e;
TESShalfEdge *eSym;
TESSmesh *mesh = (TESSmesh *)alloc->memalloc( alloc->userData, sizeof( TESSmesh ));
if (mesh == NULL) {
return NULL;
}
if (alloc->meshEdgeBucketSize < 16)
alloc->meshEdgeBucketSize = 16;
if (alloc->meshEdgeBucketSize > 4096)
alloc->meshEdgeBucketSize = 4096;
if (alloc->meshVertexBucketSize < 16)
alloc->meshVertexBucketSize = 16;
if (alloc->meshVertexBucketSize > 4096)
alloc->meshVertexBucketSize = 4096;
if (alloc->meshFaceBucketSize < 16)
alloc->meshFaceBucketSize = 16;
if (alloc->meshFaceBucketSize > 4096)
alloc->meshFaceBucketSize = 4096;
mesh->edgeBucket = createBucketAlloc( alloc, "Mesh Edges", sizeof(EdgePair), alloc->meshEdgeBucketSize );
mesh->vertexBucket = createBucketAlloc( alloc, "Mesh Vertices", sizeof(TESSvertex), alloc->meshVertexBucketSize );
mesh->faceBucket = createBucketAlloc( alloc, "Mesh Faces", sizeof(TESSface), alloc->meshFaceBucketSize );
v = &mesh->vHead;
f = &mesh->fHead;
e = &mesh->eHead;
eSym = &mesh->eHeadSym;
v->next = v->prev = v;
v->anEdge = NULL;
f->next = f->prev = f;
f->anEdge = NULL;
f->trail = NULL;
f->marked = FALSE;
f->inside = FALSE;
e->next = e;
e->Sym = eSym;
e->Onext = NULL;
e->Lnext = NULL;
e->Org = NULL;
e->Lface = NULL;
e->winding = 0;
e->activeRegion = NULL;
eSym->next = eSym;
eSym->Sym = e;
eSym->Onext = NULL;
eSym->Lnext = NULL;
eSym->Org = NULL;
eSym->Lface = NULL;
eSym->winding = 0;
eSym->activeRegion = NULL;
return mesh;
}
/* tessMeshUnion( mesh1, mesh2 ) forms the union of all structures in
* both meshes, and returns the new mesh (the old meshes are destroyed).
*/
TESSmesh *tessMeshUnion( TESSalloc* alloc, TESSmesh *mesh1, TESSmesh *mesh2 )
{
TESSface *f1 = &mesh1->fHead;
TESSvertex *v1 = &mesh1->vHead;
TESShalfEdge *e1 = &mesh1->eHead;
TESSface *f2 = &mesh2->fHead;
TESSvertex *v2 = &mesh2->vHead;
TESShalfEdge *e2 = &mesh2->eHead;
/* Add the faces, vertices, and edges of mesh2 to those of mesh1 */
if( f2->next != f2 ) {
f1->prev->next = f2->next;
f2->next->prev = f1->prev;
f2->prev->next = f1;
f1->prev = f2->prev;
}
if( v2->next != v2 ) {
v1->prev->next = v2->next;
v2->next->prev = v1->prev;
v2->prev->next = v1;
v1->prev = v2->prev;
}
if( e2->next != e2 ) {
e1->Sym->next->Sym->next = e2->next;
e2->next->Sym->next = e1->Sym->next;
e2->Sym->next->Sym->next = e1;
e1->Sym->next = e2->Sym->next;
}
alloc->memfree( alloc->userData, mesh2 );
return mesh1;
}
static int CountFaceVerts( TESSface *f )
{
TESShalfEdge *eCur = f->anEdge;
int n = 0;
do
{
n++;
eCur = eCur->Lnext;
}
while (eCur != f->anEdge);
return n;
}
int tessMeshMergeConvexFaces( TESSmesh *mesh, int maxVertsPerFace )
{
TESSface *f;
TESShalfEdge *eCur, *eNext, *eSym;
TESSvertex *vStart;
int curNv, symNv;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next )
{
// Skip faces which are outside the result.
if( !f->inside )
continue;
eCur = f->anEdge;
vStart = eCur->Org;
while (1)
{
eNext = eCur->Lnext;
eSym = eCur->Sym;
// Try to merge if the neighbour face is valid.
if( eSym && eSym->Lface && eSym->Lface->inside )
{
// Try to merge the neighbour faces if the resulting polygons
// does not exceed maximum number of vertices.
curNv = CountFaceVerts( f );
symNv = CountFaceVerts( eSym->Lface );
if( (curNv+symNv-2) <= maxVertsPerFace )
{
// Merge if the resulting poly is convex.
if( VertCCW( eCur->Lprev->Org, eCur->Org, eSym->Lnext->Lnext->Org ) &&
VertCCW( eSym->Lprev->Org, eSym->Org, eCur->Lnext->Lnext->Org ) )
{
eNext = eSym->Lnext;
if( !tessMeshDelete( mesh, eSym ) )
return 0;
eCur = 0;
}
}
}
if( eCur && eCur->Lnext->Org == vStart )
break;
// Continue to next edge.
eCur = eNext;
}
}
return 1;
}
void tessMeshFlipEdge( TESSmesh *mesh, TESShalfEdge *edge )
{
TESShalfEdge *a0 = edge;
TESShalfEdge *a1 = a0->Lnext;
TESShalfEdge *a2 = a1->Lnext;
TESShalfEdge *b0 = edge->Sym;
TESShalfEdge *b1 = b0->Lnext;
TESShalfEdge *b2 = b1->Lnext;
TESSvertex *aOrg = a0->Org;
TESSvertex *aOpp = a2->Org;
TESSvertex *bOrg = b0->Org;
TESSvertex *bOpp = b2->Org;
TESSface *fa = a0->Lface;
TESSface *fb = b0->Lface;
assert(EdgeIsInternal(edge));
assert(a2->Lnext == a0);
assert(b2->Lnext == b0);
a0->Org = bOpp;
a0->Onext = b1->Sym;
b0->Org = aOpp;
b0->Onext = a1->Sym;
a2->Onext = b0;
b2->Onext = a0;
b1->Onext = a2->Sym;
a1->Onext = b2->Sym;
a0->Lnext = a2;
a2->Lnext = b1;
b1->Lnext = a0;
b0->Lnext = b2;
b2->Lnext = a1;
a1->Lnext = b0;
a1->Lface = fb;
b1->Lface = fa;
fa->anEdge = a0;
fb->anEdge = b0;
if (aOrg->anEdge == a0) aOrg->anEdge = b1;
if (bOrg->anEdge == b0) bOrg->anEdge = a1;
assert( a0->Lnext->Onext->Sym == a0 );
assert( a0->Onext->Sym->Lnext == a0 );
assert( a0->Org->anEdge->Org == a0->Org );
assert( a1->Lnext->Onext->Sym == a1 );
assert( a1->Onext->Sym->Lnext == a1 );
assert( a1->Org->anEdge->Org == a1->Org );
assert( a2->Lnext->Onext->Sym == a2 );
assert( a2->Onext->Sym->Lnext == a2 );
assert( a2->Org->anEdge->Org == a2->Org );
assert( b0->Lnext->Onext->Sym == b0 );
assert( b0->Onext->Sym->Lnext == b0 );
assert( b0->Org->anEdge->Org == b0->Org );
assert( b1->Lnext->Onext->Sym == b1 );
assert( b1->Onext->Sym->Lnext == b1 );
assert( b1->Org->anEdge->Org == b1->Org );
assert( b2->Lnext->Onext->Sym == b2 );
assert( b2->Onext->Sym->Lnext == b2 );
assert( b2->Org->anEdge->Org == b2->Org );
assert(aOrg->anEdge->Org == aOrg);
assert(bOrg->anEdge->Org == bOrg);
assert(a0->Oprev->Onext->Org == a0->Org);
}
#ifdef DELETE_BY_ZAPPING
/* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
*/
void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh )
{
TESSface *fHead = &mesh->fHead;
while( fHead->next != fHead ) {
tessMeshZapFace( fHead->next );
}
assert( mesh->vHead.next == &mesh->vHead );
alloc->memfree( alloc->userData, mesh );
}
#else
/* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
*/
void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh )
{
deleteBucketAlloc(mesh->edgeBucket);
deleteBucketAlloc(mesh->vertexBucket);
deleteBucketAlloc(mesh->faceBucket);
alloc->memfree( alloc->userData, mesh );
}
#endif
#ifndef NDEBUG
/* tessMeshCheckMesh( mesh ) checks a mesh for self-consistency.
*/
void tessMeshCheckMesh( TESSmesh *mesh )
{
TESSface *fHead = &mesh->fHead;
TESSvertex *vHead = &mesh->vHead;
TESShalfEdge *eHead = &mesh->eHead;
TESSface *f, *fPrev;
TESSvertex *v, *vPrev;
TESShalfEdge *e, *ePrev;
fPrev = fHead;
for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) {
assert( f->prev == fPrev );
e = f->anEdge;
do {
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
assert( e->Lface == f );
e = e->Lnext;
} while( e != f->anEdge );
}
assert( f->prev == fPrev && f->anEdge == NULL );
vPrev = vHead;
for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) {
assert( v->prev == vPrev );
e = v->anEdge;
do {
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
assert( e->Org == v );
e = e->Onext;
} while( e != v->anEdge );
}
assert( v->prev == vPrev && v->anEdge == NULL );
ePrev = eHead;
for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) {
assert( e->Sym->next == ePrev->Sym );
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Org != NULL );
assert( e->Dst != NULL );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
}
assert( e->Sym->next == ePrev->Sym
&& e->Sym == &mesh->eHeadSym
&& e->Sym->Sym == e
&& e->Org == NULL && e->Dst == NULL
&& e->Lface == NULL && e->Rface == NULL );
}
#endif

269
src/libtess2/Source/mesh.h Executable file
View File

@ -0,0 +1,269 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
#ifndef MESH_H
#define MESH_H
#include "../Include/tesselator.h"
typedef struct TESSmesh TESSmesh;
typedef struct TESSvertex TESSvertex;
typedef struct TESSface TESSface;
typedef struct TESShalfEdge TESShalfEdge;
typedef struct ActiveRegion ActiveRegion;
/* The mesh structure is similar in spirit, notation, and operations
* to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives
* for the manipulation of general subdivisions and the computation of
* Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985).
* For a simplified description, see the course notes for CS348a,
* "Mathematical Foundations of Computer Graphics", available at the
* Stanford bookstore (and taught during the fall quarter).
* The implementation also borrows a tiny subset of the graph-based approach
* use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction
* to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988).
*
* The fundamental data structure is the "half-edge". Two half-edges
* go together to make an edge, but they point in opposite directions.
* Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym),
* its origin vertex (Org), the face on its left side (Lface), and the
* adjacent half-edges in the CCW direction around the origin vertex
* (Onext) and around the left face (Lnext). There is also a "next"
* pointer for the global edge list (see below).
*
* The notation used for mesh navigation:
* Sym = the mate of a half-edge (same edge, but opposite direction)
* Onext = edge CCW around origin vertex (keep same origin)
* Dnext = edge CCW around destination vertex (keep same dest)
* Lnext = edge CCW around left face (dest becomes new origin)
* Rnext = edge CCW around right face (origin becomes new dest)
*
* "prev" means to substitute CW for CCW in the definitions above.
*
* The mesh keeps global lists of all vertices, faces, and edges,
* stored as doubly-linked circular lists with a dummy header node.
* The mesh stores pointers to these dummy headers (vHead, fHead, eHead).
*
* The circular edge list is special; since half-edges always occur
* in pairs (e and e->Sym), each half-edge stores a pointer in only
* one direction. Starting at eHead and following the e->next pointers
* will visit each *edge* once (ie. e or e->Sym, but not both).
* e->Sym stores a pointer in the opposite direction, thus it is
* always true that e->Sym->next->Sym->next == e.
*
* Each vertex has a pointer to next and previous vertices in the
* circular list, and a pointer to a half-edge with this vertex as
* the origin (NULL if this is the dummy header). There is also a
* field "data" for client data.
*
* Each face has a pointer to the next and previous faces in the
* circular list, and a pointer to a half-edge with this face as
* the left face (NULL if this is the dummy header). There is also
* a field "data" for client data.
*
* Note that what we call a "face" is really a loop; faces may consist
* of more than one loop (ie. not simply connected), but there is no
* record of this in the data structure. The mesh may consist of
* several disconnected regions, so it may not be possible to visit
* the entire mesh by starting at a half-edge and traversing the edge
* structure.
*
* The mesh does NOT support isolated vertices; a vertex is deleted along
* with its last edge. Similarly when two faces are merged, one of the
* faces is deleted (see tessMeshDelete below). For mesh operations,
* all face (loop) and vertex pointers must not be NULL. However, once
* mesh manipulation is finished, TESSmeshZapFace can be used to delete
* faces of the mesh, one at a time. All external faces can be "zapped"
* before the mesh is returned to the client; then a NULL face indicates
* a region which is not part of the output polygon.
*/
struct TESSvertex {
TESSvertex *next; /* next vertex (never NULL) */
TESSvertex *prev; /* previous vertex (never NULL) */
TESShalfEdge *anEdge; /* a half-edge with this origin */
/* Internal data (keep hidden) */
TESSreal coords[3]; /* vertex location in 3D */
TESSreal s, t; /* projection onto the sweep plane */
int pqHandle; /* to allow deletion from priority queue */
TESSindex n; /* to allow identify unique vertices */
TESSindex idx; /* to allow map result to original verts */
};
struct TESSface {
TESSface *next; /* next face (never NULL) */
TESSface *prev; /* previous face (never NULL) */
TESShalfEdge *anEdge; /* a half edge with this left face */
/* Internal data (keep hidden) */
TESSface *trail; /* "stack" for conversion to strips */
TESSindex n; /* to allow identiy unique faces */
char marked; /* flag for conversion to strips */
char inside; /* this face is in the polygon interior */
};
struct TESShalfEdge {
TESShalfEdge *next; /* doubly-linked list (prev==Sym->next) */
TESShalfEdge *Sym; /* same edge, opposite direction */
TESShalfEdge *Onext; /* next edge CCW around origin */
TESShalfEdge *Lnext; /* next edge CCW around left face */
TESSvertex *Org; /* origin vertex (Overtex too long) */
TESSface *Lface; /* left face */
/* Internal data (keep hidden) */
ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */
int winding; /* change in winding number when crossing
from the right face to the left face */
int mark; /* Used by the Edge Flip algorithm */
};
#define Rface Sym->Lface
#define Dst Sym->Org
#define Oprev Sym->Lnext
#define Lprev Onext->Sym
#define Dprev Lnext->Sym
#define Rprev Sym->Onext
#define Dnext Rprev->Sym /* 3 pointers */
#define Rnext Oprev->Sym /* 3 pointers */
struct TESSmesh {
TESSvertex vHead; /* dummy header for vertex list */
TESSface fHead; /* dummy header for face list */
TESShalfEdge eHead; /* dummy header for edge list */
TESShalfEdge eHeadSym; /* and its symmetric counterpart */
struct BucketAlloc* edgeBucket;
struct BucketAlloc* vertexBucket;
struct BucketAlloc* faceBucket;
};
/* The mesh operations below have three motivations: completeness,
* convenience, and efficiency. The basic mesh operations are MakeEdge,
* Splice, and Delete. All the other edge operations can be implemented
* in terms of these. The other operations are provided for convenience
* and/or efficiency.
*
* When a face is split or a vertex is added, they are inserted into the
* global list *before* the existing vertex or face (ie. e->Org or e->Lface).
* This makes it easier to process all vertices or faces in the global lists
* without worrying about processing the same data twice. As a convenience,
* when a face is split, the "inside" flag is copied from the old face.
* Other internal data (v->data, v->activeRegion, f->data, f->marked,
* f->trail, e->winding) is set to zero.
*
* ********************** Basic Edge Operations **************************
*
* tessMeshMakeEdge( mesh ) creates one edge, two vertices, and a loop.
* The loop (face) consists of the two new half-edges.
*
* tessMeshSplice( eOrg, eDst ) is the basic operation for changing the
* mesh connectivity and topology. It changes the mesh so that
* eOrg->Onext <- OLD( eDst->Onext )
* eDst->Onext <- OLD( eOrg->Onext )
* where OLD(...) means the value before the meshSplice operation.
*
* This can have two effects on the vertex structure:
* - if eOrg->Org != eDst->Org, the two vertices are merged together
* - if eOrg->Org == eDst->Org, the origin is split into two vertices
* In both cases, eDst->Org is changed and eOrg->Org is untouched.
*
* Similarly (and independently) for the face structure,
* - if eOrg->Lface == eDst->Lface, one loop is split into two
* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
*
* tessMeshDelete( eDel ) removes the edge eDel. There are several cases:
* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
* the newly created loop will contain eDel->Dst. If the deletion of eDel
* would create isolated vertices, those are deleted as well.
*
* ********************** Other Edge Operations **************************
*
* tessMeshAddEdgeVertex( eOrg ) creates a new edge eNew such that
* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
* eOrg and eNew will have the same left face.
*
* tessMeshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
* eOrg and eNew will have the same left face.
*
* tessMeshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
* to eDst->Org, and returns the corresponding half-edge eNew.
* If eOrg->Lface == eDst->Lface, this splits one loop into two,
* and the newly created loop is eNew->Lface. Otherwise, two disjoint
* loops are merged into one, and the loop eDst->Lface is destroyed.
*
* ************************ Other Operations *****************************
*
* tessMeshNewMesh() creates a new mesh with no edges, no vertices,
* and no loops (what we usually call a "face").
*
* tessMeshUnion( mesh1, mesh2 ) forms the union of all structures in
* both meshes, and returns the new mesh (the old meshes are destroyed).
*
* tessMeshDeleteMesh( mesh ) will free all storage for any valid mesh.
*
* tessMeshZapFace( fZap ) destroys a face and removes it from the
* global face list. All edges of fZap will have a NULL pointer as their
* left face. Any edges which also have a NULL pointer as their right face
* are deleted entirely (along with any isolated vertices this produces).
* An entire mesh can be deleted by zapping its faces, one at a time,
* in any order. Zapped faces cannot be used in further mesh operations!
*
* tessMeshCheckMesh( mesh ) checks a mesh for self-consistency.
*/
TESShalfEdge *tessMeshMakeEdge( TESSmesh *mesh );
int tessMeshSplice( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst );
int tessMeshDelete( TESSmesh *mesh, TESShalfEdge *eDel );
TESShalfEdge *tessMeshAddEdgeVertex( TESSmesh *mesh, TESShalfEdge *eOrg );
TESShalfEdge *tessMeshSplitEdge( TESSmesh *mesh, TESShalfEdge *eOrg );
TESShalfEdge *tessMeshConnect( TESSmesh *mesh, TESShalfEdge *eOrg, TESShalfEdge *eDst );
TESSmesh *tessMeshNewMesh( TESSalloc* alloc );
TESSmesh *tessMeshUnion( TESSalloc* alloc, TESSmesh *mesh1, TESSmesh *mesh2 );
int tessMeshMergeConvexFaces( TESSmesh *mesh, int maxVertsPerFace );
void tessMeshDeleteMesh( TESSalloc* alloc, TESSmesh *mesh );
void tessMeshZapFace( TESSmesh *mesh, TESSface *fZap );
void tessMeshFlipEdge( TESSmesh *mesh, TESShalfEdge *edge );
#ifdef NDEBUG
#define tessMeshCheckMesh( mesh )
#else
void tessMeshCheckMesh( TESSmesh *mesh );
#endif
#endif

514
src/libtess2/Source/priorityq.c Executable file
View File

@ -0,0 +1,514 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
//#include "tesos.h"
#include <stddef.h>
#include <assert.h>
#include "../Include/tesselator.h"
#include "priorityq.h"
#define INIT_SIZE 32
#define TRUE 1
#define FALSE 0
#ifdef FOR_TRITE_TEST_PROGRAM
#define LEQ(x,y) (*pq->leq)(x,y)
#else
/* Violates modularity, but a little faster */
#include "geom.h"
#define LEQ(x,y) VertLeq((TESSvertex *)x, (TESSvertex *)y)
#endif
/* Include all the code for the regular heap-based queue here. */
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
#define pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key)
#define pqHeapIsEmpty(pq) ((pq)->size == 0)
/* really pqHeapNewPriorityQHeap */
PriorityQHeap *pqHeapNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) )
{
PriorityQHeap *pq = (PriorityQHeap *)alloc->memalloc( alloc->userData, sizeof( PriorityQHeap ));
if (pq == NULL) return NULL;
pq->size = 0;
pq->max = size;
pq->nodes = (PQnode *)alloc->memalloc( alloc->userData, (size + 1) * sizeof(pq->nodes[0]) );
if (pq->nodes == NULL) {
alloc->memfree( alloc->userData, pq );
return NULL;
}
pq->handles = (PQhandleElem *)alloc->memalloc( alloc->userData, (size + 1) * sizeof(pq->handles[0]) );
if (pq->handles == NULL) {
alloc->memfree( alloc->userData, pq->nodes );
alloc->memfree( alloc->userData, pq );
return NULL;
}
pq->initialized = FALSE;
pq->freeList = 0;
pq->leq = leq;
pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */
pq->handles[1].key = NULL;
return pq;
}
/* really pqHeapDeletePriorityQHeap */
void pqHeapDeletePriorityQ( TESSalloc* alloc, PriorityQHeap *pq )
{
alloc->memfree( alloc->userData, pq->handles );
alloc->memfree( alloc->userData, pq->nodes );
alloc->memfree( alloc->userData, pq );
}
static void FloatDown( PriorityQHeap *pq, int curr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hCurr, hChild;
int child;
hCurr = n[curr].handle;
for( ;; ) {
child = curr << 1;
if( child < pq->size && LEQ( h[n[child+1].handle].key,
h[n[child].handle].key )) {
++child;
}
assert(child <= pq->max);
hChild = n[child].handle;
if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) {
n[curr].handle = hCurr;
h[hCurr].node = curr;
break;
}
n[curr].handle = hChild;
h[hChild].node = curr;
curr = child;
}
}
static void FloatUp( PriorityQHeap *pq, int curr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hCurr, hParent;
int parent;
hCurr = n[curr].handle;
for( ;; ) {
parent = curr >> 1;
hParent = n[parent].handle;
if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) {
n[curr].handle = hCurr;
h[hCurr].node = curr;
break;
}
n[curr].handle = hParent;
h[hParent].node = curr;
curr = parent;
}
}
/* really pqHeapInit */
void pqHeapInit( PriorityQHeap *pq )
{
int i;
/* This method of building a heap is O(n), rather than O(n lg n). */
for( i = pq->size; i >= 1; --i ) {
FloatDown( pq, i );
}
pq->initialized = TRUE;
}
/* really pqHeapInsert */
/* returns INV_HANDLE iff out of memory */
PQhandle pqHeapInsert( TESSalloc* alloc, PriorityQHeap *pq, PQkey keyNew )
{
int curr;
PQhandle free;
curr = ++ pq->size;
if( (curr*2) > pq->max ) {
if (!alloc->memrealloc)
{
return INV_HANDLE;
}
else
{
PQnode *saveNodes= pq->nodes;
PQhandleElem *saveHandles= pq->handles;
// If the heap overflows, double its size.
pq->max <<= 1;
pq->nodes = (PQnode *)alloc->memrealloc( alloc->userData, pq->nodes,
(size_t)((pq->max + 1) * sizeof( pq->nodes[0] )));
if (pq->nodes == NULL) {
pq->nodes = saveNodes; // restore ptr to free upon return
return INV_HANDLE;
}
pq->handles = (PQhandleElem *)alloc->memrealloc( alloc->userData, pq->handles,
(size_t) ((pq->max + 1) * sizeof( pq->handles[0] )));
if (pq->handles == NULL) {
pq->handles = saveHandles; // restore ptr to free upon return
return INV_HANDLE;
}
}
}
if( pq->freeList == 0 ) {
free = curr;
} else {
free = pq->freeList;
pq->freeList = pq->handles[free].node;
}
pq->nodes[curr].handle = free;
pq->handles[free].node = curr;
pq->handles[free].key = keyNew;
if( pq->initialized ) {
FloatUp( pq, curr );
}
assert(free != INV_HANDLE);
return free;
}
/* really pqHeapExtractMin */
PQkey pqHeapExtractMin( PriorityQHeap *pq )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hMin = n[1].handle;
PQkey min = h[hMin].key;
if( pq->size > 0 ) {
n[1].handle = n[pq->size].handle;
h[n[1].handle].node = 1;
h[hMin].key = NULL;
h[hMin].node = pq->freeList;
pq->freeList = hMin;
if( -- pq->size > 0 ) {
FloatDown( pq, 1 );
}
}
return min;
}
/* really pqHeapDelete */
void pqHeapDelete( PriorityQHeap *pq, PQhandle hCurr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
int curr;
assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL );
curr = h[hCurr].node;
n[curr].handle = n[pq->size].handle;
h[n[curr].handle].node = curr;
if( curr <= -- pq->size ) {
if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) {
FloatDown( pq, curr );
} else {
FloatUp( pq, curr );
}
}
h[hCurr].key = NULL;
h[hCurr].node = pq->freeList;
pq->freeList = hCurr;
}
/* Now redefine all the function names to map to their "Sort" versions. */
/* really tessPqSortNewPriorityQ */
PriorityQ *pqNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) )
{
PriorityQ *pq = (PriorityQ *)alloc->memalloc( alloc->userData, sizeof( PriorityQ ));
if (pq == NULL) return NULL;
pq->heap = pqHeapNewPriorityQ( alloc, size, leq );
if (pq->heap == NULL) {
alloc->memfree( alloc->userData, pq );
return NULL;
}
// pq->keys = (PQkey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) );
pq->keys = (PQkey *)alloc->memalloc( alloc->userData, size * sizeof(pq->keys[0]) );
if (pq->keys == NULL) {
pqHeapDeletePriorityQ( alloc, pq->heap );
alloc->memfree( alloc->userData, pq );
return NULL;
}
pq->size = 0;
pq->max = size; //INIT_SIZE;
pq->initialized = FALSE;
pq->leq = leq;
return pq;
}
/* really tessPqSortDeletePriorityQ */
void pqDeletePriorityQ( TESSalloc* alloc, PriorityQ *pq )
{
assert(pq != NULL);
if (pq->heap != NULL) pqHeapDeletePriorityQ( alloc, pq->heap );
if (pq->order != NULL) alloc->memfree( alloc->userData, pq->order );
if (pq->keys != NULL) alloc->memfree( alloc->userData, pq->keys );
alloc->memfree( alloc->userData, pq );
}
#define LT(x,y) (! LEQ(y,x))
#define GT(x,y) (! LEQ(x,y))
#define Swap(a,b) if(1){PQkey *tmp = *a; *a = *b; *b = tmp;}else
/* really tessPqSortInit */
int pqInit( TESSalloc* alloc, PriorityQ *pq )
{
PQkey **p, **r, **i, **j, *piv;
struct { PQkey **p, **r; } Stack[50], *top = Stack;
unsigned int seed = 2016473283;
/* Create an array of indirect pointers to the keys, so that we
* the handles we have returned are still valid.
*/
/*
pq->order = (PQkey **)memAlloc( (size_t)
(pq->size * sizeof(pq->order[0])) );
*/
pq->order = (PQkey **)alloc->memalloc( alloc->userData,
(size_t)((pq->size+1) * sizeof(pq->order[0])) );
/* the previous line is a patch to compensate for the fact that IBM */
/* machines return a null on a malloc of zero bytes (unlike SGI), */
/* so we have to put in this defense to guard against a memory */
/* fault four lines down. from fossum@austin.ibm.com. */
if (pq->order == NULL) return 0;
p = pq->order;
r = p + pq->size - 1;
for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) {
*i = piv;
}
/* Sort the indirect pointers in descending order,
* using randomized Quicksort
*/
top->p = p; top->r = r; ++top;
while( --top >= Stack ) {
p = top->p;
r = top->r;
while( r > p + 10 ) {
seed = seed * 1539415821 + 1;
i = p + seed % (r - p + 1);
piv = *i;
*i = *p;
*p = piv;
i = p - 1;
j = r + 1;
do {
do { ++i; } while( GT( **i, *piv ));
do { --j; } while( LT( **j, *piv ));
Swap( i, j );
} while( i < j );
Swap( i, j ); /* Undo last swap */
if( i - p < r - j ) {
top->p = j+1; top->r = r; ++top;
r = i-1;
} else {
top->p = p; top->r = i-1; ++top;
p = j+1;
}
}
/* Insertion sort small lists */
for( i = p+1; i <= r; ++i ) {
piv = *i;
for( j = i; j > p && LT( **(j-1), *piv ); --j ) {
*j = *(j-1);
}
*j = piv;
}
}
pq->max = pq->size;
pq->initialized = TRUE;
pqHeapInit( pq->heap ); /* always succeeds */
#ifndef NDEBUG
p = pq->order;
r = p + pq->size - 1;
for( i = p; i < r; ++i ) {
assert( LEQ( **(i+1), **i ));
}
#endif
return 1;
}
/* really tessPqSortInsert */
/* returns INV_HANDLE iff out of memory */
PQhandle pqInsert( TESSalloc* alloc, PriorityQ *pq, PQkey keyNew )
{
int curr;
if( pq->initialized ) {
return pqHeapInsert( alloc, pq->heap, keyNew );
}
curr = pq->size;
if( ++ pq->size >= pq->max ) {
if (!alloc->memrealloc)
{
return INV_HANDLE;
}
else
{
PQkey *saveKey= pq->keys;
// If the heap overflows, double its size.
pq->max <<= 1;
pq->keys = (PQkey *)alloc->memrealloc( alloc->userData, pq->keys,
(size_t)(pq->max * sizeof( pq->keys[0] )));
if (pq->keys == NULL) {
pq->keys = saveKey; // restore ptr to free upon return
return INV_HANDLE;
}
}
}
assert(curr != INV_HANDLE);
pq->keys[curr] = keyNew;
/* Negative handles index the sorted array. */
return -(curr+1);
}
/* really tessPqSortExtractMin */
PQkey pqExtractMin( PriorityQ *pq )
{
PQkey sortMin, heapMin;
if( pq->size == 0 ) {
return pqHeapExtractMin( pq->heap );
}
sortMin = *(pq->order[pq->size-1]);
if( ! pqHeapIsEmpty( pq->heap )) {
heapMin = pqHeapMinimum( pq->heap );
if( LEQ( heapMin, sortMin )) {
return pqHeapExtractMin( pq->heap );
}
}
do {
-- pq->size;
} while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL );
return sortMin;
}
/* really tessPqSortMinimum */
PQkey pqMinimum( PriorityQ *pq )
{
PQkey sortMin, heapMin;
if( pq->size == 0 ) {
return pqHeapMinimum( pq->heap );
}
sortMin = *(pq->order[pq->size-1]);
if( ! pqHeapIsEmpty( pq->heap )) {
heapMin = pqHeapMinimum( pq->heap );
if( LEQ( heapMin, sortMin )) {
return heapMin;
}
}
return sortMin;
}
/* really tessPqSortIsEmpty */
int pqIsEmpty( PriorityQ *pq )
{
return (pq->size == 0) && pqHeapIsEmpty( pq->heap );
}
/* really tessPqSortDelete */
void pqDelete( PriorityQ *pq, PQhandle curr )
{
if( curr >= 0 ) {
pqHeapDelete( pq->heap, curr );
return;
}
curr = -(curr+1);
assert( curr < pq->max && pq->keys[curr] != NULL );
pq->keys[curr] = NULL;
while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) {
-- pq->size;
}
}

104
src/libtess2/Source/priorityq.h Executable file
View File

@ -0,0 +1,104 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
#ifndef PRIORITYQ_H
#define PRIORITYQ_H
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef void *PQkey;
typedef int PQhandle;
typedef struct PriorityQHeap PriorityQHeap;
#define INV_HANDLE 0x0fffffff
typedef struct { PQhandle handle; } PQnode;
typedef struct { PQkey key; PQhandle node; } PQhandleElem;
struct PriorityQHeap {
PQnode *nodes;
PQhandleElem *handles;
int size, max;
PQhandle freeList;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
typedef struct PriorityQ PriorityQ;
struct PriorityQ {
PriorityQHeap *heap;
PQkey *keys;
PQkey **order;
PQhandle size, max;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( TESSalloc* alloc, int size, int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( TESSalloc* alloc, PriorityQ *pq );
int pqInit( TESSalloc* alloc, PriorityQ *pq );
PQhandle pqInsert( TESSalloc* alloc, PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
PQkey pqMinimum( PriorityQ *pq );
int pqIsEmpty( PriorityQ *pq );
#endif

1325
src/libtess2/Source/sweep.c Executable file

File diff suppressed because it is too large Load Diff

74
src/libtess2/Source/sweep.h Executable file
View File

@ -0,0 +1,74 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
#ifndef SWEEP_H
#define SWEEP_H
#include "mesh.h"
/* tessComputeInterior( tess ) computes the planar arrangement specified
* by the given contours, and further subdivides this arrangement
* into regions. Each region is marked "inside" if it belongs
* to the polygon, according to the rule given by tess->windingRule.
* Each interior region is guaranteed be monotone.
*/
int tessComputeInterior( TESStesselator *tess );
/* The following is here *only* for access by debugging routines */
#include "dict.h"
/* For each pair of adjacent edges crossing the sweep line, there is
* an ActiveRegion to represent the region between them. The active
* regions are kept in sorted order in a dynamic dictionary. As the
* sweep line crosses each vertex, we update the affected regions.
*/
struct ActiveRegion {
TESShalfEdge *eUp; /* upper edge, directed right to left */
DictNode *nodeUp; /* dictionary node corresponding to eUp */
int windingNumber; /* used to determine which regions are
* inside the polygon */
int inside; /* is this region inside the polygon? */
int sentinel; /* marks fake edges at t = +/-infinity */
int dirty; /* marks regions where the upper or lower
* edge has changed, but we haven't checked
* whether they intersect yet */
int fixUpperEdge; /* marks temporary edges introduced when
* we process a "right vertex" (one without
* any edges leaving to the right) */
};
#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp)))
#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp)))
#endif

1095
src/libtess2/Source/tess.c Executable file

File diff suppressed because it is too large Load Diff

90
src/libtess2/Source/tess.h Executable file
View File

@ -0,0 +1,90 @@
/*
** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
** Copyright (C) [dates of first publication] Silicon Graphics, Inc.
** All Rights Reserved.
**
** 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 including the dates of first publication and either this
** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ 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 SILICON GRAPHICS, INC.
** 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.
**
** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not
** be used in advertising or otherwise to promote the sale, use or other dealings in
** this Software without prior written authorization from Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
*/
#ifndef TESS_H
#define TESS_H
#include <setjmp.h>
#include "bucketalloc.h"
#include "mesh.h"
#include "dict.h"
#include "priorityq.h"
#include "../Include/tesselator.h"
#ifdef __cplusplus
extern "C" {
#endif
//typedef struct TESStesselator TESStesselator;
struct TESStesselator {
/*** state needed for collecting the input data ***/
TESSmesh *mesh; /* stores the input contours, and eventually
the tessellation itself */
int outOfMemory;
/*** state needed for projecting onto the sweep plane ***/
TESSreal normal[3]; /* user-specified normal (if provided) */
TESSreal sUnit[3]; /* unit vector in s-direction (debugging) */
TESSreal tUnit[3]; /* unit vector in t-direction (debugging) */
TESSreal bmin[2];
TESSreal bmax[2];
/*** state needed for the line sweep ***/
int windingRule; /* rule for determining polygon interior */
Dict *dict; /* edge dictionary for sweep line */
PriorityQ *pq; /* priority queue of vertex events */
TESSvertex *event; /* current sweep event being processed */
struct BucketAlloc* regionPool;
TESSindex vertexIndexCounter;
TESSreal *vertices;
TESSindex *vertexIndices;
int vertexCount;
TESSindex *elements;
int elementCount;
TESSalloc alloc;
jmp_buf env; /* place to jump to when memAllocs fail */
};
#ifdef __cplusplus
};
#endif
#endif

View File

@ -9,6 +9,10 @@ EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector2d)
using Eigen::Vector2d;
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector3d)
using Eigen::Vector3d;
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector3f)
using Eigen::Vector3f;
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector3i)
using Eigen::Vector3i;
typedef Eigen::AlignedBox<double, 3> BoundingBox;
using Eigen::Matrix3f;
@ -17,9 +21,6 @@ using Eigen::Matrix4d;
#define Transform3d Eigen::Affine3d
#define Transform2d Eigen::Affine2d
typedef std::vector<Vector3d> Polygon;
typedef std::vector<Polygon> Polygons;
bool matrix_contains_infinity( const Transform3d &m );
bool matrix_contains_nan( const Transform3d &m );

View File

@ -256,7 +256,7 @@ MainWindow::MainWindow(const QString &filename)
knownFileExtensions["scad"] = "";
knownFileExtensions["csg"] = "";
editActionZoomIn->setShortcuts(QList<QKeySequence>() << editActionZoomIn->shortcuts() << QKeySequence("CTRL+="));
editActionZoomTextIn->setShortcuts(QList<QKeySequence>() << editActionZoomTextIn->shortcuts() << QKeySequence("CTRL+="));
connect(this, SIGNAL(highlightError(int)), editor, SLOT(highlightError(int)));
connect(this, SIGNAL(unhighlightLastError()), editor, SLOT(unhighlightLastError()));
@ -330,8 +330,8 @@ MainWindow::MainWindow(const QString &filename)
connect(this->editActionConvertTabsToSpaces, SIGNAL(triggered()), this, SLOT(convertTabsToSpaces()));
connect(this->editActionPasteVPT, SIGNAL(triggered()), this, SLOT(pasteViewportTranslation()));
connect(this->editActionPasteVPR, SIGNAL(triggered()), this, SLOT(pasteViewportRotation()));
connect(this->editActionZoomIn, SIGNAL(triggered()), editor, SLOT(zoomIn()));
connect(this->editActionZoomOut, SIGNAL(triggered()), editor, SLOT(zoomOut()));
connect(this->editActionZoomTextIn, SIGNAL(triggered()), editor, SLOT(zoomIn()));
connect(this->editActionZoomTextOut, SIGNAL(triggered()), editor, SLOT(zoomOut()));
connect(this->editActionPreferences, SIGNAL(triggered()), this, SLOT(preferences()));
// Edit->Find
connect(this->editActionFind, SIGNAL(triggered()), this, SLOT(find()));
@ -407,6 +407,7 @@ MainWindow::MainWindow(const QString &filename)
connect(this->helpActionAbout, SIGNAL(triggered()), this, SLOT(helpAbout()));
connect(this->helpActionHomepage, SIGNAL(triggered()), this, SLOT(helpHomepage()));
connect(this->helpActionManual, SIGNAL(triggered()), this, SLOT(helpManual()));
connect(this->helpActionCheatSheet, SIGNAL(triggered()), this, SLOT(helpCheatSheet()));
connect(this->helpActionLibraryInfo, SIGNAL(triggered()), this, SLOT(helpLibrary()));
connect(this->helpActionFontInfo, SIGNAL(triggered()), this, SLOT(helpFontInfo()));
@ -473,60 +474,37 @@ MainWindow::MainWindow(const QString &filename)
addKeyboardShortCut(this->viewerToolBar->actions());
addKeyboardShortCut(this->editortoolbar->actions());
//Toolbar
int defaultcolor = viewerToolBar->palette().background().color().lightness();
if (defaultcolor > 165) {
fileActionNew->setIcon(QIcon("://images/blackNew.png"));
fileActionOpen->setIcon(QIcon("://images/Open-32.png"));
fileActionSave->setIcon(QIcon("://images/Save-32.png"));
editActionZoomIn->setIcon(QIcon("://images/zoomin.png"));
editActionZoomOut->setIcon(QIcon("://images/zoomout.png"));
designActionRender->setIcon(QIcon("://images/blackRender.png"));
viewActionShowAxes->setIcon(QIcon("://images/blackaxes.png"));
viewActionShowEdges->setIcon(QIcon("://images/Rotation-32.png"));
viewActionZoomIn->setIcon(QIcon("://images/zoomin.png"));
viewActionZoomOut->setIcon(QIcon("://images/zoomout.png"));
viewActionTop->setIcon(QIcon("://images/blackUp.png"));
viewActionBottom->setIcon(QIcon("://images/blackbottom.png"));
viewActionLeft->setIcon(QIcon("://images/blackleft (copy).png"));
viewActionRight->setIcon(QIcon("://images/rightright.png"));
viewActionFront->setIcon(QIcon("://images/blackfront.png"));
viewActionBack->setIcon(QIcon("://images/blackback.png"));
viewActionSurfaces->setIcon(QIcon("://images/surface.png"));
viewActionWireframe->setIcon(QIcon("://images/wireframe1.png"));
viewActionShowCrosshairs->setIcon(QIcon("://images/cross.png"));
viewActionPerspective->setIcon(QIcon("://images/perspective1.png"));
viewActionOrthogonal->setIcon(QIcon("://images/orthogonal.png"));
viewActionPreview->setIcon(QIcon("://images/Preview-32.png"));
viewActionAnimate->setIcon(QIcon("://images/animate.png"));
} else {
fileActionNew->setIcon(QIcon("://images/Document-New-128.png"));
fileActionOpen->setIcon(QIcon("://images/Open-128.png"));
fileActionSave->setIcon(QIcon("://images/Save-128.png"));
editActionZoomIn->setIcon(QIcon("://images/Zoom-In-32.png"));
editActionZoomOut->setIcon(QIcon("://images/Zoom-Out-32.png"));
designActionRender->setIcon(QIcon("://images/Arrowhead-Right-32.png"));
viewActionZoomIn->setIcon(QIcon("://images/Zoom-In-32.png"));
viewActionZoomOut->setIcon(QIcon("://images/Zoom-Out-32.png"));
viewActionShowAxes->setIcon(QIcon("://images/axes.png"));
viewActionShowEdges->setIcon(QIcon("://images/grid.png"));
viewActionTop->setIcon(QIcon("://images/up.png"));
viewActionBottom->setIcon(QIcon("://images/bottom.png"));
viewActionLeft->setIcon(QIcon("://images/left.png"));
viewActionRight->setIcon(QIcon("://images/right.png"));
viewActionFront->setIcon(QIcon("://images/front.png"));
viewActionBack->setIcon(QIcon("://images/back.png"));
viewActionSurfaces->setIcon(QIcon("://images/surfaceWhite.png"));
viewActionWireframe->setIcon(QIcon("://images/wireframeWhite.png"));
viewActionShowCrosshairs->setIcon(QIcon("://images/crosswhite.png"));
viewActionPreview->setIcon(QIcon("://images/Preview-32(1).png"));
viewActionPerspective->setIcon(QIcon("://images/perspective1white.png"));
viewActionOrthogonal->setIcon(QIcon("://images/orthogonalwhite.png"));
viewActionAnimate->setIcon(QIcon("://images/animate.png"));
designActionExportSTL->setIcon(QIcon(":/images/export-white.png"));
viewActionViewAll->setIcon(QIcon(":/images/zoom-all-white.png"));
}
initActionIcon(fileActionNew, ":/images/blackNew.png", ":/images/Document-New-128.png");
initActionIcon(fileActionOpen, ":/images/Open-32.png", ":/images/Open-128.png");
initActionIcon(fileActionSave, ":/images/Save-32.png", ":/images/Save-128.png");
initActionIcon(editActionZoomTextIn, ":/images/zoom-text-in.png", ":/images/zoom-text-in-white.png");
initActionIcon(editActionZoomTextOut, ":/images/zoom-text-out.png", ":/images/zoom-text-out-white.png");
initActionIcon(designActionRender, ":/images/blackRender.png", ":/images/Arrowhead-Right-32.png");
initActionIcon(viewActionShowAxes, ":/images/blackaxes.png", ":/images/axes.png");
initActionIcon(viewActionShowEdges, ":/images/Rotation-32.png", ":/images/grid.png");
initActionIcon(viewActionZoomIn, ":/images/zoomin.png", ":/images/Zoom-In-32.png");
initActionIcon(viewActionZoomOut, ":/images/zoomout.png", ":/images/Zoom-Out-32.png");
initActionIcon(viewActionTop, ":/images/blackUp.png", ":/images/up.png");
initActionIcon(viewActionBottom, ":/images/blackbottom.png", ":/images/bottom.png");
initActionIcon(viewActionLeft, ":/images/blackleft (copy).png", ":/images/left.png");
initActionIcon(viewActionRight, ":/images/rightright.png", ":/images/right.png");
initActionIcon(viewActionFront, ":/images/blackfront.png", ":/images/front.png");
initActionIcon(viewActionBack, ":/images/blackback.png", ":/images/back.png");
initActionIcon(viewActionSurfaces, ":/images/surface.png", ":/images/surfaceWhite.png");
initActionIcon(viewActionWireframe, ":/images/wireframe1.png", ":/images/wireframeWhite.png");
initActionIcon(viewActionShowCrosshairs, ":/images/cross.png", ":/images/crosswhite.png");
initActionIcon(viewActionPerspective, ":/images/perspective1.png", ":/images/perspective1white.png");
initActionIcon(viewActionOrthogonal, ":/images/orthogonal.png", ":/images/orthogonalwhite.png");
initActionIcon(designActionPreview, ":/images/Preview-32.png", ":/images/Preview-32-white.png");
initActionIcon(viewActionAnimate, ":/images/animate.png", ":/images/animate.png");
initActionIcon(designActionExportSTL, ":/images/export.png", ":/images/export-white.png");
initActionIcon(viewActionViewAll, ":/images/zoom-all.png", ":/images/zoom-all-white.png");
initActionIcon(editActionUndo, ":/images/Command-Undo-32.png", ":/images/Command-Undo-32-white.png");
initActionIcon(editActionRedo, ":/images/Command-Redo-32.png", ":/images/Command-Redo-32-white.png");
initActionIcon(editActionUnindent, ":/images/Decrease-Indent-32.png", ":/images/Decrease-Indent-32-white.png");
initActionIcon(editActionIndent, ":/images/Increase-Indent-32.png", ":/images/Increase-Indent-32-white.png");
initActionIcon(viewActionResetView, ":/images/Command-Reset-32.png", ":/images/Command-Reset-32-white.png");
initActionIcon(viewActionShowScaleProportional, ":/images/scalemarkers.png", ":/images/scalemarkers-white.png");
// make sure it looks nice..
QByteArray windowState = settings.value("window/state", QByteArray()).toByteArray();
@ -587,6 +565,13 @@ MainWindow::MainWindow(const QString &filename)
clearCurrentOutput();
}
void MainWindow::initActionIcon(QAction *action, const char *darkResource, const char *lightResource)
{
int defaultcolor = viewerToolBar->palette().background().color().lightness();
const char *resource = (defaultcolor > 165) ? darkResource : lightResource;
action->setIcon(QIcon(resource));
}
void MainWindow::addKeyboardShortCut(const QList<QAction *> &actions)
{
foreach (QAction *action, actions) {
@ -957,7 +942,7 @@ void MainWindow::waitAfterReload()
}
}
void MainWindow::on_pushButtonCompileResultClose_clicked()
void MainWindow::on_toolButtonCompileResultClose_clicked()
{
frameCompileResult->hide();
}
@ -969,6 +954,11 @@ void MainWindow::updateCompileResult()
return;
}
Settings::Settings *s = Settings::Settings::inst();
if (!s->get(Settings::Settings::showWarningsIn3dView).toBool()) {
return;
}
QString msg;
if (compileErrors > 0) {
if (fileName.isEmpty()) {
@ -977,11 +967,18 @@ void MainWindow::updateCompileResult()
QFileInfo fileInfo(fileName);
msg = QString(_("Error while compiling '%1'.")).arg(fileInfo.fileName());
}
labelCompileResultIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/information-icons-error.png")));
toolButtonCompileResultIcon->setIcon(QIcon(QString::fromUtf8(":/icons/information-icons-error.png")));
} else {
msg = QString(_("Compilation generated %1 warnings.")).arg(compileWarnings);
labelCompileResultIcon->setPixmap(QPixmap(QString::fromUtf8(":/icons/information-icons-warning.png")));
const char *fmt = ngettext("Compilation generated %1 warning.", "Compilation generated %1 warnings.", compileWarnings);
msg = QString(fmt).arg(compileWarnings);
toolButtonCompileResultIcon->setIcon(QIcon(QString::fromUtf8(":/icons/information-icons-warning.png")));
}
QFontMetrics fm(labelCompileResultMessage->font());
int sizeIcon = std::max(12, std::min(32, fm.height()));
int sizeClose = std::max(10, std::min(32, fm.height()) - 4);
toolButtonCompileResultIcon->setIconSize(QSize(sizeIcon, sizeIcon));
toolButtonCompileResultClose->setIconSize(QSize(sizeClose, sizeClose));
msg += _(" For details see <a href=\"#console\">console window</a>.");
labelCompileResultMessage->setText(msg);
frameCompileResult->show();
@ -1994,7 +1991,7 @@ void MainWindow::actionExport(export_type_e, QString, QString)
setCurrentOutput();
if (!this->root_geom) {
PRINT("Nothing to export! Try building first (press F6).");
PRINT("WARNING: Nothing to export! Try building first (press F6).");
clearCurrentOutput();
return;
}
@ -2033,7 +2030,6 @@ void MainWindow::actionExport(export_type_e, QString, QString)
QString filename = this->fileName.isEmpty() ? QString(_("Untitled")) + suffix : QFileInfo(this->fileName).baseName() + suffix;
QString export_filename = QFileDialog::getSaveFileName(this, title, filename, filter);
if (export_filename.isEmpty()) {
PRINTB("No filename specified. %s export aborted.", type_name);
clearCurrentOutput();
return;
}
@ -2074,13 +2070,13 @@ QString MainWindow::get2dExportFilename(QString format, QString extension) {
setCurrentOutput();
if (!this->root_geom) {
PRINT("Nothing to export! Try building first (press F6).");
PRINT("WARNING: Nothing to export! Try building first (press F6).");
clearCurrentOutput();
return QString();
}
if (this->root_geom->getDimension() != 2) {
PRINT("Current top level object is not a 2D object.");
PRINT("WARNING: Current top level object is not a 2D object.");
clearCurrentOutput();
return QString();
}
@ -2133,7 +2129,7 @@ void MainWindow::actionExportCSG()
setCurrentOutput();
if (!this->root_node) {
PRINT("Nothing to export. Please try compiling first...");
PRINT("WARNING: Nothing to export. Please try compiling first...");
clearCurrentOutput();
return;
}
@ -2518,6 +2514,11 @@ void MainWindow::helpManual()
UIUtils::openUserManualURL();
}
void MainWindow::helpCheatSheet()
{
UIUtils::openCheatSheetURL();
}
void MainWindow::helpLibrary()
{
if (!this->library_info_dialog) {

View File

@ -93,6 +93,8 @@ static std::string arg_colorscheme;
#define QUOTE(x__) # x__
#define QUOTED(x__) QUOTE(x__)
std::string versionnumber = QUOTED(OPENSCAD_VERSION);
std::string openscad_versionnumber = QUOTED(OPENSCAD_VERSION)
#ifdef OPENSCAD_COMMIT
" (git " QUOTED(OPENSCAD_COMMIT) ")"
@ -664,6 +666,12 @@ int gui(vector<string> &inputFiles, const fs::path &original_path, int argc, cha
installAppleEventHandlers();
#endif
#ifdef Q_OS_WIN
QSettings reg_setting(QLatin1String("HKEY_CURRENT_USER"), QSettings::NativeFormat);
QString appPath = QDir::toNativeSeparators(app.applicationFilePath() + QLatin1String(",1"));
reg_setting.setValue(QLatin1String("Software/Classes/OpenSCAD_File/DefaultIcon/Default"),QVariant(appPath));
#endif
#ifdef OPENSCAD_UPDATER
AutoUpdater *updater = new SparkleAutoUpdater;
AutoUpdater::setUpdater(updater);
@ -901,4 +909,3 @@ int main(int argc, char **argv)
return rc;
}

View File

@ -35,6 +35,7 @@ extern std::string commandline_commands;
// doing this, use currentdir to get the original CWD.
extern std::string currentdir;
// Version number without the git suffix.
extern std::string versionnumber;
// Just the number (might have the git commit as suffix), e.g. 2014.12.23.

View File

@ -2,6 +2,7 @@
#include "polyset.h"
#include "Polygon2d.h"
#include "printutils.h"
#include "GeometryUtils.h"
#ifdef ENABLE_CGAL
#include "cgalutils.h"
#endif
@ -47,41 +48,30 @@ namespace PolysetUtils {
The tessellation will be robust wrt. degenerate and self-intersecting
*/
void tessellate_faces(const PolySet &inps, PolySet &outps) {
#ifdef ENABLE_CGAL
int degeneratePolygons = 0;
for (size_t i = 0; i < inps.polygons.size(); i++) {
const Polygon pgon = inps.polygons[i];
const Polygon &pgon = inps.polygons[i];
if (pgon.size() < 3) {
degeneratePolygons++;
continue;
}
std::vector<Polygon> triangles;
if (pgon.size() == 3) {
triangles.push_back(pgon);
}
else {
// Build a data structure that CGAL accepts
PolygonK cgalpoints;
BOOST_FOREACH(const Vector3d &v, pgon) {
cgalpoints.push_back(Vertex3K(v[0], v[1], v[2]));
Polygons triangles;
bool err = GeometryUtils::tessellatePolygon(pgon, triangles);
if (triangles.empty()) {
degeneratePolygons++;
}
else {
// ..and pass to the output polyhedron
BOOST_FOREACH(const Polygon &t, triangles) {
outps.append_poly();
outps.append_vertex(t[0]);
outps.append_vertex(t[1]);
outps.append_vertex(t[2]);
}
}
bool err = CGALUtils::tessellatePolygon(cgalpoints, triangles);
}
// ..and pass to the output polyhedron
for (size_t j=0;j<triangles.size();j++) {
Polygon t = triangles[j];
outps.append_poly();
outps.append_vertex(t[0].x(),t[0].y(),t[0].z());
outps.append_vertex(t[1].x(),t[1].y(),t[1].z());
outps.append_vertex(t[2].x(),t[2].y(),t[2].z());
}
}
if (degeneratePolygons > 0) PRINT("WARNING: PolySet has degenerate polygons");
#else
assert(false);
#endif
}
bool is_approximately_convex(const PolySet &ps) {

View File

@ -92,21 +92,31 @@ void PolySet::append_vertex(double x, double y, double z)
append_vertex(Vector3d(x, y, z));
}
void PolySet::append_vertex(Vector3d v)
void PolySet::append_vertex(const Vector3d &v)
{
polygons.back().push_back(v);
}
void PolySet::append_vertex(const Vector3f &v)
{
polygons.back().push_back(v.cast<double>());
}
void PolySet::insert_vertex(double x, double y, double z)
{
insert_vertex(Vector3d(x, y, z));
}
void PolySet::insert_vertex(Vector3d v)
void PolySet::insert_vertex(const Vector3d &v)
{
polygons.back().insert(polygons.back().begin(), v);
}
void PolySet::insert_vertex(const Vector3f &v)
{
polygons.back().insert(polygons.back().begin(), v.cast<double>());
}
BoundingBox PolySet::getBoundingBox() const
{
BoundingBox bbox;

View File

@ -3,6 +3,7 @@
#include "Geometry.h"
#include "system-gl.h"
#include "linalg.h"
#include "GeometryUtils.h"
#include "renderer.h"
#include "Polygon2d.h"
#include <vector>
@ -31,9 +32,11 @@ public:
size_t numPolygons() const { return polygons.size(); }
void append_poly();
void append_vertex(double x, double y, double z = 0.0);
void append_vertex(Vector3d v);
void append_vertex(const Vector3d &v);
void append_vertex(const Vector3f &v);
void insert_vertex(double x, double y, double z = 0.0);
void insert_vertex(Vector3d v);
void insert_vertex(const Vector3d &v);
void insert_vertex(const Vector3f &v);
void append(const PolySet &ps);
void render_surface(Renderer::csgmode_e csgmode, const Transform3d &m, GLint *shaderinfo = NULL) const;

View File

@ -331,7 +331,6 @@ void ScintillaEditor::noColor()
qsci->setIndicatorForegroundColor(QColor(255, 0, 0, 128), indicatorNumber);
qsci->setIndicatorOutlineColor(QColor(0, 0, 0, 255), indicatorNumber); // only alpha part is used
qsci->setCaretLineBackgroundColor(Qt::white);
qsci->setWhitespaceBackgroundColor(Qt::white);
qsci->setWhitespaceForegroundColor(Qt::black);
qsci->setMarginsBackgroundColor(Qt::white);
qsci->setMarginsForegroundColor(Qt::black);

View File

@ -121,6 +121,7 @@ Visitor::~Visitor()
* external settings file. The second value is the display value that
* can be translated.
*/
SettingsEntry Settings::showWarningsIn3dView("3dview", "showWarningsIn3dView", Value(true), Value(true));
SettingsEntry Settings::indentationWidth("editor", "indentationWidth", Value(Value::RangeType(1, 16)), Value(4));
SettingsEntry Settings::tabWidth("editor", "tabWidth", Value(Value::RangeType(1, 16)), Value(8));
SettingsEntry Settings::lineWrap("editor", "lineWrap", values("None", _("None"), "Char", _("Wrap at character boundaries"), "Word", _("Wrap at word boundaries")), Value("Word"));

View File

@ -35,6 +35,7 @@ protected:
class Settings
{
public:
static SettingsEntry showWarningsIn3dView;
static SettingsEntry indentationWidth;
static SettingsEntry tabWidth;
static SettingsEntry lineWrap;

5
testdata/scad/bugs/issue1165.scad vendored Normal file
View File

@ -0,0 +1,5 @@
translate([0,10,0]) difference() {
cube(10, center=true);
translate([6,5.5,0]) cube(11, center=true);
translate([6,-5.500000000088,0]) cube(11, center=true);
}

2
testdata/scad/bugs/issue13.scad vendored Normal file
View File

@ -0,0 +1,2 @@
cube(size = [10,10,5]);
translate([5, 5, 8.1-9+0.9]) cube(size = [5,10,10]);

2
testdata/scad/bugs/issue13b.scad vendored Normal file
View File

@ -0,0 +1,2 @@
rotate([90,0,180]) translate ([0, 0, 8]) cube(10, center=true);
rotate([90,0,270]) translate ([0, 0, 8]) cube(10, center=true);

View File

@ -555,7 +555,7 @@ else()
endif()
# Internal includes
include_directories(../src)
include_directories(../src ../src/libtess2/Include)
# Handle OpenSCAD version based on VERSION env. variable.
# Use current timestamp if not specified (development builds)
@ -670,7 +670,8 @@ set(NOCGAL_SOURCES
../src/export.cc
../src/LibraryInfo.cc
../src/polyset.cc
../src/polyset-utils.cc)
../src/polyset-utils.cc
../src/GeometryUtils.cc)
set(CGAL_SOURCES
@ -680,7 +681,6 @@ set(CGAL_SOURCES
../src/cgalutils.cc
../src/cgalutils-tess.cc
../src/cgalutils-polyhedron.cc
../src/cgalutils-tess-old.cc
../src/CGALCache.cc
../src/CGAL_Nef_polyhedron_DxfData.cc
../src/Polygon2d-CGAL.cc
@ -692,8 +692,15 @@ set(COMMON_SOURCES
../src/traverser.cc
../src/GeometryCache.cc
../src/clipper-utils.cc
../src/Tree.cc
../src/polyclipping/clipper.cpp
../src/Tree.cc)
../src/libtess2/Source/bucketalloc.c
../src/libtess2/Source/dict.c
../src/libtess2/Source/geom.c
../src/libtess2/Source/mesh.c
../src/libtess2/Source/priorityq.c
../src/libtess2/Source/sweep.c
../src/libtess2/Source/tess.c)
#
# Offscreen OpenGL context source code
@ -1208,7 +1215,9 @@ set_test_config(Heavy cgalpngtest_rotate_extrude-tests
# Bugs
list(APPEND BUGS_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue584.scad
list(APPEND BUGS_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue13.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue13b.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue584.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue591.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue666.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue791.scad
@ -1222,7 +1231,8 @@ list(APPEND BUGS_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue584.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1105.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1105b.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1105c.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1105d.scad)
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1105d.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue1165.scad)
list(APPEND EXPORT3D_TEST_FILES ${BUGS_FILES})
list(REMOVE_ITEM EXPORT3D_TEST_FILES
${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/issue899.scad

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,15 @@
group() {
group();
group() {
difference() {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 7.5], [0, 0, 0, 1]]) {
cube(size = [30, 30, 15], center = true);
}
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 5], [0, 0, 0, 1]]) {
linear_extrude(height = 30, center = false, convexity = 3, scale = [1, 1], $fn = 0, $fa = 12, $fs = 2) {
text(text = "M", size = 22, spacing = 1, font = "Tahoma", direction = "ltr", language = "en", script = "latin", halign = "center", valign = "center", $fn = 0, $fa = 12, $fs = 2);
}
}
}
}
}

View File

@ -17,7 +17,7 @@ group() {
}
}
group() {
multmatrix([[1, 0, 0, -14], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
multmatrix([[1, 0, 0, -18], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
import(file = "chopped_blocks.stl", layer = "", origin = [0, 0], scale = 1, convexity = 12, $fn = 0, $fa = 12, $fs = 2);
}
}
@ -36,7 +36,7 @@ group() {
}
}
group() {
multmatrix([[1, 0, 0, -14], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
multmatrix([[1, 0, 0, -18], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
import(file = "chopped_blocks.stl", layer = "", origin = [0, 0], scale = 1, convexity = 12, $fn = 0, $fa = 12, $fs = 2);
}
}
@ -55,7 +55,7 @@ group() {
}
}
group() {
multmatrix([[1, 0, 0, -14], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
multmatrix([[1, 0, 0, -18], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
import(file = "chopped_blocks.stl", layer = "", origin = [0, 0], scale = 1, convexity = 12, $fn = 0, $fa = 12, $fs = 2);
}
}
@ -74,7 +74,7 @@ group() {
}
}
group() {
multmatrix([[1, 0, 0, -14], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
multmatrix([[1, 0, 0, -18], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
import(file = "chopped_blocks.stl", layer = "", origin = [0, 0], scale = 1, convexity = 12, $fn = 0, $fa = 12, $fs = 2);
}
}

View File

@ -2,9 +2,9 @@ group() {
group();
difference() {
sphere($fn = 0, $fa = 12, $fs = 2, r = 20);
multmatrix([[1, 0, 0, -2.92], [0, 1, 0, 0.5], [0, 0, 1, 20], [0, 0, 0, 1]]) {
multmatrix([[1, 0, 0, 0], [0, 1, 0, 0.5], [0, 0, 1, 20], [0, 0, 0, 1]]) {
multmatrix([[-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]) {
import(file = "sphere.stl", layer = "", origin = [0, 0], scale = 1, convexity = 5, $fn = 0, $fa = 12, $fs = 2);
import(file = "M.stl", layer = "", origin = [0, 0], scale = 1, convexity = 5, $fn = 0, $fa = 12, $fs = 2);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Some files were not shown because too many files have changed in this diff Show More