Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7e64c405dd | ||
![]() |
cf7db87b38 | ||
![]() |
000bf71a50 | ||
![]() |
27767f7a48 | ||
![]() |
1e56860f88 | ||
![]() |
981f772d81 | ||
![]() |
3b31ab9190 | ||
![]() |
0cbd48bd67 | ||
![]() |
479c406d2b | ||
![]() |
2ecc612801 | ||
![]() |
15446e8f0a |
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build*
|
67
CMakeLists.txt
Normal file
|
@ -0,0 +1,67 @@
|
|||
# cmake configuration
|
||||
|
||||
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
|
||||
cmake_policy(VERSION 3.1.0)
|
||||
|
||||
# project
|
||||
|
||||
project(solvespace)
|
||||
|
||||
# common compiler flags
|
||||
|
||||
if(MINGW)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
|
||||
endif()
|
||||
|
||||
if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
endif()
|
||||
|
||||
# solvespace-only compiler flags
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(
|
||||
-D_CRT_SECURE_NO_DEPRECATE
|
||||
-D_CRT_SECURE_NO_WARNINGS
|
||||
-D_SCL_SECURE_NO_WARNINGS
|
||||
-D_WIN32_WINNT=0x500
|
||||
-D_WIN32_IE=_WIN32_WINNT
|
||||
-DISOLATION_AWARE_ENABLED
|
||||
-DWIN32
|
||||
-DWIN32_LEAN_AND_MEAN
|
||||
-DUNICODE
|
||||
-D_UNICODE
|
||||
-DNOMINMAX
|
||||
-D_USE_MATH_DEFINES)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# Many versions of MSVC do not have the (C99) inline keyword, instead
|
||||
# they have their own __inline; this breaks `static inline` functions.
|
||||
# We do not want to care and so we fix this with a definition.
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
|
||||
# Same for the (C99) __func__ special variable; we use it only in C++ code.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D__func__=__FUNCTION__")
|
||||
|
||||
# We rely on these /we flags. They correspond to the GNU-style flags below as
|
||||
# follows: /w4062=-Wswitch
|
||||
set(WARNING_FLAGS "${WARNING_FLAGS} /we4062")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(WARNING_FLAGS "-Wall -Wextra -Wno-unused-parameter")
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(WARNING_FLAGS "${WARNING_FLAGS} -Wfloat-conversion")
|
||||
endif()
|
||||
# We rely on these -Werror flags.
|
||||
set(WARNING_FLAGS "${WARNING_FLAGS} -Werror=switch")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||
|
||||
# components
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(exposed)
|
102
Makefile
|
@ -1,102 +0,0 @@
|
|||
DEFINES = /D_WIN32_WINNT=0x500 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN32_LEAN_AND_MEAN /DWIN32
|
||||
# Use the multi-threaded static libc because libpng and zlib do; not sure if anything bad
|
||||
# happens if those mix, but don't want to risk it.
|
||||
CFLAGS = /W3 /nologo -MT -Iextlib -I..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /Zi /EHs # /O2
|
||||
|
||||
HEADERS = ..\common\win32\freeze.h ui.h solvespace.h dsc.h sketch.h expr.h polygon.h srf\surface.h
|
||||
|
||||
OBJDIR = obj
|
||||
|
||||
FREEZE = $(OBJDIR)\freeze.obj
|
||||
|
||||
W32OBJS = $(OBJDIR)\w32main.obj \
|
||||
$(OBJDIR)\w32util.obj \
|
||||
|
||||
SSOBJS = $(OBJDIR)\solvespace.obj \
|
||||
$(OBJDIR)\textwin.obj \
|
||||
$(OBJDIR)\textscreens.obj \
|
||||
$(OBJDIR)\confscreen.obj \
|
||||
$(OBJDIR)\describescreen.obj \
|
||||
$(OBJDIR)\graphicswin.obj \
|
||||
$(OBJDIR)\modify.obj \
|
||||
$(OBJDIR)\clipboard.obj \
|
||||
$(OBJDIR)\view.obj \
|
||||
$(OBJDIR)\util.obj \
|
||||
$(OBJDIR)\style.obj \
|
||||
$(OBJDIR)\entity.obj \
|
||||
$(OBJDIR)\drawentity.obj \
|
||||
$(OBJDIR)\group.obj \
|
||||
$(OBJDIR)\groupmesh.obj \
|
||||
$(OBJDIR)\request.obj \
|
||||
$(OBJDIR)\glhelper.obj \
|
||||
$(OBJDIR)\expr.obj \
|
||||
$(OBJDIR)\constraint.obj \
|
||||
$(OBJDIR)\constrainteq.obj \
|
||||
$(OBJDIR)\mouse.obj \
|
||||
$(OBJDIR)\draw.obj \
|
||||
$(OBJDIR)\toolbar.obj \
|
||||
$(OBJDIR)\drawconstraint.obj \
|
||||
$(OBJDIR)\file.obj \
|
||||
$(OBJDIR)\undoredo.obj \
|
||||
$(OBJDIR)\system.obj \
|
||||
$(OBJDIR)\polygon.obj \
|
||||
$(OBJDIR)\mesh.obj \
|
||||
$(OBJDIR)\bsp.obj \
|
||||
$(OBJDIR)\ttf.obj \
|
||||
$(OBJDIR)\generate.obj \
|
||||
$(OBJDIR)\export.obj \
|
||||
$(OBJDIR)\exportvector.obj \
|
||||
$(OBJDIR)\exportstep.obj \
|
||||
|
||||
SRFOBJS = $(OBJDIR)\ratpoly.obj \
|
||||
$(OBJDIR)\curve.obj \
|
||||
$(OBJDIR)\surface.obj \
|
||||
$(OBJDIR)\triangulate.obj \
|
||||
$(OBJDIR)\boolean.obj \
|
||||
$(OBJDIR)\surfinter.obj \
|
||||
$(OBJDIR)\raycast.obj \
|
||||
$(OBJDIR)\merge.obj \
|
||||
|
||||
|
||||
RES = $(OBJDIR)\resource.res
|
||||
|
||||
|
||||
LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib opengl32.lib glu32.lib \
|
||||
extlib\libpng.lib extlib\zlib.lib extlib\si\siapp.lib
|
||||
|
||||
all: $(OBJDIR)/solvespace.exe
|
||||
@cp $(OBJDIR)/solvespace.exe .
|
||||
|
||||
clean:
|
||||
rm -f obj/*
|
||||
|
||||
$(OBJDIR)/solvespace.exe: $(SRFOBJS) $(SSOBJS) $(W32OBJS) $(FREEZE) $(RES)
|
||||
@$(CC) $(DEFINES) $(CFLAGS) -Fe$(OBJDIR)/solvespace.exe $(SSOBJS) $(SRFOBJS) $(W32OBJS) $(FREEZE) $(RES) $(LIBS)
|
||||
editbin /nologo /STACK:8388608 $(OBJDIR)/solvespace.exe
|
||||
@echo solvespace.exe
|
||||
|
||||
$(SSOBJS): $(@B).cpp $(HEADERS)
|
||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp
|
||||
|
||||
$(SRFOBJS): srf\$(@B).cpp $(HEADERS)
|
||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj srf\$(@B).cpp
|
||||
|
||||
$(W32OBJS): win32/$(@B).cpp $(HEADERS)
|
||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj win32/$(@B).cpp
|
||||
|
||||
$(FREEZE): ..\common\win32\$(@B).cpp $(HEADERS)
|
||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\common\win32\$(@B).cpp
|
||||
|
||||
$(RES): win32/$(@B).rc icon.ico
|
||||
rc win32/$(@B).rc
|
||||
mv win32/$(@B).res $(OBJDIR)/$(@B).res
|
||||
|
||||
toolbar.cpp: $(OBJDIR)/icons.h
|
||||
|
||||
textwin.cpp: $(OBJDIR)/icons.h
|
||||
|
||||
glhelper.cpp: bitmapfont.table font.table bitmapextra.table
|
||||
|
||||
$(OBJDIR)/icons.h: icons/* png2c.pl
|
||||
perl png2c.pl $(OBJDIR)/icons.h $(OBJDIR)/icons-proto.h
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
|
||||
0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0,
|
||||
0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
||||
0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0,
|
||||
0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0,
|
||||
0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
|
||||
0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0,
|
||||
0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0,
|
||||
0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0,
|
||||
0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0,
|
||||
0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0,
|
||||
0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0,
|
||||
0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0,
|
||||
0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
2179
bitmapfont.table
680
bsp.cpp
|
@ -1,680 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Binary space partitioning tree, used to represent a volume in 3-space
|
||||
// bounded by a triangle mesh. These are used to compute Boolean operations
|
||||
// on meshes. These aren't used for anything relating to an SShell of
|
||||
// ratpoly surfaces.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
|
||||
SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); }
|
||||
|
||||
SBsp3 *SBsp3::FromMesh(SMesh *m) {
|
||||
SBsp3 *bsp3 = NULL;
|
||||
int i;
|
||||
|
||||
SMesh mc; ZERO(&mc);
|
||||
for(i = 0; i < m->l.n; i++) {
|
||||
mc.AddTriangle(&(m->l.elem[i]));
|
||||
}
|
||||
|
||||
srand(0); // Let's be deterministic, at least!
|
||||
int n = mc.l.n;
|
||||
while(n > 1) {
|
||||
int k = rand() % n;
|
||||
n--;
|
||||
SWAP(STriangle, mc.l.elem[k], mc.l.elem[n]);
|
||||
}
|
||||
|
||||
for(i = 0; i < mc.l.n; i++) {
|
||||
bsp3 = bsp3->Insert(&(mc.l.elem[i]), NULL);
|
||||
}
|
||||
|
||||
mc.Clear();
|
||||
return bsp3;
|
||||
}
|
||||
|
||||
Vector SBsp3::IntersectionWith(Vector a, Vector b) {
|
||||
double da = a.Dot(n) - d;
|
||||
double db = b.Dot(n) - d;
|
||||
if(da*db > 0) oops();
|
||||
|
||||
double dab = (db - da);
|
||||
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
|
||||
}
|
||||
|
||||
void SBsp3::InsertInPlane(bool pos2, STriangle *tr, SMesh *m) {
|
||||
Vector tc = ((tr->a).Plus(tr->b).Plus(tr->c)).ScaledBy(1.0/3);
|
||||
|
||||
bool onFace = false;
|
||||
bool sameNormal;
|
||||
double maxNormalMag = -1;
|
||||
|
||||
Vector lln, trn = tr->Normal();
|
||||
|
||||
SBsp3 *ll = this;
|
||||
while(ll) {
|
||||
if((ll->tri).ContainsPoint(tc)) {
|
||||
onFace = true;
|
||||
// If the mesh contains almost-zero-area triangles, and we're
|
||||
// just on the edge of one of those, then don't trust its normal.
|
||||
lln = (ll->tri).Normal();
|
||||
if(lln.Magnitude() > maxNormalMag) {
|
||||
sameNormal = trn.Dot(lln) > 0;
|
||||
maxNormalMag = lln.Magnitude();
|
||||
}
|
||||
}
|
||||
ll = ll->more;
|
||||
}
|
||||
|
||||
if(m->flipNormal && ((!pos2 && !onFace) ||
|
||||
(onFace && !sameNormal && m->keepCoplanar)))
|
||||
{
|
||||
m->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
|
||||
} else if(!(m->flipNormal) && ((pos2 && !onFace) ||
|
||||
(onFace && sameNormal && m->keepCoplanar)))
|
||||
{
|
||||
m->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
|
||||
} else {
|
||||
m->atLeastOneDiscarded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SBsp3::InsertHow(int how, STriangle *tr, SMesh *instead) {
|
||||
switch(how) {
|
||||
case POS:
|
||||
if(instead && !pos) goto alt;
|
||||
pos = pos->Insert(tr, instead);
|
||||
break;
|
||||
|
||||
case NEG:
|
||||
if(instead && !neg) goto alt;
|
||||
neg = neg->Insert(tr, instead);
|
||||
break;
|
||||
|
||||
case COPLANAR: {
|
||||
if(instead) goto alt;
|
||||
SBsp3 *m = Alloc();
|
||||
m->n = n;
|
||||
m->d = d;
|
||||
m->tri = *tr;
|
||||
m->more = more;
|
||||
more = m;
|
||||
break;
|
||||
}
|
||||
default: oops();
|
||||
}
|
||||
return;
|
||||
|
||||
alt:
|
||||
if(how == POS && !(instead->flipNormal)) {
|
||||
instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
|
||||
} else if(how == NEG && instead->flipNormal) {
|
||||
instead->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
|
||||
} else if(how == COPLANAR) {
|
||||
if(edges) {
|
||||
edges->InsertTriangle(tr, instead, this);
|
||||
} else {
|
||||
// I suppose this actually is allowed to happen, if the coplanar
|
||||
// face is the leaf, and all of its neighbors are earlier in tree?
|
||||
InsertInPlane(false, tr, instead);
|
||||
}
|
||||
} else {
|
||||
instead->atLeastOneDiscarded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SBsp3::InsertConvexHow(int how, STriMeta meta, Vector *vertex, int n,
|
||||
SMesh *instead)
|
||||
{
|
||||
switch(how) {
|
||||
case POS:
|
||||
if(pos) {
|
||||
pos = pos->InsertConvex(meta, vertex, n, instead);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case NEG:
|
||||
if(neg) {
|
||||
neg = neg->InsertConvex(meta, vertex, n, instead);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
int i;
|
||||
for(i = 0; i < n - 2; i++) {
|
||||
STriangle tr = STriangle::From(meta,
|
||||
vertex[0], vertex[i+1], vertex[i+2]);
|
||||
InsertHow(how, &tr, instead);
|
||||
}
|
||||
}
|
||||
|
||||
SBsp3 *SBsp3::InsertConvex(STriMeta meta, Vector *vertex, int cnt,
|
||||
SMesh *instead)
|
||||
{
|
||||
Vector e01 = (vertex[1]).Minus(vertex[0]);
|
||||
Vector e12 = (vertex[2]).Minus(vertex[1]);
|
||||
Vector out = e01.Cross(e12);
|
||||
|
||||
#define MAX_VERTICES 50
|
||||
if(cnt+1 >= MAX_VERTICES) goto triangulate;
|
||||
|
||||
int i;
|
||||
Vector on[2];
|
||||
bool isPos[MAX_VERTICES];
|
||||
bool isNeg[MAX_VERTICES];
|
||||
bool isOn[MAX_VERTICES];
|
||||
int posc = 0, negc = 0, onc = 0;
|
||||
for(i = 0; i < cnt; i++) {
|
||||
double dt = n.Dot(vertex[i]);
|
||||
isPos[i] = isNeg[i] = isOn[i] = false;
|
||||
if(fabs(dt - d) < LENGTH_EPS) {
|
||||
isOn[i] = true;
|
||||
if(onc < 2) {
|
||||
on[onc] = vertex[i];
|
||||
}
|
||||
onc++;
|
||||
} else if(dt > d) {
|
||||
isPos[i] = true;
|
||||
posc++;
|
||||
} else {
|
||||
isNeg[i] = true;
|
||||
negc++;
|
||||
}
|
||||
}
|
||||
if(onc != 2 && onc != 1 && onc != 0) goto triangulate;
|
||||
|
||||
if(onc == 2) {
|
||||
if(!instead) {
|
||||
SEdge se = SEdge::From(on[0], on[1]);
|
||||
edges = edges->InsertEdge(&se, n, out);
|
||||
}
|
||||
}
|
||||
|
||||
if(posc == 0) {
|
||||
InsertConvexHow(NEG, meta, vertex, cnt, instead);
|
||||
return this;
|
||||
}
|
||||
if(negc == 0) {
|
||||
InsertConvexHow(POS, meta, vertex, cnt, instead);
|
||||
return this;
|
||||
}
|
||||
|
||||
Vector vpos[MAX_VERTICES];
|
||||
Vector vneg[MAX_VERTICES];
|
||||
int npos = 0, nneg = 0;
|
||||
|
||||
Vector inter[2];
|
||||
int inters = 0;
|
||||
|
||||
for(i = 0; i < cnt; i++) {
|
||||
int ip = WRAP((i + 1), cnt);
|
||||
|
||||
if(isPos[i]) {
|
||||
vpos[npos++] = vertex[i];
|
||||
}
|
||||
if(isNeg[i]) {
|
||||
vneg[nneg++] = vertex[i];
|
||||
}
|
||||
if(isOn[i]) {
|
||||
vneg[nneg++] = vertex[i];
|
||||
vpos[npos++] = vertex[i];
|
||||
}
|
||||
if((isPos[i] && isNeg[ip]) || (isNeg[i] && isPos[ip])) {
|
||||
Vector vi = IntersectionWith(vertex[i], vertex[ip]);
|
||||
vpos[npos++] = vi;
|
||||
vneg[nneg++] = vi;
|
||||
|
||||
if(inters >= 2) goto triangulate; // XXX shouldn't happen but does
|
||||
inter[inters++] = vi;
|
||||
}
|
||||
}
|
||||
if(npos > cnt + 1 || nneg > cnt + 1) oops();
|
||||
|
||||
if(!instead) {
|
||||
if(inters == 2) {
|
||||
SEdge se = SEdge::From(inter[0], inter[1]);
|
||||
edges = edges->InsertEdge(&se, n, out);
|
||||
} else if(inters == 1 && onc == 1) {
|
||||
SEdge se = SEdge::From(inter[0], on[0]);
|
||||
edges = edges->InsertEdge(&se, n, out);
|
||||
} else if(inters == 0 && onc == 2) {
|
||||
// We already handled this on-plane existing edge
|
||||
} else {
|
||||
goto triangulate;
|
||||
}
|
||||
}
|
||||
if(nneg < 3 || npos < 3) goto triangulate; // XXX
|
||||
|
||||
InsertConvexHow(NEG, meta, vneg, nneg, instead);
|
||||
InsertConvexHow(POS, meta, vpos, npos, instead);
|
||||
return this;
|
||||
|
||||
triangulate:
|
||||
// We don't handle the special case for this; do it as triangles
|
||||
SBsp3 *r = this;
|
||||
for(i = 0; i < cnt - 2; i++) {
|
||||
STriangle tr = STriangle::From(meta,
|
||||
vertex[0], vertex[i+1], vertex[i+2]);
|
||||
r = r->Insert(&tr, instead);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead) {
|
||||
if(!this) {
|
||||
if(instead) {
|
||||
if(instead->flipNormal) {
|
||||
instead->atLeastOneDiscarded = true;
|
||||
} else {
|
||||
instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Brand new node; so allocate for it, and fill us in.
|
||||
SBsp3 *r = Alloc();
|
||||
r->n = (tr->Normal()).WithMagnitude(1);
|
||||
r->d = (tr->a).Dot(r->n);
|
||||
r->tri = *tr;
|
||||
return r;
|
||||
}
|
||||
|
||||
double dt[3] = { (tr->a).Dot(n), (tr->b).Dot(n), (tr->c).Dot(n) };
|
||||
|
||||
int inc = 0, posc = 0, negc = 0;
|
||||
bool isPos[3], isNeg[3], isOn[3];
|
||||
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
|
||||
// Count vertices in the plane
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(fabs(dt[i] - d) < LENGTH_EPS) {
|
||||
inc++;
|
||||
isOn[i] = true;
|
||||
} else if(dt[i] > d) {
|
||||
posc++;
|
||||
isPos[i] = true;
|
||||
} else {
|
||||
negc++;
|
||||
isNeg[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// All vertices in-plane
|
||||
if(inc == 3) {
|
||||
InsertHow(COPLANAR, tr, instead);
|
||||
return this;
|
||||
}
|
||||
|
||||
// No split required
|
||||
if(posc == 0 || negc == 0) {
|
||||
if(inc == 2) {
|
||||
Vector a, b;
|
||||
if (!isOn[0]) { a = tr->b; b = tr->c; }
|
||||
else if(!isOn[1]) { a = tr->c; b = tr->a; }
|
||||
else if(!isOn[2]) { a = tr->a; b = tr->b; }
|
||||
else oops();
|
||||
if(!instead) {
|
||||
SEdge se = SEdge::From(a, b);
|
||||
edges = edges->InsertEdge(&se, n, tr->Normal());
|
||||
}
|
||||
}
|
||||
|
||||
if(posc > 0) {
|
||||
InsertHow(POS, tr, instead);
|
||||
} else {
|
||||
InsertHow(NEG, tr, instead);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// The polygon must be split into two pieces, one above, one below.
|
||||
Vector a, b, c;
|
||||
|
||||
if(posc == 1 && negc == 1 && inc == 1) {
|
||||
bool bpos;
|
||||
// Standardize so that a is on the plane
|
||||
if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
|
||||
} else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
|
||||
} else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
|
||||
} else oops();
|
||||
|
||||
Vector bPc = IntersectionWith(b, c);
|
||||
STriangle btri = STriangle::From(tr->meta, a, b, bPc);
|
||||
STriangle ctri = STriangle::From(tr->meta, c, a, bPc);
|
||||
|
||||
if(bpos) {
|
||||
InsertHow(POS, &btri, instead);
|
||||
InsertHow(NEG, &ctri, instead);
|
||||
} else {
|
||||
InsertHow(POS, &ctri, instead);
|
||||
InsertHow(NEG, &btri, instead);
|
||||
}
|
||||
|
||||
if(!instead) {
|
||||
SEdge se = SEdge::From(a, bPc);
|
||||
edges = edges->InsertEdge(&se, n, tr->Normal());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if(posc == 2 && negc == 1) {
|
||||
// Standardize so that a is on one side, and b and c are on the other.
|
||||
if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
|
||||
} else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
|
||||
} else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
|
||||
} else oops();
|
||||
|
||||
} else if(posc == 1 && negc == 2) {
|
||||
if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
|
||||
} else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
|
||||
} else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
|
||||
} else oops();
|
||||
} else oops();
|
||||
|
||||
Vector aPb = IntersectionWith(a, b);
|
||||
Vector cPa = IntersectionWith(c, a);
|
||||
|
||||
STriangle alone = STriangle::From(tr->meta, a, aPb, cPa);
|
||||
Vector quad[4] = { aPb, b, c, cPa };
|
||||
|
||||
if(posc == 2 && negc == 1) {
|
||||
InsertConvexHow(POS, tr->meta, quad, 4, instead);
|
||||
InsertHow(NEG, &alone, instead);
|
||||
} else {
|
||||
InsertConvexHow(NEG, tr->meta, quad, 4, instead);
|
||||
InsertHow(POS, &alone, instead);
|
||||
}
|
||||
if(!instead) {
|
||||
SEdge se = SEdge::From(aPb, cPa);
|
||||
edges = edges->InsertEdge(&se, n, alone.Normal());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void SBsp3::GenerateInPaintOrder(SMesh *m) {
|
||||
if(!this) return;
|
||||
|
||||
// Doesn't matter which branch we take if the normal has zero z
|
||||
// component, so don't need a separate case for that.
|
||||
if(n.z < 0) {
|
||||
pos->GenerateInPaintOrder(m);
|
||||
} else {
|
||||
neg->GenerateInPaintOrder(m);
|
||||
}
|
||||
|
||||
SBsp3 *flip = this;
|
||||
while(flip) {
|
||||
m->AddTriangle(&(flip->tri));
|
||||
flip = flip->more;
|
||||
}
|
||||
|
||||
if(n.z < 0) {
|
||||
neg->GenerateInPaintOrder(m);
|
||||
} else {
|
||||
pos->GenerateInPaintOrder(m);
|
||||
}
|
||||
}
|
||||
|
||||
void SBsp3::DebugDraw(void) {
|
||||
if(!this) return;
|
||||
|
||||
pos->DebugDraw();
|
||||
Vector norm = tri.Normal();
|
||||
glNormal3d(norm.x, norm.y, norm.z);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_LIGHTING);
|
||||
glBegin(GL_TRIANGLES);
|
||||
glxVertex3v(tri.a);
|
||||
glxVertex3v(tri.b);
|
||||
glxVertex3v(tri.c);
|
||||
glEnd();
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glxDepthRangeOffset(2);
|
||||
glBegin(GL_TRIANGLES);
|
||||
glxVertex3v(tri.a);
|
||||
glxVertex3v(tri.b);
|
||||
glxVertex3v(tri.c);
|
||||
glEnd();
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
|
||||
glPointSize(10);
|
||||
glxDepthRangeOffset(2);
|
||||
glBegin(GL_TRIANGLES);
|
||||
glxVertex3v(tri.a);
|
||||
glxVertex3v(tri.b);
|
||||
glxVertex3v(tri.c);
|
||||
glEnd();
|
||||
|
||||
glxDepthRangeOffset(0);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
|
||||
more->DebugDraw();
|
||||
neg->DebugDraw();
|
||||
|
||||
edges->DebugDraw(n, d);
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
|
||||
Vector SBsp2::IntersectionWith(Vector a, Vector b) {
|
||||
double da = a.Dot(no) - d;
|
||||
double db = b.Dot(no) - d;
|
||||
if(da*db > 0) oops();
|
||||
|
||||
double dab = (db - da);
|
||||
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
|
||||
}
|
||||
|
||||
SBsp2 *SBsp2::InsertEdge(SEdge *nedge, Vector nnp, Vector out) {
|
||||
if(!this) {
|
||||
// Brand new node; so allocate for it, and fill us in.
|
||||
SBsp2 *r = Alloc();
|
||||
r->np = nnp;
|
||||
r->no = ((r->np).Cross((nedge->b).Minus(nedge->a))).WithMagnitude(1);
|
||||
if(out.Dot(r->no) < 0) {
|
||||
r->no = (r->no).ScaledBy(-1);
|
||||
}
|
||||
r->d = (nedge->a).Dot(r->no);
|
||||
r->edge = *nedge;
|
||||
return r;
|
||||
}
|
||||
|
||||
double dt[2] = { (nedge->a).Dot(no), (nedge->b).Dot(no) };
|
||||
|
||||
bool isPos[2], isNeg[2], isOn[2];
|
||||
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
|
||||
for(int i = 0; i < 2; i++) {
|
||||
if(fabs(dt[i] - d) < LENGTH_EPS) {
|
||||
isOn[i] = true;
|
||||
} else if(dt[i] > d) {
|
||||
isPos[i] = true;
|
||||
} else {
|
||||
isNeg[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if((isPos[0] && isPos[1])||(isPos[0] && isOn[1])||(isOn[0] && isPos[1])) {
|
||||
pos = pos->InsertEdge(nedge, nnp, out);
|
||||
return this;
|
||||
}
|
||||
if((isNeg[0] && isNeg[1])||(isNeg[0] && isOn[1])||(isOn[0] && isNeg[1])) {
|
||||
neg = neg->InsertEdge(nedge, nnp, out);
|
||||
return this;
|
||||
}
|
||||
if(isOn[0] && isOn[1]) {
|
||||
SBsp2 *m = Alloc();
|
||||
|
||||
m->np = nnp;
|
||||
m->no = ((m->np).Cross((nedge->b).Minus(nedge->a))).WithMagnitude(1);
|
||||
if(out.Dot(m->no) < 0) {
|
||||
m->no = (m->no).ScaledBy(-1);
|
||||
}
|
||||
m->d = (nedge->a).Dot(m->no);
|
||||
m->edge = *nedge;
|
||||
|
||||
m->more = more;
|
||||
more = m;
|
||||
return this;
|
||||
}
|
||||
if((isPos[0] && isNeg[1]) || (isNeg[0] && isPos[1])) {
|
||||
Vector aPb = IntersectionWith(nedge->a, nedge->b);
|
||||
|
||||
SEdge ea = SEdge::From(nedge->a, aPb);
|
||||
SEdge eb = SEdge::From(aPb, nedge->b);
|
||||
|
||||
if(isPos[0]) {
|
||||
pos = pos->InsertEdge(&ea, nnp, out);
|
||||
neg = neg->InsertEdge(&eb, nnp, out);
|
||||
} else {
|
||||
neg = neg->InsertEdge(&ea, nnp, out);
|
||||
pos = pos->InsertEdge(&eb, nnp, out);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
oops();
|
||||
}
|
||||
|
||||
void SBsp2::InsertTriangleHow(int how, STriangle *tr, SMesh *m, SBsp3 *bsp3) {
|
||||
switch(how) {
|
||||
case POS:
|
||||
if(pos) {
|
||||
pos->InsertTriangle(tr, m, bsp3);
|
||||
} else {
|
||||
bsp3->InsertInPlane(true, tr, m);
|
||||
}
|
||||
break;
|
||||
|
||||
case NEG:
|
||||
if(neg) {
|
||||
neg->InsertTriangle(tr, m, bsp3);
|
||||
} else {
|
||||
bsp3->InsertInPlane(false, tr, m);
|
||||
}
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
void SBsp2::InsertTriangle(STriangle *tr, SMesh *m, SBsp3 *bsp3) {
|
||||
double dt[3] = { (tr->a).Dot(no), (tr->b).Dot(no), (tr->c).Dot(no) };
|
||||
|
||||
bool isPos[3], isNeg[3], isOn[3];
|
||||
int inc = 0, posc = 0, negc = 0;
|
||||
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(fabs(dt[i] - d) < LENGTH_EPS) {
|
||||
isOn[i] = true;
|
||||
inc++;
|
||||
} else if(dt[i] > d) {
|
||||
isPos[i] = true;
|
||||
posc++;
|
||||
} else {
|
||||
isNeg[i] = true;
|
||||
negc++;
|
||||
}
|
||||
}
|
||||
|
||||
if(inc == 3) {
|
||||
// All vertices on-line; so it's a degenerate triangle, to ignore.
|
||||
return;
|
||||
}
|
||||
|
||||
// No split required
|
||||
if(posc == 0 || negc == 0) {
|
||||
if(posc > 0) {
|
||||
InsertTriangleHow(POS, tr, m, bsp3);
|
||||
} else {
|
||||
InsertTriangleHow(NEG, tr, m, bsp3);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The polygon must be split into two pieces, one above, one below.
|
||||
Vector a, b, c;
|
||||
|
||||
if(posc == 1 && negc == 1 && inc == 1) {
|
||||
bool bpos;
|
||||
// Standardize so that a is on the plane
|
||||
if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
|
||||
} else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
|
||||
} else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
|
||||
} else oops();
|
||||
|
||||
Vector bPc = IntersectionWith(b, c);
|
||||
STriangle btri = STriangle::From(tr->meta, a, b, bPc);
|
||||
STriangle ctri = STriangle::From(tr->meta, c, a, bPc);
|
||||
|
||||
if(bpos) {
|
||||
InsertTriangleHow(POS, &btri, m, bsp3);
|
||||
InsertTriangleHow(NEG, &ctri, m, bsp3);
|
||||
} else {
|
||||
InsertTriangleHow(POS, &ctri, m, bsp3);
|
||||
InsertTriangleHow(NEG, &btri, m, bsp3);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(posc == 2 && negc == 1) {
|
||||
// Standardize so that a is on one side, and b and c are on the other.
|
||||
if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
|
||||
} else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
|
||||
} else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
|
||||
} else oops();
|
||||
|
||||
} else if(posc == 1 && negc == 2) {
|
||||
if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
|
||||
} else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
|
||||
} else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
|
||||
} else oops();
|
||||
} else oops();
|
||||
|
||||
Vector aPb = IntersectionWith(a, b);
|
||||
Vector cPa = IntersectionWith(c, a);
|
||||
|
||||
STriangle alone = STriangle::From(tr->meta, a, aPb, cPa);
|
||||
STriangle quad1 = STriangle::From(tr->meta, aPb, b, c );
|
||||
STriangle quad2 = STriangle::From(tr->meta, aPb, c, cPa);
|
||||
|
||||
if(posc == 2 && negc == 1) {
|
||||
InsertTriangleHow(POS, &quad1, m, bsp3);
|
||||
InsertTriangleHow(POS, &quad2, m, bsp3);
|
||||
InsertTriangleHow(NEG, &alone, m, bsp3);
|
||||
} else {
|
||||
InsertTriangleHow(NEG, &quad1, m, bsp3);
|
||||
InsertTriangleHow(NEG, &quad2, m, bsp3);
|
||||
InsertTriangleHow(POS, &alone, m, bsp3);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void SBsp2::DebugDraw(Vector n, double d) {
|
||||
if(!this) return;
|
||||
|
||||
if(fabs((edge.a).Dot(n) - d) > LENGTH_EPS) oops();
|
||||
if(fabs((edge.b).Dot(n) - d) > LENGTH_EPS) oops();
|
||||
|
||||
glLineWidth(10);
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(edge.a);
|
||||
glxVertex3v(edge.b);
|
||||
glEnd();
|
||||
pos->DebugDraw(n, d);
|
||||
neg->DebugDraw(n, d);
|
||||
more->DebugDraw(n, d);
|
||||
glLineWidth(1);
|
||||
}
|
||||
|
395
clipboard.cpp
|
@ -1,395 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// The clipboard that gets manipulated when the user selects Edit -> Cut,
|
||||
// Copy, Paste, etc.; may contain entities only, not constraints.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
void SolveSpace::Clipboard::Clear(void) {
|
||||
c.Clear();
|
||||
r.Clear();
|
||||
}
|
||||
|
||||
hEntity SolveSpace::Clipboard::NewEntityFor(hEntity he) {
|
||||
ClipboardRequest *cr;
|
||||
for(cr = r.First(); cr; cr = r.NextAfter(cr)) {
|
||||
if(cr->oldEnt.v == he.v) {
|
||||
return cr->newReq.entity(0);
|
||||
}
|
||||
for(int i = 0; i < MAX_POINTS_IN_ENTITY; i++) {
|
||||
if(cr->oldPointEnt[i].v == he.v) {
|
||||
return cr->newReq.entity(1+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Entity::NO_ENTITY;
|
||||
}
|
||||
|
||||
bool SolveSpace::Clipboard::ContainsEntity(hEntity he) {
|
||||
hEntity hen = NewEntityFor(he);
|
||||
if(hen.v) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::DeleteSelection(void) {
|
||||
SK.request.ClearTags();
|
||||
SK.constraint.ClearTags();
|
||||
List<Selection> *ls = &(selection);
|
||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||
hRequest r = { 0 };
|
||||
if(s->entity.v && s->entity.isFromRequest()) {
|
||||
r = s->entity.request();
|
||||
}
|
||||
if(r.v && !r.IsFromReferences()) {
|
||||
SK.request.Tag(r, 1);
|
||||
}
|
||||
if(s->constraint.v) {
|
||||
SK.constraint.Tag(s->constraint, 1);
|
||||
}
|
||||
}
|
||||
|
||||
SK.constraint.RemoveTagged();
|
||||
// Note that this regenerates and clears the selection, to avoid
|
||||
// lingering references to the just-deleted items.
|
||||
DeleteTaggedRequests();
|
||||
}
|
||||
|
||||
void GraphicsWindow::CopySelection(void) {
|
||||
SS.clipboard.Clear();
|
||||
|
||||
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
||||
Entity *wrkpln = SK.GetEntity(wrkpl->normal);
|
||||
Vector u = wrkpln->NormalU(),
|
||||
v = wrkpln->NormalV(),
|
||||
n = wrkpln->NormalN(),
|
||||
p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
||||
|
||||
List<Selection> *ls = &(selection);
|
||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||
if(!s->entity.v) continue;
|
||||
// Work only on entities that have requests that will generate them.
|
||||
Entity *e = SK.GetEntity(s->entity);
|
||||
bool hasDistance;
|
||||
int req, pts;
|
||||
if(!EntReqTable::GetEntityInfo(e->type, e->extraPoints,
|
||||
&req, &pts, NULL, &hasDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(req == Request::WORKPLANE) continue;
|
||||
|
||||
ClipboardRequest cr;
|
||||
ZERO(&cr);
|
||||
cr.type = req;
|
||||
cr.extraPoints = e->extraPoints;
|
||||
cr.style = e->style;
|
||||
cr.str.strcpy( e->str.str);
|
||||
cr.font.strcpy( e->font.str);
|
||||
cr.construction = e->construction;
|
||||
for(int i = 0; i < pts; i++) {
|
||||
Vector pt = SK.GetEntity(e->point[i])->PointGetNum();
|
||||
pt = pt.Minus(p);
|
||||
pt = pt.DotInToCsys(u, v, n);
|
||||
cr.point[i] = pt;
|
||||
}
|
||||
if(hasDistance) {
|
||||
cr.distance = SK.GetEntity(e->distance)->DistanceGetNum();
|
||||
}
|
||||
|
||||
cr.oldEnt = e->h;
|
||||
for(int i = 0; i < pts; i++) {
|
||||
cr.oldPointEnt[i] = e->point[i];
|
||||
}
|
||||
|
||||
SS.clipboard.r.Add(&cr);
|
||||
}
|
||||
|
||||
Constraint *c;
|
||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
||||
if(c->type == Constraint::POINTS_COINCIDENT) {
|
||||
if(!SS.clipboard.ContainsEntity(c->ptA)) continue;
|
||||
if(!SS.clipboard.ContainsEntity(c->ptB)) continue;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
SS.clipboard.c.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
|
||||
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
||||
Entity *wrkpln = SK.GetEntity(wrkpl->normal);
|
||||
Vector u = wrkpln->NormalU(),
|
||||
v = wrkpln->NormalV(),
|
||||
n = wrkpln->NormalN(),
|
||||
p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
||||
|
||||
ClipboardRequest *cr;
|
||||
for(cr = SS.clipboard.r.First(); cr; cr = SS.clipboard.r.NextAfter(cr)) {
|
||||
hRequest hr = AddRequest(cr->type, false);
|
||||
Request *r = SK.GetRequest(hr);
|
||||
r->extraPoints = cr->extraPoints;
|
||||
r->style = cr->style;
|
||||
r->str.strcpy( cr->str.str);
|
||||
r->font.strcpy( cr->font.str);
|
||||
r->construction = cr->construction;
|
||||
// Need to regen to get the right number of points, if extraPoints
|
||||
// changed.
|
||||
SS.GenerateAll(-1, -1);
|
||||
SS.MarkGroupDirty(r->group);
|
||||
bool hasDistance;
|
||||
int i, pts;
|
||||
EntReqTable::GetRequestInfo(r->type, r->extraPoints,
|
||||
NULL, &pts, NULL, &hasDistance);
|
||||
for(i = 0; i < pts; i++) {
|
||||
Vector pt = cr->point[i];
|
||||
// We need the reflection to occur within the workplane; it may
|
||||
// otherwise correspond to just a rotation as projected.
|
||||
if(scale < 0) {
|
||||
pt.x *= -1;
|
||||
}
|
||||
// Likewise the scale, which could otherwise take us out of the
|
||||
// workplane.
|
||||
pt = pt.ScaledBy(fabs(scale));
|
||||
pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0));
|
||||
pt = pt.Plus(p);
|
||||
pt = pt.RotatedAbout(n, theta);
|
||||
pt = pt.Plus(trans);
|
||||
SK.GetEntity(hr.entity(i+1))->PointForceTo(pt);
|
||||
}
|
||||
if(hasDistance) {
|
||||
SK.GetEntity(hr.entity(64))->DistanceForceTo(
|
||||
cr->distance*fabs(scale));
|
||||
}
|
||||
|
||||
cr->newReq = hr;
|
||||
MakeSelected(hr.entity(0));
|
||||
for(i = 0; i < pts; i++) {
|
||||
MakeSelected(hr.entity(i+1));
|
||||
}
|
||||
}
|
||||
|
||||
Constraint *c;
|
||||
for(c = SS.clipboard.c.First(); c; c = SS.clipboard.c.NextAfter(c)) {
|
||||
if(c->type == Constraint::POINTS_COINCIDENT) {
|
||||
Constraint::ConstrainCoincident(SS.clipboard.NewEntityFor(c->ptA),
|
||||
SS.clipboard.NewEntityFor(c->ptB));
|
||||
}
|
||||
}
|
||||
|
||||
SS.later.generateAll = true;
|
||||
}
|
||||
|
||||
void GraphicsWindow::MenuClipboard(int id) {
|
||||
if(id != MNU_DELETE && !SS.GW.LockedInWorkplane()) {
|
||||
Error("Cut, paste, and copy work only in a workplane.\n\n"
|
||||
"Select one with Sketch -> In Workplane.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch(id) {
|
||||
case MNU_PASTE: {
|
||||
SS.UndoRemember();
|
||||
Vector trans = SS.GW.projRight.ScaledBy(80/SS.GW.scale).Plus(
|
||||
SS.GW.projUp .ScaledBy(40/SS.GW.scale));
|
||||
SS.GW.ClearSelection();
|
||||
SS.GW.PasteClipboard(trans, 0, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_PASTE_TRANSFORM: {
|
||||
if(SS.clipboard.r.n == 0) {
|
||||
Error("Clipboard is empty; nothing to paste.");
|
||||
break;
|
||||
}
|
||||
|
||||
Entity *wrkpl = SK.GetEntity(SS.GW.ActiveWorkplane());
|
||||
Vector p = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
||||
SS.TW.shown.paste.times = 1;
|
||||
SS.TW.shown.paste.trans = Vector::From(0, 0, 0);
|
||||
SS.TW.shown.paste.theta = 0;
|
||||
SS.TW.shown.paste.origin = p;
|
||||
SS.TW.shown.paste.scale = 1;
|
||||
SS.TW.GoToScreen(TextWindow::SCREEN_PASTE_TRANSFORMED);
|
||||
SS.GW.ForceTextWindowShown();
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_COPY:
|
||||
SS.GW.CopySelection();
|
||||
SS.GW.ClearSelection();
|
||||
break;
|
||||
|
||||
case MNU_CUT:
|
||||
SS.UndoRemember();
|
||||
SS.GW.CopySelection();
|
||||
SS.GW.DeleteSelection();
|
||||
break;
|
||||
|
||||
case MNU_DELETE:
|
||||
SS.UndoRemember();
|
||||
SS.GW.DeleteSelection();
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
bool TextWindow::EditControlDoneForPaste(char *s) {
|
||||
Expr *e;
|
||||
switch(edit.meaning) {
|
||||
case EDIT_PASTE_TIMES_REPEATED: {
|
||||
e = Expr::From(s, true);
|
||||
if(!e) break;
|
||||
int v = (int)e->Eval();
|
||||
if(v > 0) {
|
||||
shown.paste.times = v;
|
||||
} else {
|
||||
Error("Number of copies to paste must be at least one.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EDIT_PASTE_ANGLE:
|
||||
e = Expr::From(s, true);
|
||||
if(!e) break;
|
||||
shown.paste.theta = WRAP_SYMMETRIC((e->Eval())*PI/180, 2*PI);
|
||||
break;
|
||||
|
||||
case EDIT_PASTE_SCALE: {
|
||||
e = Expr::From(s, true);
|
||||
double v = e->Eval();
|
||||
if(fabs(v) > 1e-6) {
|
||||
shown.paste.scale = v;
|
||||
} else {
|
||||
Error("Scale cannot be zero.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangePasteTransformed(int link, DWORD v) {
|
||||
char str[300];
|
||||
switch(link) {
|
||||
case 't':
|
||||
sprintf(str, "%d", SS.TW.shown.paste.times);
|
||||
SS.TW.ShowEditControl(10, 13, str);
|
||||
SS.TW.edit.meaning = EDIT_PASTE_TIMES_REPEATED;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
sprintf(str, "%.3f", SS.TW.shown.paste.theta*180/PI);
|
||||
SS.TW.ShowEditControl(12, 13, str);
|
||||
SS.TW.edit.meaning = EDIT_PASTE_ANGLE;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
sprintf(str, "%.3f", SS.TW.shown.paste.scale);
|
||||
SS.TW.ShowEditControl(18, 13, str);
|
||||
SS.TW.edit.meaning = EDIT_PASTE_SCALE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TextWindow::ScreenPasteTransformed(int link, DWORD v) {
|
||||
SS.GW.GroupSelection();
|
||||
switch(link) {
|
||||
case 'o':
|
||||
if(SS.GW.gs.points == 1 && SS.GW.gs.n == 1) {
|
||||
Entity *e = SK.GetEntity(SS.GW.gs.point[0]);
|
||||
SS.TW.shown.paste.origin = e->PointGetNum();
|
||||
} else {
|
||||
Error("Select one point to define origin of rotation.");
|
||||
}
|
||||
SS.GW.ClearSelection();
|
||||
break;
|
||||
|
||||
case 't':
|
||||
if(SS.GW.gs.points == 2 && SS.GW.gs.n == 2) {
|
||||
Entity *pa = SK.GetEntity(SS.GW.gs.point[0]),
|
||||
*pb = SK.GetEntity(SS.GW.gs.point[1]);
|
||||
SS.TW.shown.paste.trans =
|
||||
(pb->PointGetNum()).Minus(pa->PointGetNum());
|
||||
} else {
|
||||
Error("Select two points to define translation vector.");
|
||||
}
|
||||
SS.GW.ClearSelection();
|
||||
break;
|
||||
|
||||
case 'g': {
|
||||
if(fabs(SS.TW.shown.paste.theta) < LENGTH_EPS &&
|
||||
SS.TW.shown.paste.trans.Magnitude() < LENGTH_EPS &&
|
||||
SS.TW.shown.paste.times != 1)
|
||||
{
|
||||
Message("Transformation is identity. So all copies will be "
|
||||
"exactly on top of each other.");
|
||||
}
|
||||
if(SS.TW.shown.paste.times*SS.clipboard.r.n > 100) {
|
||||
Error("Too many items to paste; split this into smaller "
|
||||
"pastes.");
|
||||
break;
|
||||
}
|
||||
if(!SS.GW.LockedInWorkplane()) {
|
||||
Error("No workplane active.");
|
||||
break;
|
||||
}
|
||||
Entity *wrkpl = SK.GetEntity(SS.GW.ActiveWorkplane());
|
||||
Entity *wrkpln = SK.GetEntity(wrkpl->normal);
|
||||
Vector wn = wrkpln->NormalN();
|
||||
SS.UndoRemember();
|
||||
SS.GW.ClearSelection();
|
||||
for(int i = 0; i < SS.TW.shown.paste.times; i++) {
|
||||
Vector trans = SS.TW.shown.paste.trans.ScaledBy(i+1),
|
||||
origin = SS.TW.shown.paste.origin;
|
||||
double theta = SS.TW.shown.paste.theta*(i+1);
|
||||
// desired transformation is Q*(p - o) + o + t =
|
||||
// Q*p - Q*o + o + t = Q*p + (t + o - Q*o)
|
||||
Vector t = trans.Plus(
|
||||
origin).Minus(
|
||||
origin.RotatedAbout(wn, theta));
|
||||
|
||||
SS.GW.PasteClipboard(t, theta, SS.TW.shown.paste.scale);
|
||||
}
|
||||
SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextWindow::ShowPasteTransformed(void) {
|
||||
Printf(true, "%FtPASTE TRANSFORMED%E");
|
||||
Printf(true, "%Ba %Ftrepeat%E %d time%s %Fl%Lt%f[change]%E",
|
||||
shown.paste.times, (shown.paste.times == 1) ? "" : "s",
|
||||
&ScreenChangePasteTransformed);
|
||||
Printf(false, "%Bd %Ftrotate%E %@ degrees %Fl%Lr%f[change]%E",
|
||||
shown.paste.theta*180/PI,
|
||||
&ScreenChangePasteTransformed);
|
||||
Printf(false, "%Ba %Ftabout pt%E (%s, %s, %s) %Fl%Lo%f[use selected]%E",
|
||||
SS.MmToString(shown.paste.origin.x),
|
||||
SS.MmToString(shown.paste.origin.y),
|
||||
SS.MmToString(shown.paste.origin.z),
|
||||
&ScreenPasteTransformed);
|
||||
Printf(false, "%Bd %Fttranslate%E (%s, %s, %s) %Fl%Lt%f[use selected]%E",
|
||||
SS.MmToString(shown.paste.trans.x),
|
||||
SS.MmToString(shown.paste.trans.y),
|
||||
SS.MmToString(shown.paste.trans.z),
|
||||
&ScreenPasteTransformed);
|
||||
Printf(false, "%Ba %Ftscale%E %@ %Fl%Ls%f[change]%E",
|
||||
shown.paste.scale,
|
||||
&ScreenChangePasteTransformed);
|
||||
|
||||
Printf(true, " %Fl%Lg%fpaste transformed now%E", &ScreenPasteTransformed);
|
||||
|
||||
Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
|
||||
}
|
||||
|
433
confscreen.cpp
|
@ -1,433 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// For the configuration screen, setup items that are not specific to the
|
||||
// file being edited right now.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
void TextWindow::ScreenChangeLightDirection(int link, DWORD v) {
|
||||
char str[1024];
|
||||
sprintf(str, "%.2f, %.2f, %.2f", CO(SS.lightDir[v]));
|
||||
SS.TW.ShowEditControl(29+2*v, 8, str);
|
||||
SS.TW.edit.meaning = EDIT_LIGHT_DIRECTION;
|
||||
SS.TW.edit.i = v;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeLightIntensity(int link, DWORD v) {
|
||||
char str[1024];
|
||||
sprintf(str, "%.2f", SS.lightIntensity[v]);
|
||||
SS.TW.ShowEditControl(29+2*v, 31, str);
|
||||
SS.TW.edit.meaning = EDIT_LIGHT_INTENSITY;
|
||||
SS.TW.edit.i = v;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeColor(int link, DWORD v) {
|
||||
SS.TW.ShowEditControlWithColorPicker(9+2*v, 13, SS.modelColor[v]);
|
||||
|
||||
SS.TW.edit.meaning = EDIT_COLOR;
|
||||
SS.TW.edit.i = v;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeChordTolerance(int link, DWORD v) {
|
||||
char str[1024];
|
||||
sprintf(str, "%.2f", SS.chordTol);
|
||||
SS.TW.ShowEditControl(37, 3, str);
|
||||
SS.TW.edit.meaning = EDIT_CHORD_TOLERANCE;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeMaxSegments(int link, DWORD v) {
|
||||
char str[1024];
|
||||
sprintf(str, "%d", SS.maxSegments);
|
||||
SS.TW.ShowEditControl(41, 3, str);
|
||||
SS.TW.edit.meaning = EDIT_MAX_SEGMENTS;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) {
|
||||
char str[1024];
|
||||
sprintf(str, "%.3f", 1000*SS.cameraTangent);
|
||||
SS.TW.ShowEditControl(47, 3, str);
|
||||
SS.TW.edit.meaning = EDIT_CAMERA_TANGENT;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeGridSpacing(int link, DWORD v) {
|
||||
SS.TW.ShowEditControl(51, 3, SS.MmToString(SS.gridSpacing));
|
||||
SS.TW.edit.meaning = EDIT_GRID_SPACING;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeDigitsAfterDecimal(int link, DWORD v) {
|
||||
char buf[128];
|
||||
sprintf(buf, "%d", SS.UnitDigitsAfterDecimal());
|
||||
SS.TW.ShowEditControl(55, 3, buf);
|
||||
SS.TW.edit.meaning = EDIT_DIGITS_AFTER_DECIMAL;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeExportScale(int link, DWORD v) {
|
||||
char str[1024];
|
||||
sprintf(str, "%.3f", (double)SS.exportScale);
|
||||
|
||||
SS.TW.ShowEditControl(61, 5, str);
|
||||
SS.TW.edit.meaning = EDIT_EXPORT_SCALE;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeExportOffset(int link, DWORD v) {
|
||||
SS.TW.ShowEditControl(65, 3, SS.MmToString(SS.exportOffset));
|
||||
SS.TW.edit.meaning = EDIT_EXPORT_OFFSET;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeFixExportColors(int link, DWORD v) {
|
||||
SS.fixExportColors = !SS.fixExportColors;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeBackFaces(int link, DWORD v) {
|
||||
SS.drawBackFaces = !SS.drawBackFaces;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeCheckClosedContour(int link, DWORD v) {
|
||||
SS.checkClosedContour = !SS.checkClosedContour;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeShadedTriangles(int link, DWORD v) {
|
||||
SS.exportShadedTriangles = !SS.exportShadedTriangles;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangePwlCurves(int link, DWORD v) {
|
||||
SS.exportPwlCurves = !SS.exportPwlCurves;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeCanvasSizeAuto(int link, DWORD v) {
|
||||
if(link == 't') {
|
||||
SS.exportCanvasSizeAuto = true;
|
||||
} else {
|
||||
SS.exportCanvasSizeAuto = false;
|
||||
}
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeCanvasSize(int link, DWORD v) {
|
||||
double d;
|
||||
switch(v) {
|
||||
case 0: d = SS.exportMargin.left; break;
|
||||
case 1: d = SS.exportMargin.right; break;
|
||||
case 2: d = SS.exportMargin.bottom; break;
|
||||
case 3: d = SS.exportMargin.top; break;
|
||||
|
||||
case 10: d = SS.exportCanvas.width; break;
|
||||
case 11: d = SS.exportCanvas.height; break;
|
||||
case 12: d = SS.exportCanvas.dx; break;
|
||||
case 13: d = SS.exportCanvas.dy; break;
|
||||
|
||||
default: return;
|
||||
}
|
||||
int row = 81, col;
|
||||
if(v < 10) {
|
||||
row += v*2;
|
||||
col = 11;
|
||||
} else {
|
||||
row += (v - 10)*2;
|
||||
col = 13;
|
||||
}
|
||||
SS.TW.ShowEditControl(row, col, SS.MmToString(d));
|
||||
SS.TW.edit.meaning = EDIT_CANVAS_SIZE;
|
||||
SS.TW.edit.i = v;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenChangeGCodeParameter(int link, DWORD v) {
|
||||
char buf[1024] = "";
|
||||
int row = 93;
|
||||
switch(link) {
|
||||
case 'd':
|
||||
SS.TW.edit.meaning = EDIT_G_CODE_DEPTH;
|
||||
strcpy(buf, SS.MmToString(SS.gCode.depth));
|
||||
row += 0;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
SS.TW.edit.meaning = EDIT_G_CODE_PASSES;
|
||||
sprintf(buf, "%d", SS.gCode.passes);
|
||||
row += 2;
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
SS.TW.edit.meaning = EDIT_G_CODE_FEED;
|
||||
strcpy(buf, SS.MmToString(SS.gCode.feed));
|
||||
row += 4;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
SS.TW.edit.meaning = EDIT_G_CODE_PLUNGE_FEED;
|
||||
strcpy(buf, SS.MmToString(SS.gCode.plungeFeed));
|
||||
row += 6;
|
||||
break;
|
||||
}
|
||||
SS.TW.ShowEditControl(row, 14, buf);
|
||||
}
|
||||
|
||||
void TextWindow::ShowConfiguration(void) {
|
||||
int i;
|
||||
Printf(true, "%Ft user color (r, g, b)");
|
||||
|
||||
for(i = 0; i < SS.MODEL_COLORS; i++) {
|
||||
Printf(false, "%Bp #%d: %Bp %Bp (%@, %@, %@) %f%D%Ll%Fl[change]%E",
|
||||
(i & 1) ? 'd' : 'a',
|
||||
i, 0x80000000 | SS.modelColor[i],
|
||||
(i & 1) ? 'd' : 'a',
|
||||
REDf(SS.modelColor[i]),
|
||||
GREENf(SS.modelColor[i]),
|
||||
BLUEf(SS.modelColor[i]),
|
||||
&ScreenChangeColor, i);
|
||||
}
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, "%Ft light direction intensity");
|
||||
for(i = 0; i < 2; i++) {
|
||||
Printf(false, "%Bp #%d (%2,%2,%2)%Fl%D%f%Ll[c]%E "
|
||||
"%2 %Fl%D%f%Ll[c]%E",
|
||||
(i & 1) ? 'd' : 'a', i,
|
||||
CO(SS.lightDir[i]), i, &ScreenChangeLightDirection,
|
||||
SS.lightIntensity[i], i, &ScreenChangeLightIntensity);
|
||||
}
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, "%Ft chord tolerance (in screen pixels)%E");
|
||||
Printf(false, "%Ba %@ %Fl%Ll%f%D[change]%E; now %d triangles",
|
||||
SS.chordTol,
|
||||
&ScreenChangeChordTolerance, 0,
|
||||
SK.GetGroup(SS.GW.activeGroup)->displayMesh.l.n);
|
||||
Printf(false, "%Ft max piecewise linear segments%E");
|
||||
Printf(false, "%Ba %d %Fl%Ll%f[change]%E",
|
||||
SS.maxSegments,
|
||||
&ScreenChangeMaxSegments);
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, "%Ft perspective factor (0 for parallel)%E");
|
||||
Printf(false, "%Ba %# %Fl%Ll%f%D[change]%E",
|
||||
SS.cameraTangent*1000,
|
||||
&ScreenChangeCameraTangent, 0);
|
||||
Printf(false, "%Ft snap grid spacing%E");
|
||||
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.gridSpacing),
|
||||
&ScreenChangeGridSpacing, 0);
|
||||
Printf(false, "%Ft digits after decimal point to show%E");
|
||||
Printf(false, "%Ba %d %Fl%Ll%f%D[change]%E (e.g. '%s')",
|
||||
SS.UnitDigitsAfterDecimal(),
|
||||
&ScreenChangeDigitsAfterDecimal, 0,
|
||||
SS.MmToString(SS.StringToMm("1.23456789")));
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, "%Ft export scale factor (1:1=mm, 1:25.4=inch)");
|
||||
Printf(false, "%Ba 1:%# %Fl%Ll%f%D[change]%E",
|
||||
(double)SS.exportScale,
|
||||
&ScreenChangeExportScale, 0);
|
||||
Printf(false, "%Ft cutter radius offset (0=no offset) ");
|
||||
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportOffset),
|
||||
&ScreenChangeExportOffset, 0);
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, " %Fd%f%Ll%c export shaded 2d triangles%E",
|
||||
&ScreenChangeShadedTriangles,
|
||||
SS.exportShadedTriangles ? CHECK_TRUE : CHECK_FALSE);
|
||||
if(fabs(SS.exportOffset) > LENGTH_EPS) {
|
||||
Printf(false, " %Fd%c curves as piecewise linear%E "
|
||||
"(since cutter radius is not zero)", CHECK_TRUE);
|
||||
} else {
|
||||
Printf(false, " %Fd%f%Ll%c export curves as piecewise linear%E",
|
||||
&ScreenChangePwlCurves,
|
||||
SS.exportPwlCurves ? CHECK_TRUE : CHECK_FALSE);
|
||||
}
|
||||
Printf(false, " %Fd%f%Ll%c fix white exported lines%E",
|
||||
&ScreenChangeFixExportColors,
|
||||
SS.fixExportColors ? CHECK_TRUE : CHECK_FALSE);
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, "%Ft export canvas size: "
|
||||
"%f%Fd%Lf%c fixed%E "
|
||||
"%f%Fd%Lt%c auto%E",
|
||||
&ScreenChangeCanvasSizeAuto,
|
||||
!SS.exportCanvasSizeAuto ? RADIO_TRUE : RADIO_FALSE,
|
||||
&ScreenChangeCanvasSizeAuto,
|
||||
SS.exportCanvasSizeAuto ? RADIO_TRUE : RADIO_FALSE);
|
||||
|
||||
if(SS.exportCanvasSizeAuto) {
|
||||
Printf(false, "%Ft (by margins around exported geometry)");
|
||||
Printf(false, "%Ba%Ft left: %Fd%s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportMargin.left), &ScreenChangeCanvasSize, 0);
|
||||
Printf(false, "%Bd%Ft right: %Fd%s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportMargin.right), &ScreenChangeCanvasSize, 1);
|
||||
Printf(false, "%Ba%Ft bottom: %Fd%s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportMargin.bottom), &ScreenChangeCanvasSize, 2);
|
||||
Printf(false, "%Bd%Ft top: %Fd%s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportMargin.top), &ScreenChangeCanvasSize, 3);
|
||||
} else {
|
||||
Printf(false, "%Ft (by absolute dimensions and offsets)");
|
||||
Printf(false, "%Ba%Ft width: %Fd%s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportCanvas.width), &ScreenChangeCanvasSize, 10);
|
||||
Printf(false, "%Bd%Ft height: %Fd%s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportCanvas.height), &ScreenChangeCanvasSize, 11);
|
||||
Printf(false, "%Ba%Ft offset x: %Fd%s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportCanvas.dx), &ScreenChangeCanvasSize, 12);
|
||||
Printf(false, "%Bd%Ft offset y: %Fd%s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.exportCanvas.dy), &ScreenChangeCanvasSize, 13);
|
||||
}
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, "%Ft exported g code parameters");
|
||||
Printf(false, "%Ba%Ft depth: %Fd%s %Fl%Ld%f[change]%E",
|
||||
SS.MmToString(SS.gCode.depth), &ScreenChangeGCodeParameter);
|
||||
Printf(false, "%Bd%Ft passes: %Fd%d %Fl%Ls%f[change]%E",
|
||||
SS.gCode.passes, &ScreenChangeGCodeParameter);
|
||||
Printf(false, "%Ba%Ft feed: %Fd%s %Fl%LF%f[change]%E",
|
||||
SS.MmToString(SS.gCode.feed), &ScreenChangeGCodeParameter);
|
||||
Printf(false, "%Bd%Ft plunge fd: %Fd%s %Fl%LP%f[change]%E",
|
||||
SS.MmToString(SS.gCode.plungeFeed), &ScreenChangeGCodeParameter);
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, " %Fd%f%Ll%c draw triangle back faces in red%E",
|
||||
&ScreenChangeBackFaces,
|
||||
SS.drawBackFaces ? CHECK_TRUE : CHECK_FALSE);
|
||||
Printf(false, " %Fd%f%Ll%c check sketch for closed contour%E",
|
||||
&ScreenChangeCheckClosedContour,
|
||||
SS.checkClosedContour ? CHECK_TRUE : CHECK_FALSE);
|
||||
|
||||
Printf(false, "");
|
||||
Printf(false, " %Ftgl vendor %E%s", glGetString(GL_VENDOR));
|
||||
Printf(false, " %Ft renderer %E%s", glGetString(GL_RENDERER));
|
||||
Printf(false, " %Ft version %E%s", glGetString(GL_VERSION));
|
||||
}
|
||||
|
||||
bool TextWindow::EditControlDoneForConfiguration(char *s) {
|
||||
switch(edit.meaning) {
|
||||
case EDIT_LIGHT_INTENSITY:
|
||||
SS.lightIntensity[edit.i] = min(1, max(0, atof(s)));
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
case EDIT_LIGHT_DIRECTION: {
|
||||
double x, y, z;
|
||||
if(sscanf(s, "%lf, %lf, %lf", &x, &y, &z)==3) {
|
||||
SS.lightDir[edit.i] = Vector::From(x, y, z);
|
||||
} else {
|
||||
Error("Bad format: specify coordinates as x, y, z");
|
||||
}
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
}
|
||||
case EDIT_COLOR: {
|
||||
Vector rgb;
|
||||
if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
|
||||
rgb = rgb.ClampWithin(0, 1);
|
||||
SS.modelColor[edit.i] = RGBf(rgb.x, rgb.y, rgb.z);
|
||||
} else {
|
||||
Error("Bad format: specify color as r, g, b");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EDIT_CHORD_TOLERANCE: {
|
||||
SS.chordTol = min(10, max(0.1, atof(s)));
|
||||
SS.GenerateAll(0, INT_MAX);
|
||||
break;
|
||||
}
|
||||
case EDIT_MAX_SEGMENTS: {
|
||||
SS.maxSegments = min(1000, max(7, atoi(s)));
|
||||
SS.GenerateAll(0, INT_MAX);
|
||||
break;
|
||||
}
|
||||
case EDIT_CAMERA_TANGENT: {
|
||||
SS.cameraTangent = (min(2, max(0, atof(s))))/1000.0;
|
||||
if(!SS.usePerspectiveProj) {
|
||||
Message("The perspective factor will have no effect until you "
|
||||
"enable View -> Use Perspective Projection.");
|
||||
}
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
}
|
||||
case EDIT_GRID_SPACING: {
|
||||
SS.gridSpacing = (float)min(1e4, max(1e-3, SS.StringToMm(s)));
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
}
|
||||
case EDIT_DIGITS_AFTER_DECIMAL: {
|
||||
int v = atoi(s);
|
||||
if(v < 0 || v > 8) {
|
||||
Error("Specify between 0 and 8 digits after the decimal.");
|
||||
} else {
|
||||
SS.SetUnitDigitsAfterDecimal(v);
|
||||
}
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
}
|
||||
case EDIT_EXPORT_SCALE: {
|
||||
Expr *e = Expr::From(s, true);
|
||||
if(e) {
|
||||
double ev = e->Eval();
|
||||
if(fabs(ev) < 0.001 || isnan(ev)) {
|
||||
Error("Export scale must not be zero!");
|
||||
} else {
|
||||
SS.exportScale = (float)ev;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EDIT_EXPORT_OFFSET: {
|
||||
Expr *e = Expr::From(s, true);
|
||||
if(e) {
|
||||
double ev = SS.ExprToMm(e);
|
||||
if(isnan(ev) || ev < 0) {
|
||||
Error("Cutter radius offset must not be negative!");
|
||||
} else {
|
||||
SS.exportOffset = (float)ev;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EDIT_CANVAS_SIZE: {
|
||||
Expr *e = Expr::From(s, true);
|
||||
if(!e) {
|
||||
break;
|
||||
}
|
||||
float d = (float)SS.ExprToMm(e);
|
||||
switch(edit.i) {
|
||||
case 0: SS.exportMargin.left = d; break;
|
||||
case 1: SS.exportMargin.right = d; break;
|
||||
case 2: SS.exportMargin.bottom = d; break;
|
||||
case 3: SS.exportMargin.top = d; break;
|
||||
|
||||
case 10: SS.exportCanvas.width = d; break;
|
||||
case 11: SS.exportCanvas.height = d; break;
|
||||
case 12: SS.exportCanvas.dx = d; break;
|
||||
case 13: SS.exportCanvas.dy = d; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EDIT_G_CODE_DEPTH: {
|
||||
Expr *e = Expr::From(s, true);
|
||||
if(e) SS.gCode.depth = (float)SS.ExprToMm(e);
|
||||
break;
|
||||
}
|
||||
case EDIT_G_CODE_PASSES: {
|
||||
Expr *e = Expr::From(s, true);
|
||||
if(e) SS.gCode.passes = (int)(e->Eval());
|
||||
SS.gCode.passes = max(1, min(1000, SS.gCode.passes));
|
||||
break;
|
||||
}
|
||||
case EDIT_G_CODE_FEED: {
|
||||
Expr *e = Expr::From(s, true);
|
||||
if(e) SS.gCode.feed = (float)SS.ExprToMm(e);
|
||||
break;
|
||||
}
|
||||
case EDIT_G_CODE_PLUNGE_FEED: {
|
||||
Expr *e = Expr::From(s, true);
|
||||
if(e) SS.gCode.plungeFeed = (float)SS.ExprToMm(e);
|
||||
break;
|
||||
}
|
||||
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,339 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// The screens when an entity is selected, that show some description of it--
|
||||
// endpoints of the lines, diameter of the circle, etc.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
void TextWindow::ScreenUnselectAll(int link, DWORD v) {
|
||||
GraphicsWindow::MenuEdit(GraphicsWindow::MNU_UNSELECT_ALL);
|
||||
}
|
||||
|
||||
void TextWindow::ScreenEditTtfText(int link, DWORD v) {
|
||||
hRequest hr = { v };
|
||||
Request *r = SK.GetRequest(hr);
|
||||
|
||||
SS.TW.ShowEditControl(13, 10, r->str.str);
|
||||
SS.TW.edit.meaning = EDIT_TTF_TEXT;
|
||||
SS.TW.edit.request = hr;
|
||||
}
|
||||
|
||||
#define gs (SS.GW.gs)
|
||||
void TextWindow::ScreenSetTtfFont(int link, DWORD v) {
|
||||
int i = (int)v;
|
||||
if(i < 0) return;
|
||||
if(i >= SS.fonts.l.n) return;
|
||||
|
||||
SS.GW.GroupSelection();
|
||||
if(gs.entities != 1 || gs.n != 1) return;
|
||||
|
||||
Entity *e = SK.entity.FindByIdNoOops(gs.entity[0]);
|
||||
if(!e || e->type != Entity::TTF_TEXT || !e->h.isFromRequest()) return;
|
||||
|
||||
Request *r = SK.request.FindByIdNoOops(e->h.request());
|
||||
if(!r) return;
|
||||
|
||||
SS.UndoRemember();
|
||||
r->font.strcpy(SS.fonts.l.elem[i].FontFileBaseName());
|
||||
SS.MarkGroupDirty(r->group);
|
||||
SS.later.generateAll = true;
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
|
||||
void TextWindow::DescribeSelection(void) {
|
||||
Entity *e;
|
||||
Vector p;
|
||||
int i;
|
||||
Printf(false, "");
|
||||
|
||||
if(gs.n == 1 && (gs.points == 1 || gs.entities == 1)) {
|
||||
e = SK.GetEntity(gs.points == 1 ? gs.point[0] : gs.entity[0]);
|
||||
|
||||
#define COSTR(p) \
|
||||
SS.MmToString((p).x), SS.MmToString((p).y), SS.MmToString((p).z)
|
||||
#define PT_AS_STR "(%Fi%s%E, %Fi%s%E, %Fi%s%E)"
|
||||
#define PT_AS_NUM "(%Fi%3%E, %Fi%3%E, %Fi%3%E)"
|
||||
switch(e->type) {
|
||||
case Entity::POINT_IN_3D:
|
||||
case Entity::POINT_IN_2D:
|
||||
case Entity::POINT_N_TRANS:
|
||||
case Entity::POINT_N_ROT_TRANS:
|
||||
case Entity::POINT_N_COPY:
|
||||
case Entity::POINT_N_ROT_AA:
|
||||
p = e->PointGetNum();
|
||||
Printf(false, "%FtPOINT%E at " PT_AS_STR, COSTR(p));
|
||||
break;
|
||||
|
||||
case Entity::NORMAL_IN_3D:
|
||||
case Entity::NORMAL_IN_2D:
|
||||
case Entity::NORMAL_N_COPY:
|
||||
case Entity::NORMAL_N_ROT:
|
||||
case Entity::NORMAL_N_ROT_AA: {
|
||||
Quaternion q = e->NormalGetNum();
|
||||
p = q.RotationN();
|
||||
Printf(false, "%FtNORMAL / COORDINATE SYSTEM%E");
|
||||
Printf(true, " basis n = " PT_AS_NUM, CO(p));
|
||||
p = q.RotationU();
|
||||
Printf(false, " u = " PT_AS_NUM, CO(p));
|
||||
p = q.RotationV();
|
||||
Printf(false, " v = " PT_AS_NUM, CO(p));
|
||||
break;
|
||||
}
|
||||
case Entity::WORKPLANE: {
|
||||
p = SK.GetEntity(e->point[0])->PointGetNum();
|
||||
Printf(false, "%FtWORKPLANE%E");
|
||||
Printf(true, " origin = " PT_AS_STR, COSTR(p));
|
||||
Quaternion q = e->Normal()->NormalGetNum();
|
||||
p = q.RotationN();
|
||||
Printf(true, " normal = " PT_AS_NUM, CO(p));
|
||||
break;
|
||||
}
|
||||
case Entity::LINE_SEGMENT: {
|
||||
Vector p0 = SK.GetEntity(e->point[0])->PointGetNum();
|
||||
p = p0;
|
||||
Printf(false, "%FtLINE SEGMENT%E");
|
||||
Printf(true, " thru " PT_AS_STR, COSTR(p));
|
||||
Vector p1 = SK.GetEntity(e->point[1])->PointGetNum();
|
||||
p = p1;
|
||||
Printf(false, " " PT_AS_STR, COSTR(p));
|
||||
Printf(true, " len = %Fi%s%E",
|
||||
SS.MmToString((p1.Minus(p0).Magnitude())));
|
||||
break;
|
||||
}
|
||||
case Entity::CUBIC_PERIODIC:
|
||||
case Entity::CUBIC:
|
||||
int pts;
|
||||
if(e->type == Entity::CUBIC_PERIODIC) {
|
||||
Printf(false, "%FtPERIODIC C2 CUBIC SPLINE%E");
|
||||
pts = (3 + e->extraPoints);
|
||||
} else if(e->extraPoints > 0) {
|
||||
Printf(false, "%FtINTERPOLATING C2 CUBIC SPLINE%E");
|
||||
pts = (4 + e->extraPoints);
|
||||
} else {
|
||||
Printf(false, "%FtCUBIC BEZIER CURVE%E");
|
||||
pts = 4;
|
||||
}
|
||||
for(i = 0; i < pts; i++) {
|
||||
p = SK.GetEntity(e->point[i])->PointGetNum();
|
||||
Printf((i==0), " p%d = " PT_AS_STR, i, COSTR(p));
|
||||
}
|
||||
break;
|
||||
|
||||
case Entity::ARC_OF_CIRCLE: {
|
||||
Printf(false, "%FtARC OF A CIRCLE%E");
|
||||
p = SK.GetEntity(e->point[0])->PointGetNum();
|
||||
Printf(true, " center = " PT_AS_STR, COSTR(p));
|
||||
p = SK.GetEntity(e->point[1])->PointGetNum();
|
||||
Printf(true, " endpoints = " PT_AS_STR, COSTR(p));
|
||||
p = SK.GetEntity(e->point[2])->PointGetNum();
|
||||
Printf(false, " " PT_AS_STR, COSTR(p));
|
||||
double r = e->CircleGetRadiusNum();
|
||||
Printf(true, " diameter = %Fi%s", SS.MmToString(r*2));
|
||||
Printf(false, " radius = %Fi%s", SS.MmToString(r));
|
||||
double thetas, thetaf, dtheta;
|
||||
e->ArcGetAngles(&thetas, &thetaf, &dtheta);
|
||||
Printf(false, " arc len = %Fi%s", SS.MmToString(dtheta*r));
|
||||
break;
|
||||
}
|
||||
case Entity::CIRCLE: {
|
||||
Printf(false, "%FtCIRCLE%E");
|
||||
p = SK.GetEntity(e->point[0])->PointGetNum();
|
||||
Printf(true, " center = " PT_AS_STR, COSTR(p));
|
||||
double r = e->CircleGetRadiusNum();
|
||||
Printf(true, " diameter = %Fi%s", SS.MmToString(r*2));
|
||||
Printf(false, " radius = %Fi%s", SS.MmToString(r));
|
||||
break;
|
||||
}
|
||||
case Entity::FACE_NORMAL_PT:
|
||||
case Entity::FACE_XPROD:
|
||||
case Entity::FACE_N_ROT_TRANS:
|
||||
case Entity::FACE_N_ROT_AA:
|
||||
case Entity::FACE_N_TRANS:
|
||||
Printf(false, "%FtPLANE FACE%E");
|
||||
p = e->FaceGetNormalNum();
|
||||
Printf(true, " normal = " PT_AS_NUM, CO(p));
|
||||
p = e->FaceGetPointNum();
|
||||
Printf(false, " thru = " PT_AS_STR, COSTR(p));
|
||||
break;
|
||||
|
||||
case Entity::TTF_TEXT: {
|
||||
Printf(false, "%FtTRUETYPE FONT TEXT%E");
|
||||
Printf(true, " font = '%Fi%s%E'", e->font.str);
|
||||
if(e->h.isFromRequest()) {
|
||||
Printf(false, " text = '%Fi%s%E' %Fl%Ll%f%D[change]%E",
|
||||
e->str.str, &ScreenEditTtfText, e->h.request());
|
||||
Printf(true, " select new font");
|
||||
SS.fonts.LoadAll();
|
||||
int i;
|
||||
for(i = 0; i < SS.fonts.l.n; i++) {
|
||||
TtfFont *tf = &(SS.fonts.l.elem[i]);
|
||||
if(strcmp(e->font.str, tf->FontFileBaseName())==0) {
|
||||
Printf(false, "%Bp %s",
|
||||
(i & 1) ? 'd' : 'a',
|
||||
tf->name.str);
|
||||
} else {
|
||||
Printf(false, "%Bp %f%D%Fl%Ll%s%E%Bp",
|
||||
(i & 1) ? 'd' : 'a',
|
||||
&ScreenSetTtfFont, i,
|
||||
tf->name.str,
|
||||
(i & 1) ? 'd' : 'a');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Printf(false, " text = '%Fi%s%E'", e->str.str);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Printf(true, "%Ft?? ENTITY%E");
|
||||
break;
|
||||
}
|
||||
|
||||
Group *g = SK.GetGroup(e->group);
|
||||
Printf(false, "");
|
||||
Printf(false, "%FtIN GROUP%E %s", g->DescriptionString());
|
||||
if(e->workplane.v == Entity::FREE_IN_3D.v) {
|
||||
Printf(false, "%FtNOT LOCKED IN WORKPLANE%E");
|
||||
} else {
|
||||
Entity *w = SK.GetEntity(e->workplane);
|
||||
Printf(false, "%FtIN WORKPLANE%E %s", w->DescriptionString());
|
||||
}
|
||||
if(e->style.v) {
|
||||
Style *s = Style::Get(e->style);
|
||||
Printf(false, "%FtIN STYLE%E %s", s->DescriptionString());
|
||||
} else {
|
||||
Printf(false, "%FtIN STYLE%E none");
|
||||
}
|
||||
if(e->construction) {
|
||||
Printf(false, "%FtCONSTRUCTION");
|
||||
}
|
||||
} else if(gs.n == 2 && gs.points == 2) {
|
||||
Printf(false, "%FtTWO POINTS");
|
||||
Vector p0 = SK.GetEntity(gs.point[0])->PointGetNum();
|
||||
Printf(true, " at " PT_AS_STR, COSTR(p0));
|
||||
Vector p1 = SK.GetEntity(gs.point[1])->PointGetNum();
|
||||
Printf(false, " " PT_AS_STR, COSTR(p1));
|
||||
double d = (p1.Minus(p0)).Magnitude();
|
||||
Printf(true, " d = %Fi%s", SS.MmToString(d));
|
||||
} else if(gs.n == 2 && gs.faces == 1 && gs.points == 1) {
|
||||
Printf(false, "%FtA POINT AND A PLANE FACE");
|
||||
Vector pt = SK.GetEntity(gs.point[0])->PointGetNum();
|
||||
Printf(true, " point = " PT_AS_STR, COSTR(pt));
|
||||
Vector n = SK.GetEntity(gs.face[0])->FaceGetNormalNum();
|
||||
Printf(true, " plane normal = " PT_AS_NUM, CO(n));
|
||||
Vector pl = SK.GetEntity(gs.face[0])->FaceGetPointNum();
|
||||
Printf(false, " plane thru = " PT_AS_STR, COSTR(pl));
|
||||
double dd = n.Dot(pl) - n.Dot(pt);
|
||||
Printf(true, " distance = %Fi%s", SS.MmToString(dd));
|
||||
} else if(gs.n == 3 && gs.points == 2 && gs.vectors == 1) {
|
||||
Printf(false, "%FtTWO POINTS AND A VECTOR");
|
||||
Vector p0 = SK.GetEntity(gs.point[0])->PointGetNum();
|
||||
Printf(true, " pointA = " PT_AS_STR, COSTR(p0));
|
||||
Vector p1 = SK.GetEntity(gs.point[1])->PointGetNum();
|
||||
Printf(false, " pointB = " PT_AS_STR, COSTR(p1));
|
||||
Vector v = SK.GetEntity(gs.vector[0])->VectorGetNum();
|
||||
v = v.WithMagnitude(1);
|
||||
Printf(true, " vector = " PT_AS_NUM, CO(v));
|
||||
double d = (p1.Minus(p0)).Dot(v);
|
||||
Printf(true, " proj_d = %Fi%s", SS.MmToString(d));
|
||||
} else if(gs.n == 2 && gs.lineSegments == 1 && gs.points == 1) {
|
||||
Entity *ln = SK.GetEntity(gs.entity[0]);
|
||||
Vector lp0 = SK.GetEntity(ln->point[0])->PointGetNum(),
|
||||
lp1 = SK.GetEntity(ln->point[1])->PointGetNum();
|
||||
Printf(false, "%FtLINE SEGMENT AND POINT%E");
|
||||
Printf(true, " ln thru " PT_AS_STR, COSTR(lp0));
|
||||
Printf(false, " " PT_AS_STR, COSTR(lp1));
|
||||
Vector pp = SK.GetEntity(gs.point[0])->PointGetNum();
|
||||
Printf(true, " point " PT_AS_STR, COSTR(pp));
|
||||
Printf(true, " pt-ln distance = %Fi%s%E",
|
||||
SS.MmToString(pp.DistanceToLine(lp0, lp1.Minus(lp0))));
|
||||
} else if(gs.n == 2 && gs.vectors == 2) {
|
||||
Printf(false, "%FtTWO VECTORS");
|
||||
|
||||
Vector v0 = SK.GetEntity(gs.entity[0])->VectorGetNum(),
|
||||
v1 = SK.GetEntity(gs.entity[1])->VectorGetNum();
|
||||
v0 = v0.WithMagnitude(1);
|
||||
v1 = v1.WithMagnitude(1);
|
||||
|
||||
Printf(true, " vectorA = " PT_AS_NUM, CO(v0));
|
||||
Printf(false, " vectorB = " PT_AS_NUM, CO(v1));
|
||||
|
||||
double theta = acos(v0.Dot(v1));
|
||||
Printf(true, " angle = %Fi%2%E degrees", theta*180/PI);
|
||||
while(theta < PI/2) theta += PI;
|
||||
while(theta > PI/2) theta -= PI;
|
||||
Printf(false, " or angle = %Fi%2%E (mod 180)", theta*180/PI);
|
||||
} else if(gs.n == 2 && gs.faces == 2) {
|
||||
Printf(false, "%FtTWO PLANE FACES");
|
||||
|
||||
Vector n0 = SK.GetEntity(gs.face[0])->FaceGetNormalNum();
|
||||
Printf(true, " planeA normal = " PT_AS_NUM, CO(n0));
|
||||
Vector p0 = SK.GetEntity(gs.face[0])->FaceGetPointNum();
|
||||
Printf(false, " planeA thru = " PT_AS_STR, COSTR(p0));
|
||||
|
||||
Vector n1 = SK.GetEntity(gs.face[1])->FaceGetNormalNum();
|
||||
Printf(true, " planeB normal = " PT_AS_NUM, CO(n1));
|
||||
Vector p1 = SK.GetEntity(gs.face[1])->FaceGetPointNum();
|
||||
Printf(false, " planeB thru = " PT_AS_STR, COSTR(p1));
|
||||
|
||||
double theta = acos(n0.Dot(n1));
|
||||
Printf(true, " angle = %Fi%2%E degrees", theta*180/PI);
|
||||
while(theta < PI/2) theta += PI;
|
||||
while(theta > PI/2) theta -= PI;
|
||||
Printf(false, " or angle = %Fi%2%E (mod 180)", theta*180/PI);
|
||||
|
||||
if(fabs(theta) < 0.01) {
|
||||
double d = (p1.Minus(p0)).Dot(n0);
|
||||
Printf(true, " distance = %Fi%s", SS.MmToString(d));
|
||||
}
|
||||
} else if(gs.n == 0 && gs.stylables > 0) {
|
||||
Printf(false, "%FtSELECTED:%E comment text");
|
||||
} else if(gs.n == 0 && gs.constraints == 1) {
|
||||
Printf(false, "%FtSELECTED:%E %s",
|
||||
SK.GetConstraint(gs.constraint[0])->DescriptionString());
|
||||
} else {
|
||||
int n = SS.GW.selection.n;
|
||||
Printf(false, "%FtSELECTED:%E %d item%s", n, n == 1 ? "" : "s");
|
||||
}
|
||||
|
||||
if(shown.screen == SCREEN_STYLE_INFO &&
|
||||
shown.style.v >= Style::FIRST_CUSTOM && gs.stylables > 0)
|
||||
{
|
||||
// If we are showing a screen for a particular style, then offer the
|
||||
// option to assign our selected entities to that style.
|
||||
Style *s = Style::Get(shown.style);
|
||||
Printf(true, "%Fl%D%f%Ll(assign to style %s)%E",
|
||||
shown.style.v,
|
||||
&ScreenAssignSelectionToStyle,
|
||||
s->DescriptionString());
|
||||
}
|
||||
// If any of the selected entities have an assigned style, then offer
|
||||
// the option to remove that style.
|
||||
bool styleAssigned = false;
|
||||
for(i = 0; i < gs.entities; i++) {
|
||||
Entity *e = SK.GetEntity(gs.entity[i]);
|
||||
if(e->style.v != 0) {
|
||||
styleAssigned = true;
|
||||
}
|
||||
}
|
||||
for(i = 0; i < gs.constraints; i++) {
|
||||
Constraint *c = SK.GetConstraint(gs.constraint[i]);
|
||||
if(c->type == Constraint::COMMENT && c->disp.style.v != 0) {
|
||||
styleAssigned = true;
|
||||
}
|
||||
}
|
||||
if(styleAssigned) {
|
||||
Printf(true, "%Fl%D%f%Ll(remove assigned style)%E",
|
||||
0,
|
||||
&ScreenAssignSelectionToStyle);
|
||||
}
|
||||
|
||||
Printf(true, "%Fl%f%Ll(unselect all)%E", &TextWindow::ScreenUnselectAll);
|
||||
}
|
||||
|
||||
void TextWindow::GoToScreen(int screen) {
|
||||
shown.screen = screen;
|
||||
}
|
||||
|
820
draw.cpp
|
@ -1,820 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// The root function to paint our graphics window, after setting up all the
|
||||
// views and such appropriately. Also contains all the stuff to manage the
|
||||
// selection.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
bool GraphicsWindow::Selection::Equals(Selection *b) {
|
||||
if(entity.v != b->entity.v) return false;
|
||||
if(constraint.v != b->constraint.v) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphicsWindow::Selection::IsEmpty(void) {
|
||||
if(entity.v) return false;
|
||||
if(constraint.v) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphicsWindow::Selection::IsStylable(void) {
|
||||
if(entity.v) return true;
|
||||
if(constraint.v) {
|
||||
Constraint *c = SK.GetConstraint(constraint);
|
||||
if(c->type == Constraint::COMMENT) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GraphicsWindow::Selection::HasEndpoints(void) {
|
||||
if(!entity.v) return false;
|
||||
Entity *e = SK.GetEntity(entity);
|
||||
return e->HasEndpoints();
|
||||
}
|
||||
|
||||
void GraphicsWindow::Selection::Clear(void) {
|
||||
entity.v = constraint.v = 0;
|
||||
emphasized = false;
|
||||
}
|
||||
|
||||
void GraphicsWindow::Selection::Draw(void) {
|
||||
Vector refp;
|
||||
if(entity.v) {
|
||||
Entity *e = SK.GetEntity(entity);
|
||||
e->Draw();
|
||||
if(emphasized) refp = e->GetReferencePos();
|
||||
}
|
||||
if(constraint.v) {
|
||||
Constraint *c = SK.GetConstraint(constraint);
|
||||
c->Draw();
|
||||
if(emphasized) refp = c->GetReferencePos();
|
||||
}
|
||||
if(emphasized && (constraint.v || entity.v)) {
|
||||
// We want to emphasize this constraint or entity, by drawing a thick
|
||||
// line from the top left corner of the screen to the reference point
|
||||
// of that entity or constraint.
|
||||
double s = 0.501/SS.GW.scale;
|
||||
Vector topLeft = SS.GW.projRight.ScaledBy(-SS.GW.width*s);
|
||||
topLeft = topLeft.Plus(SS.GW.projUp.ScaledBy(SS.GW.height*s));
|
||||
topLeft = topLeft.Minus(SS.GW.offset);
|
||||
|
||||
glLineWidth(40);
|
||||
DWORD rgb = Style::Color(Style::HOVERED);
|
||||
glColor4d(REDf(rgb), GREENf(rgb), BLUEf(rgb), 0.2);
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(topLeft);
|
||||
glxVertex3v(refp);
|
||||
glEnd();
|
||||
glLineWidth(1);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::ClearSelection(void) {
|
||||
selection.Clear();
|
||||
SS.later.showTW = true;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
void GraphicsWindow::ClearNonexistentSelectionItems(void) {
|
||||
bool change = false;
|
||||
Selection *s;
|
||||
selection.ClearTags();
|
||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
if(s->constraint.v && !(SK.constraint.FindByIdNoOops(s->constraint))) {
|
||||
s->tag = 1;
|
||||
change = true;
|
||||
}
|
||||
if(s->entity.v && !(SK.entity.FindByIdNoOops(s->entity))) {
|
||||
s->tag = 1;
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
selection.RemoveTagged();
|
||||
if(change) InvalidateGraphics();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Is this entity/constraint selected?
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GraphicsWindow::IsSelected(hEntity he) {
|
||||
Selection s;
|
||||
ZERO(&s);
|
||||
s.entity = he;
|
||||
return IsSelected(&s);
|
||||
}
|
||||
bool GraphicsWindow::IsSelected(Selection *st) {
|
||||
Selection *s;
|
||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
if(s->Equals(st)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Unselect an item, if it is selected. We can either unselect just that item,
|
||||
// or also unselect any coincident points. The latter is useful if the user
|
||||
// somehow selects two coincident points (like with select all), because it
|
||||
// would otherwise be impossible to de-select the lower of the two.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::MakeUnselected(hEntity he, bool coincidentPointTrick) {
|
||||
Selection stog;
|
||||
ZERO(&stog);
|
||||
stog.entity = he;
|
||||
MakeUnselected(&stog, coincidentPointTrick);
|
||||
}
|
||||
void GraphicsWindow::MakeUnselected(Selection *stog, bool coincidentPointTrick){
|
||||
if(stog->IsEmpty()) return;
|
||||
|
||||
Selection *s;
|
||||
|
||||
// If an item was selected, then we just un-select it.
|
||||
bool wasSelected = false;
|
||||
selection.ClearTags();
|
||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
if(s->Equals(stog)) {
|
||||
s->tag = 1;
|
||||
}
|
||||
}
|
||||
// If two points are coincident, then it's impossible to hover one of
|
||||
// them. But make sure to deselect both, to avoid mysterious seeming
|
||||
// inability to deselect if the bottom one did somehow get selected.
|
||||
if(stog->entity.v && coincidentPointTrick) {
|
||||
Entity *e = SK.GetEntity(stog->entity);
|
||||
if(e->IsPoint()) {
|
||||
Vector ep = e->PointGetNum();
|
||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
if(!s->entity.v) continue;
|
||||
if(s->entity.v == stog->entity.v) continue;
|
||||
Entity *se = SK.GetEntity(s->entity);
|
||||
if(!se->IsPoint()) continue;
|
||||
if(ep.Equals(se->PointGetNum())) {
|
||||
s->tag = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
selection.RemoveTagged();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Select an item, if it isn't selected already.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::MakeSelected(hEntity he) {
|
||||
Selection stog;
|
||||
ZERO(&stog);
|
||||
stog.entity = he;
|
||||
MakeSelected(&stog);
|
||||
}
|
||||
void GraphicsWindow::MakeSelected(Selection *stog) {
|
||||
if(stog->IsEmpty()) return;
|
||||
if(IsSelected(stog)) return;
|
||||
|
||||
if(stog->entity.v != 0 && SK.GetEntity(stog->entity)->IsFace()) {
|
||||
// In the interest of speed for the triangle drawing code,
|
||||
// only two faces may be selected at a time.
|
||||
int c = 0;
|
||||
Selection *s;
|
||||
selection.ClearTags();
|
||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
hEntity he = s->entity;
|
||||
if(he.v != 0 && SK.GetEntity(he)->IsFace()) {
|
||||
c++;
|
||||
if(c >= 2) s->tag = 1;
|
||||
}
|
||||
}
|
||||
selection.RemoveTagged();
|
||||
}
|
||||
|
||||
selection.Add(stog);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Select everything that lies within the marquee view-aligned rectangle. For
|
||||
// points, we test by the point location. For normals, we test by the normal's
|
||||
// associated point. For anything else, we test by any piecewise linear edge.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::SelectByMarquee(void) {
|
||||
Point2d begin = ProjectPoint(orig.marqueePoint);
|
||||
double xmin = min(orig.mouse.x, begin.x),
|
||||
xmax = max(orig.mouse.x, begin.x),
|
||||
ymin = min(orig.mouse.y, begin.y),
|
||||
ymax = max(orig.mouse.y, begin.y);
|
||||
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group.v != SS.GW.activeGroup.v) continue;
|
||||
if(e->IsFace() || e->IsDistance()) continue;
|
||||
if(!e->IsVisible()) continue;
|
||||
|
||||
if(e->IsPoint() || e->IsNormal()) {
|
||||
Vector p = e->IsPoint() ? e->PointGetNum() :
|
||||
SK.GetEntity(e->point[0])->PointGetNum();
|
||||
Point2d pp = ProjectPoint(p);
|
||||
if(pp.x >= xmin && pp.x <= xmax &&
|
||||
pp.y >= ymin && pp.y <= ymax)
|
||||
{
|
||||
MakeSelected(e->h);
|
||||
}
|
||||
} else {
|
||||
// Use the 3d bounding box test routines, to avoid duplication;
|
||||
// so let our bounding square become a bounding box that certainly
|
||||
// includes the z = 0 plane.
|
||||
Vector ptMin = Vector::From(xmin, ymin, -1),
|
||||
ptMax = Vector::From(xmax, ymax, 1);
|
||||
SEdgeList sel;
|
||||
ZERO(&sel);
|
||||
e->GenerateEdges(&sel, true);
|
||||
SEdge *se;
|
||||
for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) {
|
||||
Point2d ppa = ProjectPoint(se->a),
|
||||
ppb = ProjectPoint(se->b);
|
||||
Vector ptA = Vector::From(ppa.x, ppa.y, 0),
|
||||
ptB = Vector::From(ppb.x, ppb.y, 0);
|
||||
if(Vector::BoundingBoxIntersectsLine(ptMax, ptMin,
|
||||
ptA, ptB, true) ||
|
||||
!ptA.OutsideAndNotOn(ptMax, ptMin) ||
|
||||
!ptB.OutsideAndNotOn(ptMax, ptMin))
|
||||
{
|
||||
MakeSelected(e->h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sel.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sort the selection according to various critieria: the entities and
|
||||
// constraints separately, counts of certain types of entities (circles,
|
||||
// lines, etc.), and so on.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::GroupSelection(void) {
|
||||
memset(&gs, 0, sizeof(gs));
|
||||
int i;
|
||||
for(i = 0; i < selection.n && i < MAX_SELECTED; i++) {
|
||||
Selection *s = &(selection.elem[i]);
|
||||
if(s->entity.v) {
|
||||
(gs.n)++;
|
||||
|
||||
Entity *e = SK.entity.FindById(s->entity);
|
||||
// A list of points, and a list of all entities that aren't points.
|
||||
if(e->IsPoint()) {
|
||||
gs.point[(gs.points)++] = s->entity;
|
||||
} else {
|
||||
gs.entity[(gs.entities)++] = s->entity;
|
||||
(gs.stylables)++;
|
||||
}
|
||||
|
||||
// And an auxiliary list of normals, including normals from
|
||||
// workplanes.
|
||||
if(e->IsNormal()) {
|
||||
gs.anyNormal[(gs.anyNormals)++] = s->entity;
|
||||
} else if(e->IsWorkplane()) {
|
||||
gs.anyNormal[(gs.anyNormals)++] = e->Normal()->h;
|
||||
}
|
||||
|
||||
// And of vectors (i.e., stuff with a direction to constrain)
|
||||
if(e->HasVector()) {
|
||||
gs.vector[(gs.vectors)++] = s->entity;
|
||||
}
|
||||
|
||||
// Faces (which are special, associated/drawn with triangles)
|
||||
if(e->IsFace()) {
|
||||
gs.face[(gs.faces)++] = s->entity;
|
||||
}
|
||||
|
||||
if(e->HasEndpoints()) {
|
||||
(gs.withEndpoints)++;
|
||||
}
|
||||
|
||||
// And some aux counts too
|
||||
switch(e->type) {
|
||||
case Entity::WORKPLANE: (gs.workplanes)++; break;
|
||||
case Entity::LINE_SEGMENT: (gs.lineSegments)++; break;
|
||||
case Entity::CUBIC: (gs.cubics)++; break;
|
||||
case Entity::CUBIC_PERIODIC: (gs.periodicCubics)++; break;
|
||||
|
||||
case Entity::ARC_OF_CIRCLE:
|
||||
(gs.circlesOrArcs)++;
|
||||
(gs.arcs)++;
|
||||
break;
|
||||
|
||||
case Entity::CIRCLE: (gs.circlesOrArcs)++; break;
|
||||
}
|
||||
}
|
||||
if(s->constraint.v) {
|
||||
gs.constraint[(gs.constraints)++] = s->constraint;
|
||||
Constraint *c = SK.GetConstraint(s->constraint);
|
||||
if(c->type == Constraint::COMMENT) {
|
||||
(gs.stylables)++;
|
||||
(gs.comments)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
|
||||
int i;
|
||||
double d, dmin = 1e12;
|
||||
Selection s;
|
||||
ZERO(&s);
|
||||
|
||||
// Always do the entities; we might be dragging something that should
|
||||
// be auto-constrained, and we need the hover for that.
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
// Don't hover whatever's being dragged.
|
||||
if(e->h.request().v == pending.point.request().v) {
|
||||
// The one exception is when we're creating a new cubic; we
|
||||
// want to be able to hover the first point, because that's
|
||||
// how we turn it into a periodic spline.
|
||||
if(!e->IsPoint()) continue;
|
||||
if(!e->h.isFromRequest()) continue;
|
||||
Request *r = SK.GetRequest(e->h.request());
|
||||
if(r->type != Request::CUBIC) continue;
|
||||
if(r->extraPoints < 2) continue;
|
||||
if(e->h.v != r->h.entity(1).v) continue;
|
||||
}
|
||||
|
||||
d = e->GetDistance(mp);
|
||||
if(d < 10 && d < dmin) {
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.entity = e->h;
|
||||
dmin = d;
|
||||
}
|
||||
}
|
||||
|
||||
// The constraints and faces happen only when nothing's in progress.
|
||||
if(pending.operation == 0) {
|
||||
// Constraints
|
||||
for(i = 0; i < SK.constraint.n; i++) {
|
||||
d = SK.constraint.elem[i].GetDistance(mp);
|
||||
if(d < 10 && d < dmin) {
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.constraint = SK.constraint.elem[i].h;
|
||||
dmin = d;
|
||||
}
|
||||
}
|
||||
|
||||
// Faces, from the triangle mesh; these are lowest priority
|
||||
if(s.constraint.v == 0 && s.entity.v == 0 && showShaded && showFaces) {
|
||||
Group *g = SK.GetGroup(activeGroup);
|
||||
SMesh *m = &(g->displayMesh);
|
||||
|
||||
DWORD v = m->FirstIntersectionWith(mp);
|
||||
if(v) {
|
||||
s.entity.v = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!s.Equals(&hover)) {
|
||||
hover = s;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project a point in model space to screen space, exactly as gl would; return
|
||||
// units are pixels.
|
||||
//-----------------------------------------------------------------------------
|
||||
Point2d GraphicsWindow::ProjectPoint(Vector p) {
|
||||
Vector p3 = ProjectPoint3(p);
|
||||
Point2d p2 = { p3.x, p3.y };
|
||||
return p2;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project a point in model space to screen space, exactly as gl would; return
|
||||
// units are pixels. The z coordinate is also returned, also in pixels.
|
||||
//-----------------------------------------------------------------------------
|
||||
Vector GraphicsWindow::ProjectPoint3(Vector p) {
|
||||
double w;
|
||||
Vector r = ProjectPoint4(p, &w);
|
||||
return r.ScaledBy(scale/w);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project a point in model space halfway into screen space. The scale is
|
||||
// not applied, and the perspective divide isn't applied; instead the w
|
||||
// coordinate is returned separately.
|
||||
//-----------------------------------------------------------------------------
|
||||
Vector GraphicsWindow::ProjectPoint4(Vector p, double *w) {
|
||||
p = p.Plus(offset);
|
||||
|
||||
Vector r;
|
||||
r.x = p.Dot(projRight);
|
||||
r.y = p.Dot(projUp);
|
||||
r.z = p.Dot(projUp.Cross(projRight));
|
||||
|
||||
*w = 1 + r.z*SS.CameraTangent()*scale;
|
||||
return r;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Return a point in the plane parallel to the screen and through the offset,
|
||||
// that projects onto the specified (x, y) coordinates.
|
||||
//-----------------------------------------------------------------------------
|
||||
Vector GraphicsWindow::UnProjectPoint(Point2d p) {
|
||||
Vector orig = offset.ScaledBy(-1);
|
||||
|
||||
// Note that we ignoring the effects of perspective. Since our returned
|
||||
// point has the same component normal to the screen as the offset, it
|
||||
// will have z = 0 after the rotation is applied, thus w = 1. So this is
|
||||
// correct.
|
||||
orig = orig.Plus(projRight.ScaledBy(p.x / scale)).Plus(
|
||||
projUp. ScaledBy(p.y / scale));
|
||||
return orig;
|
||||
}
|
||||
|
||||
void GraphicsWindow::NormalizeProjectionVectors(void) {
|
||||
if(projRight.Magnitude() < LENGTH_EPS) {
|
||||
projRight = Vector::From(1, 0, 0);
|
||||
}
|
||||
|
||||
Vector norm = projRight.Cross(projUp);
|
||||
// If projRight and projUp somehow ended up parallel, then pick an
|
||||
// arbitrary projUp normal to projRight.
|
||||
if(norm.Magnitude() < LENGTH_EPS) {
|
||||
norm = projRight.Normal(0);
|
||||
}
|
||||
projUp = norm.Cross(projRight);
|
||||
|
||||
projUp = projUp.WithMagnitude(1);
|
||||
projRight = projRight.WithMagnitude(1);
|
||||
}
|
||||
|
||||
Vector GraphicsWindow::VectorFromProjs(Vector rightUpForward) {
|
||||
Vector n = projRight.Cross(projUp);
|
||||
|
||||
Vector r = (projRight.ScaledBy(rightUpForward.x));
|
||||
r = r.Plus(projUp.ScaledBy(rightUpForward.y));
|
||||
r = r.Plus(n.ScaledBy(rightUpForward.z));
|
||||
return r;
|
||||
}
|
||||
|
||||
void GraphicsWindow::Paint(void) {
|
||||
int i;
|
||||
havePainted = true;
|
||||
|
||||
int w, h;
|
||||
GetGraphicsWindowSize(&w, &h);
|
||||
width = w; height = h;
|
||||
glViewport(0, 0, w, h);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
glScaled(scale*2.0/w, scale*2.0/h, scale*1.0/30000);
|
||||
|
||||
double mat[16];
|
||||
// Last thing before display is to apply the perspective
|
||||
double clp = SS.CameraTangent()*scale;
|
||||
MakeMatrix(mat, 1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, clp, 1);
|
||||
glMultMatrixd(mat);
|
||||
// Before that, we apply the rotation
|
||||
Vector n = projUp.Cross(projRight);
|
||||
MakeMatrix(mat, projRight.x, projRight.y, projRight.z, 0,
|
||||
projUp.x, projUp.y, projUp.z, 0,
|
||||
n.x, n.y, n.z, 0,
|
||||
0, 0, 0, 1);
|
||||
glMultMatrixd(mat);
|
||||
// And before that, the translation
|
||||
MakeMatrix(mat, 1, 0, 0, offset.x,
|
||||
0, 1, 0, offset.y,
|
||||
0, 0, 1, offset.z,
|
||||
0, 0, 0, 1);
|
||||
glMultMatrixd(mat);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glShadeModel(GL_SMOOTH);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
// don't enable GL_POLYGON_SMOOTH; that looks ugly on some graphics cards,
|
||||
// drawn with leaks in the mesh
|
||||
glEnable(GL_POLYGON_OFFSET_LINE);
|
||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
|
||||
glEnable(GL_NORMALIZE);
|
||||
|
||||
// At the same depth, we want later lines drawn over earlier.
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
if(SS.AllGroupsOkay()) {
|
||||
glClearColor(REDf(SS.backgroundColor),
|
||||
GREENf(SS.backgroundColor),
|
||||
BLUEf(SS.backgroundColor), 1.0f);
|
||||
} else {
|
||||
// Draw a different background whenever we're having solve problems.
|
||||
DWORD rgb = Style::Color(Style::DRAW_ERROR);
|
||||
glClearColor(0.4f*REDf(rgb), 0.4f*GREENf(rgb), 0.4f*BLUEf(rgb), 1.0f);
|
||||
// And show the text window, which has info to debug it
|
||||
ForceTextWindowShown();
|
||||
}
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClearDepth(1.0);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
if(SS.bgImage.fromFile) {
|
||||
// If a background image is loaded, then we draw it now as a texture.
|
||||
// This handles the resizing for us nicely.
|
||||
glBindTexture(GL_TEXTURE_2D, TEXTURE_BACKGROUND_IMG);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
|
||||
SS.bgImage.rw, SS.bgImage.rh,
|
||||
0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE,
|
||||
SS.bgImage.fromFile);
|
||||
|
||||
double tw = ((double)SS.bgImage.w) / SS.bgImage.rw,
|
||||
th = ((double)SS.bgImage.h) / SS.bgImage.rh;
|
||||
|
||||
double mmw = SS.bgImage.w / SS.bgImage.scale,
|
||||
mmh = SS.bgImage.h / SS.bgImage.scale;
|
||||
|
||||
Vector origin = SS.bgImage.origin;
|
||||
origin = origin.DotInToCsys(projRight, projUp, n);
|
||||
// Place the depth of our origin at the point that corresponds to
|
||||
// w = 1, so that it's unaffected by perspective.
|
||||
origin.z = (offset.ScaledBy(-1)).Dot(n);
|
||||
origin = origin.ScaleOutOfCsys(projRight, projUp, n);
|
||||
|
||||
// Place the background at the very back of the Z order, though, by
|
||||
// mucking with the depth range.
|
||||
glDepthRange(1, 1);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2d(0, 0);
|
||||
glxVertex3v(origin);
|
||||
|
||||
glTexCoord2d(0, th);
|
||||
glxVertex3v(origin.Plus(projUp.ScaledBy(mmh)));
|
||||
|
||||
glTexCoord2d(tw, th);
|
||||
glxVertex3v(origin.Plus(projRight.ScaledBy(mmw).Plus(
|
||||
projUp. ScaledBy(mmh))));
|
||||
|
||||
glTexCoord2d(tw, 0);
|
||||
glxVertex3v(origin.Plus(projRight.ScaledBy(mmw)));
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
glxDepthRangeOffset(0);
|
||||
|
||||
// Nasty case when we're reloading the imported files; could be that
|
||||
// we get an error, so a dialog pops up, and a message loop starts, and
|
||||
// we have to get called to paint ourselves. If the sketch is screwed
|
||||
// up, then we could trigger an oops trying to draw.
|
||||
if(!SS.allConsistent) return;
|
||||
|
||||
// Let's use two lights, at the user-specified locations
|
||||
GLfloat f;
|
||||
glEnable(GL_LIGHT0);
|
||||
f = (GLfloat)SS.lightIntensity[0];
|
||||
GLfloat li0[] = { f, f, f, 1.0f };
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, li0);
|
||||
glLightfv(GL_LIGHT0, GL_SPECULAR, li0);
|
||||
|
||||
glEnable(GL_LIGHT1);
|
||||
f = (GLfloat)SS.lightIntensity[1];
|
||||
GLfloat li1[] = { f, f, f, 1.0f };
|
||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, li1);
|
||||
glLightfv(GL_LIGHT1, GL_SPECULAR, li1);
|
||||
|
||||
Vector ld;
|
||||
ld = VectorFromProjs(SS.lightDir[0]);
|
||||
GLfloat ld0[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, ld0);
|
||||
ld = VectorFromProjs(SS.lightDir[1]);
|
||||
GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
|
||||
glLightfv(GL_LIGHT1, GL_POSITION, ld1);
|
||||
|
||||
if(SS.drawBackFaces) {
|
||||
// For debugging, draw the backs of the triangles in red, so that we
|
||||
// notice when a shell is open
|
||||
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1);
|
||||
} else {
|
||||
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0);
|
||||
}
|
||||
|
||||
GLfloat ambient[4] = { (float)SS.ambientIntensity,
|
||||
(float)SS.ambientIntensity,
|
||||
(float)SS.ambientIntensity, 1 };
|
||||
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
|
||||
|
||||
glxUnlockColor();
|
||||
|
||||
if(showSnapGrid && LockedInWorkplane()) {
|
||||
hEntity he = ActiveWorkplane();
|
||||
EntityBase *wrkpl = SK.GetEntity(he),
|
||||
*norm = wrkpl->Normal();
|
||||
Vector wu, wv, wn, wp;
|
||||
wp = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
||||
wu = norm->NormalU();
|
||||
wv = norm->NormalV();
|
||||
wn = norm->NormalN();
|
||||
|
||||
double g = SS.gridSpacing;
|
||||
|
||||
double umin = VERY_POSITIVE, umax = VERY_NEGATIVE,
|
||||
vmin = VERY_POSITIVE, vmax = VERY_NEGATIVE;
|
||||
int a;
|
||||
for(a = 0; a < 4; a++) {
|
||||
// Ideally, we would just do +/- half the width and height; but
|
||||
// allow some extra slop for rounding.
|
||||
Vector horiz = projRight.ScaledBy((0.6*width)/scale + 2*g),
|
||||
vert = projUp. ScaledBy((0.6*height)/scale + 2*g);
|
||||
if(a == 2 || a == 3) horiz = horiz.ScaledBy(-1);
|
||||
if(a == 1 || a == 3) vert = vert. ScaledBy(-1);
|
||||
Vector tp = horiz.Plus(vert).Minus(offset);
|
||||
|
||||
// Project the point into our grid plane, normal to the screen
|
||||
// (not to the grid plane). If the plane is on edge then this is
|
||||
// impossible so don't try to draw the grid.
|
||||
bool parallel;
|
||||
Vector tpp = Vector::AtIntersectionOfPlaneAndLine(
|
||||
wn, wn.Dot(wp),
|
||||
tp, tp.Plus(n),
|
||||
¶llel);
|
||||
if(parallel) goto nogrid;
|
||||
|
||||
tpp = tpp.Minus(wp);
|
||||
double uu = tpp.Dot(wu),
|
||||
vv = tpp.Dot(wv);
|
||||
|
||||
umin = min(uu, umin);
|
||||
umax = max(uu, umax);
|
||||
vmin = min(vv, vmin);
|
||||
vmax = max(vv, vmax);
|
||||
}
|
||||
|
||||
int i, j, i0, i1, j0, j1;
|
||||
|
||||
i0 = (int)(umin / g);
|
||||
i1 = (int)(umax / g);
|
||||
j0 = (int)(vmin / g);
|
||||
j1 = (int)(vmax / g);
|
||||
|
||||
if(i0 > i1 || i1 - i0 > 400) goto nogrid;
|
||||
if(j0 > j1 || j1 - j0 > 400) goto nogrid;
|
||||
|
||||
glLineWidth(1);
|
||||
glxColorRGBa(Style::Color(Style::DATUM), 0.3);
|
||||
glBegin(GL_LINES);
|
||||
for(i = i0 + 1; i < i1; i++) {
|
||||
glxVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j0*g)));
|
||||
glxVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j1*g)));
|
||||
}
|
||||
for(j = j0 + 1; j < j1; j++) {
|
||||
glxVertex3v(wp.Plus(wu.ScaledBy(i0*g)).Plus(wv.ScaledBy(j*g)));
|
||||
glxVertex3v(wp.Plus(wu.ScaledBy(i1*g)).Plus(wv.ScaledBy(j*g)));
|
||||
}
|
||||
glEnd();
|
||||
|
||||
// Clear the depth buffer, so that the grid is at the very back of
|
||||
// the Z order.
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
nogrid:;
|
||||
}
|
||||
|
||||
// Draw the active group; this does stuff like the mesh and edges.
|
||||
(SK.GetGroup(activeGroup))->Draw();
|
||||
|
||||
// Now draw the entities
|
||||
if(showHdnLines) glDisable(GL_DEPTH_TEST);
|
||||
Entity::DrawAll();
|
||||
|
||||
// Draw filled paths in all groups, when those filled paths were requested
|
||||
// specially by assigning a style with a fill color, or when the filled
|
||||
// paths are just being filled by default. This should go last, to make
|
||||
// the transparency work.
|
||||
Group *g;
|
||||
for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) {
|
||||
if(!(g->IsVisible())) continue;
|
||||
g->DrawFilledPaths();
|
||||
}
|
||||
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
// Draw the constraints
|
||||
for(i = 0; i < SK.constraint.n; i++) {
|
||||
SK.constraint.elem[i].Draw();
|
||||
}
|
||||
|
||||
// Draw the traced path, if one exists
|
||||
glLineWidth(Style::Width(Style::ANALYZE));
|
||||
glxColorRGB(Style::Color(Style::ANALYZE));
|
||||
SContour *sc = &(SS.traced.path);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for(i = 0; i < sc->l.n; i++) {
|
||||
glxVertex3v(sc->l.elem[i].p);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
// And the naked edges, if the user did Analyze -> Show Naked Edges.
|
||||
glLineWidth(Style::Width(Style::DRAW_ERROR));
|
||||
glxColorRGB(Style::Color(Style::DRAW_ERROR));
|
||||
glxDrawEdges(&(SS.nakedEdges), true);
|
||||
|
||||
// Then redraw whatever the mouse is hovering over, highlighted.
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glxLockColorTo(Style::Color(Style::HOVERED));
|
||||
hover.Draw();
|
||||
|
||||
// And finally draw the selection, same mechanism.
|
||||
glxLockColorTo(Style::Color(Style::SELECTED));
|
||||
for(Selection *s = selection.First(); s; s = selection.NextAfter(s)) {
|
||||
s->Draw();
|
||||
}
|
||||
|
||||
glxUnlockColor();
|
||||
|
||||
// If a marquee selection is in progress, then draw the selection
|
||||
// rectangle, as an outline and a transparent fill.
|
||||
if(pending.operation == DRAGGING_MARQUEE) {
|
||||
Point2d begin = ProjectPoint(orig.marqueePoint);
|
||||
double xmin = min(orig.mouse.x, begin.x),
|
||||
xmax = max(orig.mouse.x, begin.x),
|
||||
ymin = min(orig.mouse.y, begin.y),
|
||||
ymax = max(orig.mouse.y, begin.y);
|
||||
|
||||
Vector tl = UnProjectPoint(Point2d::From(xmin, ymin)),
|
||||
tr = UnProjectPoint(Point2d::From(xmax, ymin)),
|
||||
br = UnProjectPoint(Point2d::From(xmax, ymax)),
|
||||
bl = UnProjectPoint(Point2d::From(xmin, ymax));
|
||||
|
||||
glLineWidth((GLfloat)1.3);
|
||||
glxColorRGB(Style::Color(Style::HOVERED));
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glxVertex3v(tl);
|
||||
glxVertex3v(tr);
|
||||
glxVertex3v(br);
|
||||
glxVertex3v(bl);
|
||||
glEnd();
|
||||
glxColorRGBa(Style::Color(Style::HOVERED), 0.10);
|
||||
glBegin(GL_QUADS);
|
||||
glxVertex3v(tl);
|
||||
glxVertex3v(tr);
|
||||
glxVertex3v(br);
|
||||
glxVertex3v(bl);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// An extra line, used to indicate the origin when rotating within the
|
||||
// plane of the monitor.
|
||||
if(SS.extraLine.draw) {
|
||||
glLineWidth(1);
|
||||
glxLockColorTo(Style::Color(Style::DATUM));
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(SS.extraLine.ptA);
|
||||
glxVertex3v(SS.extraLine.ptB);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// A note to indicate the origin in the just-exported file.
|
||||
if(SS.justExportedInfo.draw) {
|
||||
glxColorRGB(Style::Color(Style::DATUM));
|
||||
Vector p = SS.justExportedInfo.pt,
|
||||
u = SS.justExportedInfo.u,
|
||||
v = SS.justExportedInfo.v;
|
||||
|
||||
glLineWidth(1.5);
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(p.Plus(u.WithMagnitude(-15/scale)));
|
||||
glxVertex3v(p.Plus(u.WithMagnitude(30/scale)));
|
||||
glxVertex3v(p.Plus(v.WithMagnitude(-15/scale)));
|
||||
glxVertex3v(p.Plus(v.WithMagnitude(30/scale)));
|
||||
glEnd();
|
||||
|
||||
glxWriteText("(x, y) = (0, 0) for file just exported",
|
||||
DEFAULT_TEXT_HEIGHT,
|
||||
p.Plus(u.ScaledBy(10/scale)).Plus(v.ScaledBy(10/scale)),
|
||||
u, v, NULL, NULL);
|
||||
glxWriteText("press Esc to clear this message",
|
||||
DEFAULT_TEXT_HEIGHT,
|
||||
p.Plus(u.ScaledBy(40/scale)).Plus(
|
||||
v.ScaledBy(-(DEFAULT_TEXT_HEIGHT)/scale)),
|
||||
u, v, NULL, NULL);
|
||||
}
|
||||
|
||||
// And finally the toolbar.
|
||||
if(SS.showToolbar) {
|
||||
ToolbarDraw();
|
||||
}
|
||||
}
|
||||
|
1101
drawconstraint.cpp
648
drawentity.cpp
|
@ -1,648 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Draw a representation of an entity on-screen, in the case of curves up
|
||||
// to our chord tolerance, or return the distance from the user's mouse pointer
|
||||
// to the entity for selection.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
char *Entity::DescriptionString(void) {
|
||||
if(h.isFromRequest()) {
|
||||
Request *r = SK.GetRequest(h.request());
|
||||
return r->DescriptionString();
|
||||
} else {
|
||||
Group *g = SK.GetGroup(h.group());
|
||||
return g->DescriptionString();
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat) {
|
||||
if(dogd.drawing) {
|
||||
// Draw lines from active group in front of those from previous
|
||||
glxDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3);
|
||||
// Narrow lines are drawn as lines, but fat lines must be drawn as
|
||||
// filled polygons, to get the line join style right.
|
||||
if(!maybeFat || dogd.lineWidth < 3) {
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(a);
|
||||
glxVertex3v(b);
|
||||
glEnd();
|
||||
} else {
|
||||
glxFatLine(a, b, dogd.lineWidth/SS.GW.scale);
|
||||
}
|
||||
|
||||
glxDepthRangeOffset(0);
|
||||
} else {
|
||||
Point2d ap = SS.GW.ProjectPoint(a);
|
||||
Point2d bp = SS.GW.ProjectPoint(b);
|
||||
|
||||
double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true);
|
||||
// A little bit easier to select in the active group
|
||||
if(group.v == SS.GW.activeGroup.v) d -= 1;
|
||||
dogd.dmin = min(dogd.dmin, d);
|
||||
}
|
||||
dogd.refp = (a.Plus(b)).ScaledBy(0.5);
|
||||
}
|
||||
|
||||
void Entity::DrawAll(void) {
|
||||
// This handles points and line segments as a special case, because I
|
||||
// seem to be able to get a huge speedup that way, by consolidating
|
||||
// stuff to gl.
|
||||
int i;
|
||||
if(SS.GW.showPoints) {
|
||||
double s = 3.5/SS.GW.scale;
|
||||
Vector r = SS.GW.projRight.ScaledBy(s);
|
||||
Vector d = SS.GW.projUp.ScaledBy(s);
|
||||
glxColorRGB(Style::Color(Style::DATUM));
|
||||
glxDepthRangeOffset(6);
|
||||
glBegin(GL_QUADS);
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
if(!e->IsPoint()) continue;
|
||||
if(!(SK.GetGroup(e->group)->IsVisible())) continue;
|
||||
if(e->forceHidden) continue;
|
||||
|
||||
Vector v = e->PointGetNum();
|
||||
|
||||
// If we're analyzing the sketch to show the degrees of freedom,
|
||||
// then we draw big colored squares over the points that are
|
||||
// free to move.
|
||||
bool free = false;
|
||||
if(e->type == POINT_IN_3D) {
|
||||
Param *px = SK.GetParam(e->param[0]),
|
||||
*py = SK.GetParam(e->param[1]),
|
||||
*pz = SK.GetParam(e->param[2]);
|
||||
|
||||
free = (px->free) || (py->free) || (pz->free);
|
||||
} else if(e->type == POINT_IN_2D) {
|
||||
Param *pu = SK.GetParam(e->param[0]),
|
||||
*pv = SK.GetParam(e->param[1]);
|
||||
|
||||
free = (pu->free) || (pv->free);
|
||||
}
|
||||
if(free) {
|
||||
Vector re = r.ScaledBy(2.5), de = d.ScaledBy(2.5);
|
||||
|
||||
glxColorRGB(Style::Color(Style::ANALYZE));
|
||||
glxVertex3v(v.Plus (re).Plus (de));
|
||||
glxVertex3v(v.Plus (re).Minus(de));
|
||||
glxVertex3v(v.Minus(re).Minus(de));
|
||||
glxVertex3v(v.Minus(re).Plus (de));
|
||||
glxColorRGB(Style::Color(Style::DATUM));
|
||||
}
|
||||
|
||||
glxVertex3v(v.Plus (r).Plus (d));
|
||||
glxVertex3v(v.Plus (r).Minus(d));
|
||||
glxVertex3v(v.Minus(r).Minus(d));
|
||||
glxVertex3v(v.Minus(r).Plus (d));
|
||||
}
|
||||
glEnd();
|
||||
glxDepthRangeOffset(0);
|
||||
}
|
||||
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
if(e->IsPoint())
|
||||
{
|
||||
continue; // already handled
|
||||
}
|
||||
e->Draw();
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::Draw(void) {
|
||||
hStyle hs = Style::ForEntity(h);
|
||||
dogd.lineWidth = Style::Width(hs);
|
||||
glLineWidth((float)dogd.lineWidth);
|
||||
glxColorRGB(Style::Color(hs));
|
||||
|
||||
dogd.drawing = true;
|
||||
DrawOrGetDistance();
|
||||
}
|
||||
|
||||
void Entity::GenerateEdges(SEdgeList *el, bool includingConstruction) {
|
||||
if(construction && !includingConstruction) return;
|
||||
|
||||
SBezierList sbl;
|
||||
ZERO(&sbl);
|
||||
GenerateBezierCurves(&sbl);
|
||||
|
||||
int i, j;
|
||||
for(i = 0; i < sbl.l.n; i++) {
|
||||
SBezier *sb = &(sbl.l.elem[i]);
|
||||
|
||||
List<Vector> lv;
|
||||
ZERO(&lv);
|
||||
sb->MakePwlInto(&lv);
|
||||
for(j = 1; j < lv.n; j++) {
|
||||
el->AddEdge(lv.elem[j-1], lv.elem[j], style.v);
|
||||
}
|
||||
lv.Clear();
|
||||
}
|
||||
|
||||
sbl.Clear();
|
||||
}
|
||||
|
||||
double Entity::GetDistance(Point2d mp) {
|
||||
dogd.drawing = false;
|
||||
dogd.mp = mp;
|
||||
dogd.dmin = 1e12;
|
||||
|
||||
DrawOrGetDistance();
|
||||
|
||||
return dogd.dmin;
|
||||
}
|
||||
|
||||
Vector Entity::GetReferencePos(void) {
|
||||
dogd.drawing = false;
|
||||
|
||||
dogd.refp = SS.GW.offset.ScaledBy(-1);
|
||||
DrawOrGetDistance();
|
||||
|
||||
return dogd.refp;
|
||||
}
|
||||
|
||||
bool Entity::IsVisible(void) {
|
||||
Group *g = SK.GetGroup(group);
|
||||
|
||||
if(g->h.v == Group::HGROUP_REFERENCES.v && IsNormal()) {
|
||||
// The reference normals are always shown
|
||||
return true;
|
||||
}
|
||||
if(!(g->IsVisible())) return false;
|
||||
|
||||
// Don't check if points are hidden; this gets called only for
|
||||
// selected or hovered points, and those should always be shown.
|
||||
if(IsNormal() && !SS.GW.showNormals) return false;
|
||||
|
||||
if(!SS.GW.showWorkplanes) {
|
||||
if(IsWorkplane() && !h.isFromRequest()) {
|
||||
if(g->h.v != SS.GW.activeGroup.v) {
|
||||
// The group-associated workplanes are hidden outside
|
||||
// their group.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(style.v) {
|
||||
Style *s = Style::Get(style);
|
||||
if(!s->visible) return false;
|
||||
}
|
||||
|
||||
if(forceHidden) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Entity::CalculateNumerical(bool forExport) {
|
||||
if(IsPoint()) actPoint = PointGetNum();
|
||||
if(IsNormal()) actNormal = NormalGetNum();
|
||||
if(type == DISTANCE || type == DISTANCE_N_COPY) {
|
||||
actDistance = DistanceGetNum();
|
||||
}
|
||||
if(IsFace()) {
|
||||
actPoint = FaceGetPointNum();
|
||||
Vector n = FaceGetNormalNum();
|
||||
actNormal = Quaternion::From(0, n.x, n.y, n.z);
|
||||
}
|
||||
if(forExport) {
|
||||
// Visibility in copied import entities follows source file
|
||||
actVisible = IsVisible();
|
||||
} else {
|
||||
// Copied entities within a file are always visible
|
||||
actVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Entity::PointIsFromReferences(void) {
|
||||
return h.request().IsFromReferences();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Compute a cubic, second derivative continuous, interpolating spline. Same
|
||||
// routine for periodic splines (in a loop) or open splines (with specified
|
||||
// end tangents).
|
||||
//-----------------------------------------------------------------------------
|
||||
void Entity::ComputeInterpolatingSpline(SBezierList *sbl, bool periodic) {
|
||||
static const int MAX_N = BandedMatrix::MAX_UNKNOWNS;
|
||||
int ep = extraPoints;
|
||||
|
||||
// The number of unknowns to solve for.
|
||||
int n = periodic ? 3 + ep : ep;
|
||||
if(n >= MAX_N) oops();
|
||||
// The number of on-curve points, one more than the number of segments.
|
||||
int pts = periodic ? 4 + ep : 2 + ep;
|
||||
|
||||
int i, j, a;
|
||||
|
||||
// The starting and finishing control points that define our end tangents
|
||||
// (if the spline isn't periodic), and the on-curve points.
|
||||
Vector ctrl_s, ctrl_f, pt[MAX_N+4];
|
||||
if(periodic) {
|
||||
for(i = 0; i < ep + 3; i++) {
|
||||
pt[i] = SK.GetEntity(point[i])->PointGetNum();
|
||||
}
|
||||
pt[i++] = SK.GetEntity(point[0])->PointGetNum();
|
||||
} else {
|
||||
ctrl_s = SK.GetEntity(point[1])->PointGetNum();
|
||||
ctrl_f = SK.GetEntity(point[ep+2])->PointGetNum();
|
||||
j = 0;
|
||||
pt[j++] = SK.GetEntity(point[0])->PointGetNum();
|
||||
for(i = 2; i <= ep + 1; i++) {
|
||||
pt[j++] = SK.GetEntity(point[i])->PointGetNum();
|
||||
}
|
||||
pt[j++] = SK.GetEntity(point[ep+3])->PointGetNum();
|
||||
}
|
||||
|
||||
// The unknowns that we will be solving for, a set for each coordinate.
|
||||
double Xx[MAX_N], Xy[MAX_N], Xz[MAX_N];
|
||||
// For a cubic Bezier section f(t) as t goes from 0 to 1,
|
||||
// f' (0) = 3*(P1 - P0)
|
||||
// f' (1) = 3*(P3 - P2)
|
||||
// f''(0) = 6*(P0 - 2*P1 + P2)
|
||||
// f''(1) = 6*(P3 - 2*P2 + P1)
|
||||
for(a = 0; a < 3; a++) {
|
||||
BandedMatrix bm;
|
||||
ZERO(&bm);
|
||||
bm.n = n;
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
int im, it, ip;
|
||||
if(periodic) {
|
||||
im = WRAP(i - 1, n);
|
||||
it = i;
|
||||
ip = WRAP(i + 1, n);
|
||||
} else {
|
||||
im = i;
|
||||
it = i + 1;
|
||||
ip = i + 2;
|
||||
}
|
||||
// All of these are expressed in terms of a constant part, and
|
||||
// of X[i-1], X[i], and X[i+1]; so let these be the four
|
||||
// components of that vector;
|
||||
Vector4 A, B, C, D, E;
|
||||
// The on-curve interpolated point
|
||||
C = Vector4::From((pt[it]).Element(a), 0, 0, 0);
|
||||
// control point one back, C - X[i]
|
||||
B = C.Plus(Vector4::From(0, 0, -1, 0));
|
||||
// control point one forward, C + X[i]
|
||||
D = C.Plus(Vector4::From(0, 0, 1, 0));
|
||||
// control point two back
|
||||
if(i == 0 && !periodic) {
|
||||
A = Vector4::From(ctrl_s.Element(a), 0, 0, 0);
|
||||
} else {
|
||||
// pt[im] + X[i-1]
|
||||
A = Vector4::From(pt[im].Element(a), 1, 0, 0);
|
||||
}
|
||||
// control point two forward
|
||||
if(i == (n - 1) && !periodic) {
|
||||
E = Vector4::From(ctrl_f.Element(a), 0, 0, 0);
|
||||
} else {
|
||||
// pt[ip] - X[i+1]
|
||||
E = Vector4::From((pt[ip]).Element(a), 0, 0, -1);
|
||||
}
|
||||
// Write the second derivatives of each segment, dropping constant
|
||||
Vector4 fprev_pp = (C.Minus(B.ScaledBy(2))).Plus(A),
|
||||
fnext_pp = (C.Minus(D.ScaledBy(2))).Plus(E),
|
||||
eq = fprev_pp.Minus(fnext_pp);
|
||||
|
||||
bm.B[i] = -eq.w;
|
||||
if(periodic) {
|
||||
bm.A[i][WRAP(i-2, n)] = eq.x;
|
||||
bm.A[i][WRAP(i-1, n)] = eq.y;
|
||||
bm.A[i][i] = eq.z;
|
||||
} else {
|
||||
// The wrapping would work, except when n = 1 and everything
|
||||
// wraps to zero...
|
||||
if(i > 0) bm.A[i][i - 1] = eq.x;
|
||||
bm.A[i][i] = eq.y;
|
||||
if(i < (n-1)) bm.A[i][i + 1] = eq.z;
|
||||
}
|
||||
}
|
||||
bm.Solve();
|
||||
double *X = (a == 0) ? Xx :
|
||||
(a == 1) ? Xy :
|
||||
Xz;
|
||||
memcpy(X, bm.X, n*sizeof(double));
|
||||
}
|
||||
|
||||
for(i = 0; i < pts - 1; i++) {
|
||||
Vector p0, p1, p2, p3;
|
||||
if(periodic) {
|
||||
p0 = pt[i];
|
||||
int iw = WRAP(i - 1, n);
|
||||
p1 = p0.Plus(Vector::From(Xx[iw], Xy[iw], Xz[iw]));
|
||||
} else if(i == 0) {
|
||||
p0 = pt[0];
|
||||
p1 = ctrl_s;
|
||||
} else {
|
||||
p0 = pt[i];
|
||||
p1 = p0.Plus(Vector::From(Xx[i-1], Xy[i-1], Xz[i-1]));
|
||||
}
|
||||
if(periodic) {
|
||||
p3 = pt[i+1];
|
||||
int iw = WRAP(i, n);
|
||||
p2 = p3.Minus(Vector::From(Xx[iw], Xy[iw], Xz[iw]));
|
||||
} else if(i == (pts - 2)) {
|
||||
p3 = pt[pts-1];
|
||||
p2 = ctrl_f;
|
||||
} else {
|
||||
p3 = pt[i+1];
|
||||
p2 = p3.Minus(Vector::From(Xx[i], Xy[i], Xz[i]));
|
||||
}
|
||||
SBezier sb = SBezier::From(p0, p1, p2, p3);
|
||||
sbl->l.Add(&sb);
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::GenerateBezierCurves(SBezierList *sbl) {
|
||||
SBezier sb;
|
||||
|
||||
int i = sbl->l.n;
|
||||
|
||||
switch(type) {
|
||||
case LINE_SEGMENT: {
|
||||
Vector a = SK.GetEntity(point[0])->PointGetNum();
|
||||
Vector b = SK.GetEntity(point[1])->PointGetNum();
|
||||
sb = SBezier::From(a, b);
|
||||
sbl->l.Add(&sb);
|
||||
break;
|
||||
}
|
||||
case CUBIC:
|
||||
ComputeInterpolatingSpline(sbl, false);
|
||||
break;
|
||||
|
||||
case CUBIC_PERIODIC:
|
||||
ComputeInterpolatingSpline(sbl, true);
|
||||
break;
|
||||
|
||||
case CIRCLE:
|
||||
case ARC_OF_CIRCLE: {
|
||||
Vector center = SK.GetEntity(point[0])->PointGetNum();
|
||||
Quaternion q = SK.GetEntity(normal)->NormalGetNum();
|
||||
Vector u = q.RotationU(), v = q.RotationV();
|
||||
double r = CircleGetRadiusNum();
|
||||
double thetaa, thetab, dtheta;
|
||||
|
||||
if(r < LENGTH_EPS) {
|
||||
// If a circle or an arc gets dragged through zero radius,
|
||||
// then we just don't generate anything.
|
||||
break;
|
||||
}
|
||||
|
||||
if(type == CIRCLE) {
|
||||
thetaa = 0;
|
||||
thetab = 2*PI;
|
||||
dtheta = 2*PI;
|
||||
} else {
|
||||
ArcGetAngles(&thetaa, &thetab, &dtheta);
|
||||
}
|
||||
int i, n;
|
||||
if(dtheta > (3*PI/2 + 0.01)) {
|
||||
n = 4;
|
||||
} else if(dtheta > (PI + 0.01)) {
|
||||
n = 3;
|
||||
} else if(dtheta > (PI/2 + 0.01)) {
|
||||
n = 2;
|
||||
} else {
|
||||
n = 1;
|
||||
}
|
||||
dtheta /= n;
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
double s, c;
|
||||
|
||||
c = cos(thetaa);
|
||||
s = sin(thetaa);
|
||||
// The start point of the curve, and the tangent vector at
|
||||
// that start point.
|
||||
Vector p0 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
|
||||
t0 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
|
||||
|
||||
thetaa += dtheta;
|
||||
|
||||
c = cos(thetaa);
|
||||
s = sin(thetaa);
|
||||
Vector p2 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
|
||||
t2 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
|
||||
|
||||
// The control point must lie on both tangents.
|
||||
Vector p1 = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
|
||||
p2, p2.Plus(t2),
|
||||
NULL);
|
||||
|
||||
SBezier sb = SBezier::From(p0, p1, p2);
|
||||
sb.weight[1] = cos(dtheta/2);
|
||||
sbl->l.Add(&sb);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TTF_TEXT: {
|
||||
Vector topLeft = SK.GetEntity(point[0])->PointGetNum();
|
||||
Vector botLeft = SK.GetEntity(point[1])->PointGetNum();
|
||||
Vector n = Normal()->NormalN();
|
||||
Vector v = topLeft.Minus(botLeft);
|
||||
Vector u = (v.Cross(n)).WithMagnitude(v.Magnitude());
|
||||
|
||||
SS.fonts.PlotString(font.str, str.str, 0, sbl, botLeft, u, v);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Not a problem, points and normals and such don't generate curves
|
||||
break;
|
||||
}
|
||||
|
||||
// Record our style for all of the Beziers that we just created.
|
||||
for(; i < sbl->l.n; i++) {
|
||||
sbl->l.elem[i].auxA = style.v;
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::DrawOrGetDistance(void) {
|
||||
if(!IsVisible()) return;
|
||||
|
||||
Group *g = SK.GetGroup(group);
|
||||
|
||||
switch(type) {
|
||||
case POINT_N_COPY:
|
||||
case POINT_N_TRANS:
|
||||
case POINT_N_ROT_TRANS:
|
||||
case POINT_N_ROT_AA:
|
||||
case POINT_IN_3D:
|
||||
case POINT_IN_2D: {
|
||||
Vector v = PointGetNum();
|
||||
dogd.refp = v;
|
||||
|
||||
if(dogd.drawing) {
|
||||
double s = 3.5;
|
||||
Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale);
|
||||
Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale);
|
||||
|
||||
glxColorRGB(Style::Color(Style::DATUM));
|
||||
glxDepthRangeOffset(6);
|
||||
glBegin(GL_QUADS);
|
||||
glxVertex3v(v.Plus (r).Plus (d));
|
||||
glxVertex3v(v.Plus (r).Minus(d));
|
||||
glxVertex3v(v.Minus(r).Minus(d));
|
||||
glxVertex3v(v.Minus(r).Plus (d));
|
||||
glEnd();
|
||||
glxDepthRangeOffset(0);
|
||||
} else {
|
||||
Point2d pp = SS.GW.ProjectPoint(v);
|
||||
dogd.dmin = pp.DistanceTo(dogd.mp) - 6;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NORMAL_N_COPY:
|
||||
case NORMAL_N_ROT:
|
||||
case NORMAL_N_ROT_AA:
|
||||
case NORMAL_IN_3D:
|
||||
case NORMAL_IN_2D: {
|
||||
int i;
|
||||
for(i = 0; i < 2; i++) {
|
||||
if(i == 0 && !SS.GW.showNormals) {
|
||||
// When the normals are hidden, we will continue to show
|
||||
// the coordinate axes at the bottom left corner, but
|
||||
// not at the origin.
|
||||
continue;
|
||||
}
|
||||
|
||||
hRequest hr = h.request();
|
||||
// Always draw the x, y, and z axes in red, green, and blue;
|
||||
// brighter for the ones at the bottom left of the screen,
|
||||
// dimmer for the ones at the model origin.
|
||||
int f = (i == 0 ? 100 : 255);
|
||||
if(hr.v == Request::HREQUEST_REFERENCE_XY.v) {
|
||||
glxColorRGB(RGB(0, 0, f));
|
||||
} else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) {
|
||||
glxColorRGB(RGB(f, 0, 0));
|
||||
} else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) {
|
||||
glxColorRGB(RGB(0, f, 0));
|
||||
} else {
|
||||
glxColorRGB(Style::Color(Style::NORMALS));
|
||||
if(i > 0) break;
|
||||
}
|
||||
|
||||
Quaternion q = NormalGetNum();
|
||||
Vector tail;
|
||||
if(i == 0) {
|
||||
tail = SK.GetEntity(point[0])->PointGetNum();
|
||||
glLineWidth(1);
|
||||
} else {
|
||||
// Draw an extra copy of the x, y, and z axes, that's
|
||||
// always in the corner of the view and at the front.
|
||||
// So those are always available, perhaps useful.
|
||||
double s = SS.GW.scale;
|
||||
double h = 60 - SS.GW.height/2;
|
||||
double w = 60 - SS.GW.width/2;
|
||||
tail = SS.GW.projRight.ScaledBy(w/s).Plus(
|
||||
SS.GW.projUp. ScaledBy(h/s)).Minus(SS.GW.offset);
|
||||
glxDepthRangeLockToFront(true);
|
||||
glLineWidth(2);
|
||||
}
|
||||
|
||||
Vector v = (q.RotationN()).WithMagnitude(50/SS.GW.scale);
|
||||
Vector tip = tail.Plus(v);
|
||||
LineDrawOrGetDistance(tail, tip);
|
||||
|
||||
v = v.WithMagnitude(12/SS.GW.scale);
|
||||
Vector axis = q.RotationV();
|
||||
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis, 0.6)));
|
||||
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis,-0.6)));
|
||||
}
|
||||
glxDepthRangeLockToFront(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case DISTANCE:
|
||||
case DISTANCE_N_COPY:
|
||||
// These are used only as data structures, nothing to display.
|
||||
break;
|
||||
|
||||
case WORKPLANE: {
|
||||
Vector p;
|
||||
p = SK.GetEntity(point[0])->PointGetNum();
|
||||
|
||||
Vector u = Normal()->NormalU();
|
||||
Vector v = Normal()->NormalV();
|
||||
|
||||
double s = (min(SS.GW.width, SS.GW.height))*0.45/SS.GW.scale;
|
||||
|
||||
Vector us = u.ScaledBy(s);
|
||||
Vector vs = v.ScaledBy(s);
|
||||
|
||||
Vector pp = p.Plus (us).Plus (vs);
|
||||
Vector pm = p.Plus (us).Minus(vs);
|
||||
Vector mm = p.Minus(us).Minus(vs), mm2 = mm;
|
||||
Vector mp = p.Minus(us).Plus (vs);
|
||||
|
||||
glLineWidth(1);
|
||||
glxColorRGB(Style::Color(Style::NORMALS));
|
||||
glEnable(GL_LINE_STIPPLE);
|
||||
glLineStipple(3, 0x1111);
|
||||
if(!h.isFromRequest()) {
|
||||
mm = mm.Plus(v.ScaledBy(60/SS.GW.scale));
|
||||
mm2 = mm2.Plus(u.ScaledBy(60/SS.GW.scale));
|
||||
LineDrawOrGetDistance(mm2, mm);
|
||||
}
|
||||
LineDrawOrGetDistance(pp, pm);
|
||||
LineDrawOrGetDistance(pm, mm2);
|
||||
LineDrawOrGetDistance(mm, mp);
|
||||
LineDrawOrGetDistance(mp, pp);
|
||||
glDisable(GL_LINE_STIPPLE);
|
||||
|
||||
char *str = DescriptionString()+5;
|
||||
double th = DEFAULT_TEXT_HEIGHT;
|
||||
if(dogd.drawing) {
|
||||
glxWriteText(str, th, mm2, u, v, NULL, NULL);
|
||||
} else {
|
||||
Vector pos = mm2.Plus(u.ScaledBy(glxStrWidth(str, th)/2)).Plus(
|
||||
v.ScaledBy(glxStrHeight(th)/2));
|
||||
Point2d pp = SS.GW.ProjectPoint(pos);
|
||||
dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 10);
|
||||
// If a line lies in a plane, then select the line, not
|
||||
// the plane.
|
||||
dogd.dmin += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LINE_SEGMENT:
|
||||
case CIRCLE:
|
||||
case ARC_OF_CIRCLE:
|
||||
case CUBIC:
|
||||
case CUBIC_PERIODIC:
|
||||
case TTF_TEXT:
|
||||
// Nothing but the curve(s).
|
||||
break;
|
||||
|
||||
case FACE_NORMAL_PT:
|
||||
case FACE_XPROD:
|
||||
case FACE_N_ROT_TRANS:
|
||||
case FACE_N_TRANS:
|
||||
case FACE_N_ROT_AA:
|
||||
// Do nothing; these are drawn with the triangle mesh
|
||||
break;
|
||||
|
||||
default:
|
||||
oops();
|
||||
}
|
||||
|
||||
// And draw the curves; generate the rational polynomial curves for
|
||||
// everything, then piecewise linearize them, and display those.
|
||||
SEdgeList sel;
|
||||
ZERO(&sel);
|
||||
GenerateEdges(&sel, true);
|
||||
int i;
|
||||
for(i = 0; i < sel.l.n; i++) {
|
||||
SEdge *se = &(sel.l.elem[i]);
|
||||
LineDrawOrGetDistance(se->a, se->b, true);
|
||||
}
|
||||
sel.Clear();
|
||||
}
|
||||
|
732
export.cpp
|
@ -1,732 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// The 2d vector output stuff that isn't specific to any particular file
|
||||
// format: getting the appropriate lines and curves, performing hidden line
|
||||
// removal, calculating bounding boxes, and so on. Also raster and triangle
|
||||
// mesh output.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
#include <png.h>
|
||||
|
||||
void SolveSpace::ExportSectionTo(char *filename) {
|
||||
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
|
||||
gn = gn.WithMagnitude(1);
|
||||
|
||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
||||
g->GenerateDisplayItems();
|
||||
if(g->displayMesh.IsEmpty()) {
|
||||
Error("No solid model present; draw one with extrudes and revolves, "
|
||||
"or use Export 2d View to export bare lines and curves.");
|
||||
return;
|
||||
}
|
||||
|
||||
// The plane in which the exported section lies; need this because we'll
|
||||
// reorient from that plane into the xy plane before exporting.
|
||||
Vector origin, u, v, n;
|
||||
double d;
|
||||
|
||||
SS.GW.GroupSelection();
|
||||
#define gs (SS.GW.gs)
|
||||
if((gs.n == 0 && g->activeWorkplane.v != Entity::FREE_IN_3D.v)) {
|
||||
Entity *wrkpl = SK.GetEntity(g->activeWorkplane);
|
||||
origin = wrkpl->WorkplaneGetOffset();
|
||||
n = wrkpl->Normal()->NormalN();
|
||||
u = wrkpl->Normal()->NormalU();
|
||||
v = wrkpl->Normal()->NormalV();
|
||||
} else if(gs.n == 1 && gs.faces == 1) {
|
||||
Entity *face = SK.GetEntity(gs.entity[0]);
|
||||
origin = face->FaceGetPointNum();
|
||||
n = face->FaceGetNormalNum();
|
||||
if(n.Dot(gn) < 0) n = n.ScaledBy(-1);
|
||||
u = n.Normal(0);
|
||||
v = n.Normal(1);
|
||||
} else if(gs.n == 3 && gs.vectors == 2 && gs.points == 1) {
|
||||
Vector ut = SK.GetEntity(gs.entity[0])->VectorGetNum(),
|
||||
vt = SK.GetEntity(gs.entity[1])->VectorGetNum();
|
||||
ut = ut.WithMagnitude(1);
|
||||
vt = vt.WithMagnitude(1);
|
||||
|
||||
if(fabs(SS.GW.projUp.Dot(vt)) < fabs(SS.GW.projUp.Dot(ut))) {
|
||||
SWAP(Vector, ut, vt);
|
||||
}
|
||||
if(SS.GW.projRight.Dot(ut) < 0) ut = ut.ScaledBy(-1);
|
||||
if(SS.GW.projUp. Dot(vt) < 0) vt = vt.ScaledBy(-1);
|
||||
|
||||
origin = SK.GetEntity(gs.point[0])->PointGetNum();
|
||||
n = ut.Cross(vt);
|
||||
u = ut.WithMagnitude(1);
|
||||
v = (n.Cross(u)).WithMagnitude(1);
|
||||
} else {
|
||||
Error("Bad selection for export section. Please select:\n\n"
|
||||
" * nothing, with an active workplane "
|
||||
"(workplane is section plane)\n"
|
||||
" * a face (section plane through face)\n"
|
||||
" * a point and two line segments "
|
||||
"(plane through point and parallel to lines)\n");
|
||||
return;
|
||||
}
|
||||
SS.GW.ClearSelection();
|
||||
|
||||
n = n.WithMagnitude(1);
|
||||
d = origin.Dot(n);
|
||||
|
||||
SEdgeList el;
|
||||
ZERO(&el);
|
||||
SBezierList bl;
|
||||
ZERO(&bl);
|
||||
|
||||
// If there's a mesh, then grab the edges from it.
|
||||
g->runningMesh.MakeEdgesInPlaneInto(&el, n, d);
|
||||
|
||||
// If there's a shell, then grab the edges and possibly Beziers.
|
||||
g->runningShell.MakeSectionEdgesInto(n, d,
|
||||
&el,
|
||||
(SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS) ? NULL : &bl);
|
||||
|
||||
// All of these are solid model edges, so use the appropriate style.
|
||||
SEdge *se;
|
||||
for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
|
||||
se->auxA = Style::SOLID_EDGE;
|
||||
}
|
||||
SBezier *sb;
|
||||
for(sb = bl.l.First(); sb; sb = bl.l.NextAfter(sb)) {
|
||||
sb->auxA = Style::SOLID_EDGE;
|
||||
}
|
||||
|
||||
el.CullExtraneousEdges();
|
||||
bl.CullIdenticalBeziers();
|
||||
|
||||
// And write the edges.
|
||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
||||
if(out) {
|
||||
// parallel projection (no perspective), and no mesh
|
||||
ExportLinesAndMesh(&el, &bl, NULL,
|
||||
u, v, n, origin, 0,
|
||||
out);
|
||||
}
|
||||
el.Clear();
|
||||
bl.Clear();
|
||||
}
|
||||
|
||||
void SolveSpace::ExportViewOrWireframeTo(char *filename, bool wireframe) {
|
||||
int i;
|
||||
SEdgeList edges;
|
||||
ZERO(&edges);
|
||||
SBezierList beziers;
|
||||
ZERO(&beziers);
|
||||
|
||||
SMesh *sm = NULL;
|
||||
if(SS.GW.showShaded) {
|
||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
||||
g->GenerateDisplayItems();
|
||||
sm = &(g->displayMesh);
|
||||
}
|
||||
if(sm && sm->IsEmpty()) {
|
||||
sm = NULL;
|
||||
}
|
||||
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
if(!e->IsVisible()) continue;
|
||||
if(e->construction) continue;
|
||||
|
||||
if(SS.exportPwlCurves || (sm && !SS.GW.showHdnLines) ||
|
||||
fabs(SS.exportOffset) > LENGTH_EPS)
|
||||
{
|
||||
// We will be doing hidden line removal, which we can't do on
|
||||
// exact curves; so we need things broken down to pwls. Same
|
||||
// problem with cutter radius compensation.
|
||||
e->GenerateEdges(&edges);
|
||||
} else {
|
||||
e->GenerateBezierCurves(&beziers);
|
||||
}
|
||||
}
|
||||
|
||||
if(SS.GW.showEdges) {
|
||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
||||
g->GenerateDisplayItems();
|
||||
SEdgeList *selr = &(g->displayEdges);
|
||||
SEdge *se;
|
||||
for(se = selr->l.First(); se; se = selr->l.NextAfter(se)) {
|
||||
edges.AddEdge(se->a, se->b, Style::SOLID_EDGE);
|
||||
}
|
||||
}
|
||||
|
||||
if(SS.GW.showConstraints) {
|
||||
Constraint *c;
|
||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
||||
c->GetEdges(&edges);
|
||||
}
|
||||
}
|
||||
|
||||
if(wireframe) {
|
||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
||||
if(out) {
|
||||
ExportWireframeCurves(&edges, &beziers, out);
|
||||
}
|
||||
} else {
|
||||
Vector u = SS.GW.projRight,
|
||||
v = SS.GW.projUp,
|
||||
n = u.Cross(v),
|
||||
origin = SS.GW.offset.ScaledBy(-1);
|
||||
|
||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
||||
if(out) {
|
||||
ExportLinesAndMesh(&edges, &beziers, sm,
|
||||
u, v, n, origin, SS.CameraTangent()*SS.GW.scale,
|
||||
out);
|
||||
}
|
||||
|
||||
if(out && !out->HasCanvasSize()) {
|
||||
// These file formats don't have a canvas size, so they just
|
||||
// get exported in the raw coordinate system. So indicate what
|
||||
// that was on-screen.
|
||||
SS.justExportedInfo.draw = true;
|
||||
SS.justExportedInfo.pt = origin;
|
||||
SS.justExportedInfo.u = u;
|
||||
SS.justExportedInfo.v = v;
|
||||
InvalidateGraphics();
|
||||
}
|
||||
}
|
||||
|
||||
edges.Clear();
|
||||
beziers.Clear();
|
||||
}
|
||||
|
||||
void SolveSpace::ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,
|
||||
VectorFileWriter *out)
|
||||
{
|
||||
SBezierLoopSetSet sblss;
|
||||
ZERO(&sblss);
|
||||
SEdge *se;
|
||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||
SBezier sb = SBezier::From(
|
||||
(se->a).ScaledBy(1.0 / SS.exportScale),
|
||||
(se->b).ScaledBy(1.0 / SS.exportScale));
|
||||
sblss.AddOpenPath(&sb);
|
||||
}
|
||||
|
||||
sbl->ScaleSelfBy(1.0/SS.exportScale);
|
||||
SBezier *sb;
|
||||
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
|
||||
sblss.AddOpenPath(sb);
|
||||
}
|
||||
|
||||
out->Output(&sblss, NULL);
|
||||
sblss.Clear();
|
||||
}
|
||||
|
||||
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
|
||||
Vector u, Vector v, Vector n,
|
||||
Vector origin, double cameraTan,
|
||||
VectorFileWriter *out)
|
||||
{
|
||||
double s = 1.0 / SS.exportScale;
|
||||
|
||||
// Project into the export plane; so when we're done, z doesn't matter,
|
||||
// and x and y are what goes in the DXF.
|
||||
SEdge *e;
|
||||
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
|
||||
// project into the specified csys, and apply export scale
|
||||
(e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||
(e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||
}
|
||||
|
||||
SBezier *b;
|
||||
if(sbl) {
|
||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
||||
*b = b->InPerspective(u, v, n, origin, cameraTan);
|
||||
int i;
|
||||
for(i = 0; i <= b->deg; i++) {
|
||||
b->ctrl[i] = (b->ctrl[i]).ScaledBy(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If cutter radius compensation is requested, then perform it now
|
||||
if(fabs(SS.exportOffset) > LENGTH_EPS) {
|
||||
// assemble those edges into a polygon, and clear the edge list
|
||||
SPolygon sp;
|
||||
ZERO(&sp);
|
||||
sel->AssemblePolygon(&sp, NULL);
|
||||
sel->Clear();
|
||||
|
||||
SPolygon compd;
|
||||
ZERO(&compd);
|
||||
sp.normal = Vector::From(0, 0, -1);
|
||||
sp.FixContourDirections();
|
||||
sp.OffsetInto(&compd, SS.exportOffset*s);
|
||||
sp.Clear();
|
||||
|
||||
compd.MakeEdgesInto(sel);
|
||||
compd.Clear();
|
||||
}
|
||||
|
||||
// Now the triangle mesh; project, then build a BSP to perform
|
||||
// occlusion testing and generated the shaded surfaces.
|
||||
SMesh smp;
|
||||
ZERO(&smp);
|
||||
if(sm) {
|
||||
Vector l0 = (SS.lightDir[0]).WithMagnitude(1),
|
||||
l1 = (SS.lightDir[1]).WithMagnitude(1);
|
||||
STriangle *tr;
|
||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
||||
STriangle tt = *tr;
|
||||
tt.a = (tt.a).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||
tt.b = (tt.b).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||
tt.c = (tt.c).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
||||
|
||||
// And calculate lighting for the triangle
|
||||
Vector n = tt.Normal().WithMagnitude(1);
|
||||
double lighting = SS.ambientIntensity +
|
||||
max(0, (SS.lightIntensity[0])*(n.Dot(l0))) +
|
||||
max(0, (SS.lightIntensity[1])*(n.Dot(l1)));
|
||||
double r = min(1, REDf (tt.meta.color)*lighting),
|
||||
g = min(1, GREENf(tt.meta.color)*lighting),
|
||||
b = min(1, BLUEf (tt.meta.color)*lighting);
|
||||
tt.meta.color = RGBf(r, g, b);
|
||||
smp.AddTriangle(&tt);
|
||||
}
|
||||
}
|
||||
|
||||
// Use the BSP routines to generate the split triangles in paint order.
|
||||
SBsp3 *bsp = SBsp3::FromMesh(&smp);
|
||||
SMesh sms;
|
||||
ZERO(&sms);
|
||||
bsp->GenerateInPaintOrder(&sms);
|
||||
// And cull the back-facing triangles
|
||||
STriangle *tr;
|
||||
sms.l.ClearTags();
|
||||
for(tr = sms.l.First(); tr; tr = sms.l.NextAfter(tr)) {
|
||||
Vector n = tr->Normal();
|
||||
if(n.z < 0) {
|
||||
tr->tag = 1;
|
||||
}
|
||||
}
|
||||
sms.l.RemoveTagged();
|
||||
|
||||
// And now we perform hidden line removal if requested
|
||||
SEdgeList hlrd;
|
||||
ZERO(&hlrd);
|
||||
if(sm && !SS.GW.showHdnLines) {
|
||||
SKdNode *root = SKdNode::From(&smp);
|
||||
|
||||
// Generate the edges where a curved surface turns from front-facing
|
||||
// to back-facing.
|
||||
if(SS.GW.showEdges) {
|
||||
root->MakeCertainEdgesInto(sel, SKdNode::TURNING_EDGES,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
root->ClearTags();
|
||||
int cnt = 1234;
|
||||
|
||||
SEdge *se;
|
||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||
if(se->auxA == Style::CONSTRAINT) {
|
||||
// Constraints should not get hidden line removed; they're
|
||||
// always on top.
|
||||
hlrd.AddEdge(se->a, se->b, se->auxA);
|
||||
continue;
|
||||
}
|
||||
|
||||
SEdgeList out;
|
||||
ZERO(&out);
|
||||
// Split the original edge against the mesh
|
||||
out.AddEdge(se->a, se->b, se->auxA);
|
||||
root->OcclusionTestLine(*se, &out, cnt);
|
||||
// the occlusion test splits unnecessarily; so fix those
|
||||
out.MergeCollinearSegments(se->a, se->b);
|
||||
cnt++;
|
||||
// And add the results to our output
|
||||
SEdge *sen;
|
||||
for(sen = out.l.First(); sen; sen = out.l.NextAfter(sen)) {
|
||||
hlrd.AddEdge(sen->a, sen->b, sen->auxA);
|
||||
}
|
||||
out.Clear();
|
||||
}
|
||||
|
||||
sel = &hlrd;
|
||||
}
|
||||
|
||||
// We kept the line segments and Beziers separate until now; but put them
|
||||
// all together, and also project everything into the xy plane, since not
|
||||
// all export targets ignore the z component of the points.
|
||||
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
|
||||
SBezier sb = SBezier::From(e->a, e->b);
|
||||
sb.auxA = e->auxA;
|
||||
sbl->l.Add(&sb);
|
||||
}
|
||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
||||
for(int i = 0; i <= b->deg; i++) {
|
||||
b->ctrl[i].z = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If possible, then we will assemble these output curves into loops. They
|
||||
// will then get exported as closed paths.
|
||||
SBezierLoopSetSet sblss;
|
||||
ZERO(&sblss);
|
||||
SBezierList leftovers;
|
||||
ZERO(&leftovers);
|
||||
SSurface srf = SSurface::FromPlane(Vector::From(0, 0, 0),
|
||||
Vector::From(1, 0, 0),
|
||||
Vector::From(0, 1, 0));
|
||||
SPolygon spxyz;
|
||||
ZERO(&spxyz);
|
||||
bool allClosed;
|
||||
SEdge notClosedAt;
|
||||
sbl->l.ClearTags();
|
||||
sblss.FindOuterFacesFrom(sbl, &spxyz, &srf,
|
||||
SS.ChordTolMm()*s,
|
||||
&allClosed, ¬ClosedAt,
|
||||
NULL, NULL,
|
||||
&leftovers);
|
||||
for(b = leftovers.l.First(); b; b = leftovers.l.NextAfter(b)) {
|
||||
sblss.AddOpenPath(b);
|
||||
}
|
||||
|
||||
// Now write the lines and triangles to the output file
|
||||
out->Output(&sblss, &sms);
|
||||
|
||||
leftovers.Clear();
|
||||
spxyz.Clear();
|
||||
sblss.Clear();
|
||||
smp.Clear();
|
||||
sms.Clear();
|
||||
hlrd.Clear();
|
||||
}
|
||||
|
||||
double VectorFileWriter::MmToPts(double mm) {
|
||||
// 72 points in an inch
|
||||
return (mm/25.4)*72;
|
||||
}
|
||||
|
||||
VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
|
||||
VectorFileWriter *ret;
|
||||
if(StringEndsIn(filename, ".dxf")) {
|
||||
static DxfFileWriter DxfWriter;
|
||||
ret = &DxfWriter;
|
||||
} else if(StringEndsIn(filename, ".ps") || StringEndsIn(filename, ".eps")) {
|
||||
static EpsFileWriter EpsWriter;
|
||||
ret = &EpsWriter;
|
||||
} else if(StringEndsIn(filename, ".pdf")) {
|
||||
static PdfFileWriter PdfWriter;
|
||||
ret = &PdfWriter;
|
||||
} else if(StringEndsIn(filename, ".svg")) {
|
||||
static SvgFileWriter SvgWriter;
|
||||
ret = &SvgWriter;
|
||||
} else if(StringEndsIn(filename, ".plt")||StringEndsIn(filename, ".hpgl")) {
|
||||
static HpglFileWriter HpglWriter;
|
||||
ret = &HpglWriter;
|
||||
} else if(StringEndsIn(filename, ".step")||StringEndsIn(filename, ".stp")) {
|
||||
static Step2dFileWriter Step2dWriter;
|
||||
ret = &Step2dWriter;
|
||||
} else if(StringEndsIn(filename, ".txt")) {
|
||||
static GCodeFileWriter GCodeWriter;
|
||||
ret = &GCodeWriter;
|
||||
} else {
|
||||
Error("Can't identify output file type from file extension of "
|
||||
"filename '%s'; try "
|
||||
".step, .stp, .dxf, .svg, .plt, .hpgl, .pdf, .txt, "
|
||||
".eps, or .ps.",
|
||||
filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE *f = fopen(filename, "wb");
|
||||
if(!f) {
|
||||
Error("Couldn't write to '%s'", filename);
|
||||
return NULL;
|
||||
}
|
||||
ret->f = f;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void VectorFileWriter::Output(SBezierLoopSetSet *sblss, SMesh *sm) {
|
||||
STriangle *tr;
|
||||
SBezier *b;
|
||||
|
||||
// First calculate the bounding box.
|
||||
ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
||||
ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
|
||||
if(sm) {
|
||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
||||
(tr->a).MakeMaxMin(&ptMax, &ptMin);
|
||||
(tr->b).MakeMaxMin(&ptMax, &ptMin);
|
||||
(tr->c).MakeMaxMin(&ptMax, &ptMin);
|
||||
}
|
||||
}
|
||||
if(sblss) {
|
||||
SBezierLoopSet *sbls;
|
||||
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
|
||||
SBezierLoop *sbl;
|
||||
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
|
||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
||||
for(int i = 0; i <= b->deg; i++) {
|
||||
(b->ctrl[i]).MakeMaxMin(&ptMax, &ptMin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And now we compute the canvas size.
|
||||
double s = 1.0 / SS.exportScale;
|
||||
if(SS.exportCanvasSizeAuto) {
|
||||
// It's based on the calculated bounding box; we grow it along each
|
||||
// boundary by the specified amount.
|
||||
ptMin.x -= s*SS.exportMargin.left;
|
||||
ptMax.x += s*SS.exportMargin.right;
|
||||
ptMin.y -= s*SS.exportMargin.bottom;
|
||||
ptMax.y += s*SS.exportMargin.top;
|
||||
} else {
|
||||
ptMin.x = -(s*SS.exportCanvas.dx);
|
||||
ptMin.y = -(s*SS.exportCanvas.dy);
|
||||
ptMax.x = ptMin.x + (s*SS.exportCanvas.width);
|
||||
ptMax.y = ptMin.y + (s*SS.exportCanvas.height);
|
||||
}
|
||||
|
||||
StartFile();
|
||||
if(sm && SS.exportShadedTriangles) {
|
||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
||||
Triangle(tr);
|
||||
}
|
||||
}
|
||||
if(sblss) {
|
||||
SBezierLoopSet *sbls;
|
||||
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
|
||||
SBezierLoop *sbl;
|
||||
sbl = sbls->l.First();
|
||||
if(!sbl) continue;
|
||||
b = sbl->l.First();
|
||||
if(!b || !Style::Exportable(b->auxA)) continue;
|
||||
|
||||
hStyle hs = { b->auxA };
|
||||
Style *stl = Style::Get(hs);
|
||||
double lineWidth = Style::WidthMm(b->auxA)*s;
|
||||
DWORD strokeRgb = Style::Color(hs, true);
|
||||
DWORD fillRgb = Style::FillColor(hs, true);
|
||||
|
||||
StartPath(strokeRgb, lineWidth, stl->filled, fillRgb);
|
||||
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
|
||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
||||
Bezier(b);
|
||||
}
|
||||
}
|
||||
FinishPath(strokeRgb, lineWidth, stl->filled, fillRgb);
|
||||
}
|
||||
}
|
||||
FinishAndCloseFile();
|
||||
}
|
||||
|
||||
void VectorFileWriter::BezierAsPwl(SBezier *sb) {
|
||||
List<Vector> lv;
|
||||
ZERO(&lv);
|
||||
sb->MakePwlInto(&lv, SS.ChordTolMm() / SS.exportScale);
|
||||
int i;
|
||||
for(i = 1; i < lv.n; i++) {
|
||||
SBezier sb = SBezier::From(lv.elem[i-1], lv.elem[i]);
|
||||
Bezier(&sb);
|
||||
}
|
||||
lv.Clear();
|
||||
}
|
||||
|
||||
void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) {
|
||||
Vector t0 = sb->TangentAt(0), t1 = sb->TangentAt(1);
|
||||
// The curve is correct, and the first derivatives are correct, at the
|
||||
// endpoints.
|
||||
SBezier bnr = SBezier::From(
|
||||
sb->Start(),
|
||||
sb->Start().Plus(t0.ScaledBy(1.0/3)),
|
||||
sb->Finish().Minus(t1.ScaledBy(1.0/3)),
|
||||
sb->Finish());
|
||||
|
||||
double tol = SS.ChordTolMm() / SS.exportScale;
|
||||
// Arbitrary choice, but make it a little finer than pwl tolerance since
|
||||
// it should be easier to achieve that with the smooth curves.
|
||||
tol /= 2;
|
||||
|
||||
bool closeEnough = true;
|
||||
int i;
|
||||
for(i = 1; i <= 3; i++) {
|
||||
double t = i/4.0;
|
||||
Vector p0 = sb->PointAt(t),
|
||||
pn = bnr.PointAt(t);
|
||||
double d = (p0.Minus(pn)).Magnitude();
|
||||
if(d > tol) {
|
||||
closeEnough = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(closeEnough || depth > 3) {
|
||||
Bezier(&bnr);
|
||||
} else {
|
||||
SBezier bef, aft;
|
||||
sb->SplitAt(0.5, &bef, &aft);
|
||||
BezierAsNonrationalCubic(&bef, depth+1);
|
||||
BezierAsNonrationalCubic(&aft, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Export a triangle mesh, in the requested format.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SolveSpace::ExportMeshTo(char *filename) {
|
||||
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
||||
if(m->IsEmpty()) {
|
||||
Error("Active group mesh is empty; nothing to export.");
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *f = fopen(filename, "wb");
|
||||
if(!f) {
|
||||
Error("Couldn't write to '%s'", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
if(StringEndsIn(filename, ".stl")) {
|
||||
ExportMeshAsStlTo(f, m);
|
||||
} else if(StringEndsIn(filename, ".obj")) {
|
||||
ExportMeshAsObjTo(f, m);
|
||||
} else {
|
||||
Error("Can't identify output file type from file extension of "
|
||||
"filename '%s'; try .stl, .obj.", filename);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Export the mesh as an STL file; it should always be vertex-to-vertex and
|
||||
// not self-intersecting, so not much to do.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SolveSpace::ExportMeshAsStlTo(FILE *f, SMesh *sm) {
|
||||
char str[80];
|
||||
memset(str, 0, sizeof(str));
|
||||
strcpy(str, "STL exported mesh");
|
||||
fwrite(str, 1, 80, f);
|
||||
|
||||
DWORD n = sm->l.n;
|
||||
fwrite(&n, 4, 1, f);
|
||||
|
||||
double s = SS.exportScale;
|
||||
int i;
|
||||
for(i = 0; i < sm->l.n; i++) {
|
||||
STriangle *tr = &(sm->l.elem[i]);
|
||||
Vector n = tr->Normal().WithMagnitude(1);
|
||||
float w;
|
||||
w = (float)n.x; fwrite(&w, 4, 1, f);
|
||||
w = (float)n.y; fwrite(&w, 4, 1, f);
|
||||
w = (float)n.z; fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->a.x)/s); fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->a.y)/s); fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->a.z)/s); fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->b.x)/s); fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->b.y)/s); fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->b.z)/s); fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->c.x)/s); fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->c.y)/s); fwrite(&w, 4, 1, f);
|
||||
w = (float)((tr->c.z)/s); fwrite(&w, 4, 1, f);
|
||||
fputc(0, f);
|
||||
fputc(0, f);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Export the mesh as Wavefront OBJ format. This requires us to reduce all the
|
||||
// identical vertices to the same identifier, so do that first.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SolveSpace::ExportMeshAsObjTo(FILE *f, SMesh *sm) {
|
||||
SPointList spl;
|
||||
ZERO(&spl);
|
||||
STriangle *tr;
|
||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
||||
spl.IncrementTagFor(tr->a);
|
||||
spl.IncrementTagFor(tr->b);
|
||||
spl.IncrementTagFor(tr->c);
|
||||
}
|
||||
|
||||
// Output all the vertices.
|
||||
SPoint *sp;
|
||||
for(sp = spl.l.First(); sp; sp = spl.l.NextAfter(sp)) {
|
||||
fprintf(f, "v %.10f %.10f %.10f\r\n",
|
||||
sp->p.x / SS.exportScale,
|
||||
sp->p.y / SS.exportScale,
|
||||
sp->p.z / SS.exportScale);
|
||||
}
|
||||
|
||||
// And now all the triangular faces, in terms of those vertices. The
|
||||
// file format counts from 1, not 0.
|
||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
||||
fprintf(f, "f %d %d %d\r\n",
|
||||
spl.IndexForPoint(tr->a) + 1,
|
||||
spl.IndexForPoint(tr->b) + 1,
|
||||
spl.IndexForPoint(tr->c) + 1);
|
||||
}
|
||||
|
||||
spl.Clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Export a view of the model as an image; we just take a screenshot, by
|
||||
// rendering the view in the usual way and then copying the pixels.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SolveSpace::ExportAsPngTo(char *filename) {
|
||||
int w = (int)SS.GW.width, h = (int)SS.GW.height;
|
||||
// No guarantee that the back buffer contains anything valid right now,
|
||||
// so repaint the scene. And hide the toolbar too.
|
||||
int prevShowToolbar = SS.showToolbar;
|
||||
SS.showToolbar = false;
|
||||
SS.GW.Paint();
|
||||
SS.showToolbar = prevShowToolbar;
|
||||
|
||||
FILE *f = fopen(filename, "wb");
|
||||
if(!f) goto err;
|
||||
|
||||
png_struct *png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL);
|
||||
if(!png_ptr) goto err;
|
||||
|
||||
png_info *info_ptr = png_create_info_struct(png_ptr);
|
||||
if(!png_ptr) goto err;
|
||||
|
||||
if(setjmp(png_jmpbuf(png_ptr))) goto err;
|
||||
|
||||
png_init_io(png_ptr, f);
|
||||
|
||||
// glReadPixels wants to align things on 4-boundaries, and there's 3
|
||||
// bytes per pixel. As long as the row width is divisible by 4, all
|
||||
// works out.
|
||||
w &= ~3; h &= ~3;
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, w, h,
|
||||
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
// Get the pixel data from the framebuffer
|
||||
BYTE *pixels = (BYTE *)AllocTemporary(3*w*h);
|
||||
BYTE **rowptrs = (BYTE **)AllocTemporary(h*sizeof(BYTE *));
|
||||
glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
int y;
|
||||
for(y = 0; y < h; y++) {
|
||||
// gl puts the origin at lower left, but png puts it top left
|
||||
rowptrs[y] = pixels + ((h - 1) - y)*(3*w);
|
||||
}
|
||||
png_write_image(png_ptr, rowptrs);
|
||||
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
fclose(f);
|
||||
return;
|
||||
|
||||
err:
|
||||
Error("Error writing PNG file '%s'", filename);
|
||||
if(f) fclose(f);
|
||||
return;
|
||||
}
|
||||
|
363
exportstep.cpp
|
@ -1,363 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Export a STEP file describing our ratpoly shell.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
void StepFileWriter::WriteHeader(void) {
|
||||
fprintf(f,
|
||||
"ISO-10303-21;\n"
|
||||
"HEADER;\n"
|
||||
"\n"
|
||||
"FILE_DESCRIPTION((''), '2;1');\n"
|
||||
"\n"
|
||||
"FILE_NAME(\n"
|
||||
" 'output_file',\n"
|
||||
" '2009-06-07T17:44:47-07:00',\n"
|
||||
" (''),\n"
|
||||
" (''),\n"
|
||||
" 'SolveSpace',\n"
|
||||
" '',\n"
|
||||
" ''\n"
|
||||
");\n"
|
||||
"\n"
|
||||
"FILE_SCHEMA (('CONFIG_CONTROL_DESIGN'));\n"
|
||||
"ENDSEC;\n"
|
||||
"\n"
|
||||
"DATA;\n"
|
||||
"\n"
|
||||
"/**********************************************************\n"
|
||||
" * This defines the units and tolerances for the file. It\n"
|
||||
" * is always the same, independent of the actual data.\n"
|
||||
" **********************************************************/\n"
|
||||
"#158=(\n"
|
||||
"LENGTH_UNIT()\n"
|
||||
"NAMED_UNIT(*)\n"
|
||||
"SI_UNIT(.MILLI.,.METRE.)\n"
|
||||
");\n"
|
||||
"#161=(\n"
|
||||
"NAMED_UNIT(*)\n"
|
||||
"PLANE_ANGLE_UNIT()\n"
|
||||
"SI_UNIT($,.RADIAN.)\n"
|
||||
");\n"
|
||||
"#166=(\n"
|
||||
"NAMED_UNIT(*)\n"
|
||||
"SI_UNIT($,.STERADIAN.)\n"
|
||||
"SOLID_ANGLE_UNIT()\n"
|
||||
");\n"
|
||||
"#167=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.001),#158,\n"
|
||||
"'DISTANCE_ACCURACY_VALUE',\n"
|
||||
"'string');\n"
|
||||
"#168=(\n"
|
||||
"GEOMETRIC_REPRESENTATION_CONTEXT(3)\n"
|
||||
"GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#167))\n"
|
||||
"GLOBAL_UNIT_ASSIGNED_CONTEXT((#166,#161,#158))\n"
|
||||
"REPRESENTATION_CONTEXT('ID1','3D')\n"
|
||||
");\n"
|
||||
"#169=SHAPE_REPRESENTATION('',(#170),#168);\n"
|
||||
"#170=AXIS2_PLACEMENT_3D('',#173,#171,#172);\n"
|
||||
"#171=DIRECTION('',(0.,0.,1.));\n"
|
||||
"#172=DIRECTION('',(1.,0.,0.));\n"
|
||||
"#173=CARTESIAN_POINT('',(0.,0.,0.));\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
// Start the ID somewhere beyond the header IDs.
|
||||
id = 200;
|
||||
}
|
||||
|
||||
int StepFileWriter::ExportCurve(SBezier *sb) {
|
||||
int i, ret = id;
|
||||
|
||||
fprintf(f, "#%d=(\n", ret);
|
||||
fprintf(f, "BOUNDED_CURVE()\n");
|
||||
fprintf(f, "B_SPLINE_CURVE(%d,(", sb->deg);
|
||||
for(i = 0; i <= sb->deg; i++) {
|
||||
fprintf(f, "#%d", ret + i + 1);
|
||||
if(i != sb->deg) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, "),.UNSPECIFIED.,.F.,.F.)\n");
|
||||
fprintf(f, "B_SPLINE_CURVE_WITH_KNOTS((%d,%d),",
|
||||
(sb->deg + 1), (sb-> deg + 1));
|
||||
fprintf(f, "(0.000,1.000),.UNSPECIFIED.)\n");
|
||||
fprintf(f, "CURVE()\n");
|
||||
fprintf(f, "GEOMETRIC_REPRESENTATION_ITEM()\n");
|
||||
fprintf(f, "RATIONAL_B_SPLINE_CURVE((");
|
||||
for(i = 0; i <= sb->deg; i++) {
|
||||
fprintf(f, "%.10f", sb->weight[i]);
|
||||
if(i != sb->deg) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, "))\n");
|
||||
fprintf(f, "REPRESENTATION_ITEM('')\n);\n");
|
||||
|
||||
for(i = 0; i <= sb->deg; i++) {
|
||||
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
|
||||
id + 1 + i,
|
||||
CO(sb->ctrl[i]));
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
|
||||
id = ret + 1 + (sb->deg + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int StepFileWriter::ExportCurveLoop(SBezierLoop *loop, bool inner) {
|
||||
if(loop->l.n < 1) oops();
|
||||
|
||||
List<int> listOfTrims;
|
||||
ZERO(&listOfTrims);
|
||||
|
||||
SBezier *sb = &(loop->l.elem[loop->l.n - 1]);
|
||||
|
||||
// Generate "exactly closed" contours, with the same vertex id for the
|
||||
// finish of a previous edge and the start of the next one. So we need
|
||||
// the finish of the last Bezier in the loop before we start our process.
|
||||
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
|
||||
id, CO(sb->Finish()));
|
||||
fprintf(f, "#%d=VERTEX_POINT('',#%d);\n", id+1, id);
|
||||
int lastFinish = id + 1, prevFinish = lastFinish;
|
||||
id += 2;
|
||||
|
||||
for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) {
|
||||
int curveId = ExportCurve(sb);
|
||||
|
||||
int thisFinish;
|
||||
if(loop->l.NextAfter(sb) != NULL) {
|
||||
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
|
||||
id, CO(sb->Finish()));
|
||||
fprintf(f, "#%d=VERTEX_POINT('',#%d);\n", id+1, id);
|
||||
thisFinish = id + 1;
|
||||
id += 2;
|
||||
} else {
|
||||
thisFinish = lastFinish;
|
||||
}
|
||||
|
||||
fprintf(f, "#%d=EDGE_CURVE('',#%d,#%d,#%d,%s);\n",
|
||||
id, prevFinish, thisFinish, curveId, ".T.");
|
||||
fprintf(f, "#%d=ORIENTED_EDGE('',*,*,#%d,.T.);\n",
|
||||
id+1, id);
|
||||
|
||||
int oe = id+1;
|
||||
listOfTrims.Add(&oe);
|
||||
id += 2;
|
||||
|
||||
prevFinish = thisFinish;
|
||||
}
|
||||
|
||||
fprintf(f, "#%d=EDGE_LOOP('',(", id);
|
||||
int *oe;
|
||||
for(oe = listOfTrims.First(); oe; oe = listOfTrims.NextAfter(oe)) {
|
||||
fprintf(f, "#%d", *oe);
|
||||
if(listOfTrims.NextAfter(oe) != NULL) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, "));\n");
|
||||
|
||||
int fb = id + 1;
|
||||
fprintf(f, "#%d=%s('',#%d,.T.);\n",
|
||||
fb, inner ? "FACE_BOUND" : "FACE_OUTER_BOUND", id);
|
||||
|
||||
id += 2;
|
||||
listOfTrims.Clear();
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
void StepFileWriter::ExportSurface(SSurface *ss, SBezierList *sbl) {
|
||||
int i, j, srfid = id;
|
||||
|
||||
// First, we create the untrimmed surface. We always specify a rational
|
||||
// B-spline surface (in fact, just a Bezier surface).
|
||||
fprintf(f, "#%d=(\n", srfid);
|
||||
fprintf(f, "BOUNDED_SURFACE()\n");
|
||||
fprintf(f, "B_SPLINE_SURFACE(%d,%d,(", ss->degm, ss->degn);
|
||||
for(i = 0; i <= ss->degm; i++) {
|
||||
fprintf(f, "(");
|
||||
for(j = 0; j <= ss->degn; j++) {
|
||||
fprintf(f, "#%d", srfid + 1 + j + i*(ss->degn + 1));
|
||||
if(j != ss->degn) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, ")");
|
||||
if(i != ss->degm) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, "),.UNSPECIFIED.,.F.,.F.,.F.)\n");
|
||||
fprintf(f, "B_SPLINE_SURFACE_WITH_KNOTS((%d,%d),(%d,%d),",
|
||||
(ss->degm + 1), (ss->degm + 1),
|
||||
(ss->degn + 1), (ss->degn + 1));
|
||||
fprintf(f, "(0.000,1.000),(0.000,1.000),.UNSPECIFIED.)\n");
|
||||
fprintf(f, "GEOMETRIC_REPRESENTATION_ITEM()\n");
|
||||
fprintf(f, "RATIONAL_B_SPLINE_SURFACE((");
|
||||
for(i = 0; i <= ss->degm; i++) {
|
||||
fprintf(f, "(");
|
||||
for(j = 0; j <= ss->degn; j++) {
|
||||
fprintf(f, "%.10f", ss->weight[i][j]);
|
||||
if(j != ss->degn) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, ")");
|
||||
if(i != ss->degm) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, "))\n");
|
||||
fprintf(f, "REPRESENTATION_ITEM('')\n");
|
||||
fprintf(f, "SURFACE()\n");
|
||||
fprintf(f, ");\n");
|
||||
|
||||
// The control points for the untrimmed surface.
|
||||
for(i = 0; i <= ss->degm; i++) {
|
||||
for(j = 0; j <= ss->degn; j++) {
|
||||
fprintf(f, "#%d=CARTESIAN_POINT('',(%.10f,%.10f,%.10f));\n",
|
||||
srfid + 1 + j + i*(ss->degn + 1),
|
||||
CO(ss->ctrl[i][j]));
|
||||
}
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
|
||||
id = srfid + 1 + (ss->degm + 1)*(ss->degn + 1);
|
||||
|
||||
// Now we do the trim curves. We must group each outer loop separately
|
||||
// along with its inner faces, so do that now.
|
||||
SBezierLoopSetSet sblss;
|
||||
ZERO(&sblss);
|
||||
SPolygon spxyz;
|
||||
ZERO(&spxyz);
|
||||
bool allClosed;
|
||||
SEdge notClosedAt;
|
||||
// We specify a surface, so it doesn't check for coplanarity; and we
|
||||
// don't want it to give us any open contours. The polygon and chord
|
||||
// tolerance are required, because they are used to calculate the
|
||||
// contour directions and determine inner vs. outer contours.
|
||||
sblss.FindOuterFacesFrom(sbl, &spxyz, ss,
|
||||
SS.ChordTolMm() / SS.exportScale,
|
||||
&allClosed, ¬ClosedAt,
|
||||
NULL, NULL,
|
||||
NULL);
|
||||
|
||||
// So in our list of SBezierLoopSet, each set contains at least one loop
|
||||
// (the outer boundary), plus any inner loops associated with that outer
|
||||
// loop.
|
||||
SBezierLoopSet *sbls;
|
||||
for(sbls = sblss.l.First(); sbls; sbls = sblss.l.NextAfter(sbls)) {
|
||||
SBezierLoop *loop = sbls->l.First();
|
||||
|
||||
List<int> listOfLoops;
|
||||
ZERO(&listOfLoops);
|
||||
// Create the face outer boundary from the outer loop.
|
||||
int fob = ExportCurveLoop(loop, false);
|
||||
listOfLoops.Add(&fob);
|
||||
|
||||
// And create the face inner boundaries from any inner loops that
|
||||
// lie within this contour.
|
||||
loop = sbls->l.NextAfter(loop);
|
||||
for(; loop; loop = sbls->l.NextAfter(loop)) {
|
||||
int fib = ExportCurveLoop(loop, true);
|
||||
listOfLoops.Add(&fib);
|
||||
}
|
||||
|
||||
// And now create the face that corresponds to this outer loop
|
||||
// and all of its holes.
|
||||
int advFaceId = id;
|
||||
fprintf(f, "#%d=ADVANCED_FACE('',(", advFaceId);
|
||||
int *fb;
|
||||
for(fb = listOfLoops.First(); fb; fb = listOfLoops.NextAfter(fb)) {
|
||||
fprintf(f, "#%d", *fb);
|
||||
if(listOfLoops.NextAfter(fb) != NULL) fprintf(f, ",");
|
||||
}
|
||||
|
||||
fprintf(f, "),#%d,.T.);\n", srfid);
|
||||
fprintf(f, "\n");
|
||||
advancedFaces.Add(&advFaceId);
|
||||
|
||||
id++;
|
||||
listOfLoops.Clear();
|
||||
}
|
||||
sblss.Clear();
|
||||
spxyz.Clear();
|
||||
}
|
||||
|
||||
void StepFileWriter::WriteFooter(void) {
|
||||
fprintf(f,
|
||||
"\n"
|
||||
"ENDSEC;\n"
|
||||
"\n"
|
||||
"END-ISO-10303-21;\n"
|
||||
);
|
||||
}
|
||||
|
||||
void StepFileWriter::ExportSurfacesTo(char *file) {
|
||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
||||
SShell *shell = &(g->runningShell);
|
||||
|
||||
if(shell->surface.n == 0) {
|
||||
Error("The model does not contain any surfaces to export.%s",
|
||||
g->runningMesh.l.n > 0 ?
|
||||
"\n\nThe model does contain triangles from a mesh, but "
|
||||
"a triangle mesh cannot be exported as a STEP file. Try "
|
||||
"File -> Export Mesh... instead." : "");
|
||||
return;
|
||||
}
|
||||
|
||||
f = fopen(file, "wb");
|
||||
if(!f) {
|
||||
Error("Couldn't write to '%s'", file);
|
||||
return;
|
||||
}
|
||||
|
||||
WriteHeader();
|
||||
|
||||
ZERO(&advancedFaces);
|
||||
|
||||
SSurface *ss;
|
||||
for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) {
|
||||
if(ss->trim.n == 0) continue;
|
||||
|
||||
// Get all of the loops of Beziers that trim our surface (with each
|
||||
// Bezier split so that we use the section as t goes from 0 to 1), and
|
||||
// the piecewise linearization of those loops in xyz space.
|
||||
SBezierList sbl;
|
||||
ZERO(&sbl);
|
||||
ss->MakeSectionEdgesInto(shell, NULL, &sbl);
|
||||
|
||||
// Apply the export scale factor.
|
||||
ss->ScaleSelfBy(1.0/SS.exportScale);
|
||||
sbl.ScaleSelfBy(1.0/SS.exportScale);
|
||||
|
||||
ExportSurface(ss, &sbl);
|
||||
|
||||
sbl.Clear();
|
||||
}
|
||||
|
||||
fprintf(f, "#%d=CLOSED_SHELL('',(", id);
|
||||
int *af;
|
||||
for(af = advancedFaces.First(); af; af = advancedFaces.NextAfter(af)) {
|
||||
fprintf(f, "#%d", *af);
|
||||
if(advancedFaces.NextAfter(af) != NULL) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, "));\n");
|
||||
fprintf(f, "#%d=MANIFOLD_SOLID_BREP('brep',#%d);\n", id+1, id);
|
||||
fprintf(f, "#%d=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#%d,#170),#168);\n",
|
||||
id+2, id+1);
|
||||
fprintf(f, "#%d=SHAPE_REPRESENTATION_RELATIONSHIP($,$,#169,#%d);\n",
|
||||
id+3, id+2);
|
||||
|
||||
WriteFooter();
|
||||
|
||||
fclose(f);
|
||||
advancedFaces.Clear();
|
||||
}
|
||||
|
||||
void StepFileWriter::WriteWireframe(void) {
|
||||
fprintf(f, "#%d=GEOMETRIC_CURVE_SET('curves',(", id);
|
||||
int *c;
|
||||
for(c = curves.First(); c; c = curves.NextAfter(c)) {
|
||||
fprintf(f, "#%d", *c);
|
||||
if(curves.NextAfter(c) != NULL) fprintf(f, ",");
|
||||
}
|
||||
fprintf(f, "));\n");
|
||||
fprintf(f, "#%d=GEOMETRICALLY_BOUNDED_WIREFRAME_SHAPE_REPRESENTATION"
|
||||
"('',(#%d,#170),#168);\n", id+1, id);
|
||||
fprintf(f, "#%d=SHAPE_REPRESENTATION_RELATIONSHIP($,$,#169,#%d);\n",
|
||||
id+2, id+1);
|
||||
|
||||
id += 3;
|
||||
curves.Clear();
|
||||
}
|
||||
|
713
exportvector.cpp
|
@ -1,713 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// The file format-specific stuff for all of the 2d vector output formats.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Routines for DXF export
|
||||
//-----------------------------------------------------------------------------
|
||||
void DxfFileWriter::StartFile(void) {
|
||||
// Some software, like Adobe Illustrator, insists on a header.
|
||||
fprintf(f,
|
||||
" 999\r\n"
|
||||
"file created by SolveSpace\r\n"
|
||||
" 0\r\n"
|
||||
"SECTION\r\n"
|
||||
" 2\r\n"
|
||||
"HEADER\r\n"
|
||||
" 9\r\n"
|
||||
"$ACADVER\r\n"
|
||||
" 1\r\n"
|
||||
"AC1006\r\n"
|
||||
" 9\r\n"
|
||||
"$ANGDIR\r\n"
|
||||
" 70\r\n"
|
||||
"0\r\n"
|
||||
" 9\r\n"
|
||||
"$AUNITS\r\n"
|
||||
" 70\r\n"
|
||||
"0\r\n"
|
||||
" 9\r\n"
|
||||
"$AUPREC\r\n"
|
||||
" 70\r\n"
|
||||
"0\r\n"
|
||||
" 9\r\n"
|
||||
"$INSBASE\r\n"
|
||||
" 10\r\n"
|
||||
"0.0\r\n"
|
||||
" 20\r\n"
|
||||
"0.0\r\n"
|
||||
" 30\r\n"
|
||||
"0.0\r\n"
|
||||
" 9\r\n"
|
||||
"$EXTMIN\r\n"
|
||||
" 10\r\n"
|
||||
"0.0\r\n"
|
||||
" 20\r\n"
|
||||
"0.0\r\n"
|
||||
" 9\r\n"
|
||||
"$EXTMAX\r\n"
|
||||
" 10\r\n"
|
||||
"10000.0\r\n"
|
||||
" 20\r\n"
|
||||
"10000.0\r\n"
|
||||
" 0\r\n"
|
||||
"ENDSEC\r\n");
|
||||
|
||||
// Then start the entities.
|
||||
fprintf(f,
|
||||
" 0\r\n"
|
||||
"SECTION\r\n"
|
||||
" 2\r\n"
|
||||
"ENTITIES\r\n");
|
||||
}
|
||||
|
||||
void DxfFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
}
|
||||
void DxfFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
}
|
||||
|
||||
void DxfFileWriter::Triangle(STriangle *tr) {
|
||||
}
|
||||
|
||||
void DxfFileWriter::Bezier(SBezier *sb) {
|
||||
Vector c, n = Vector::From(0, 0, 1);
|
||||
double r;
|
||||
if(sb->deg == 1) {
|
||||
fprintf(f,
|
||||
" 0\r\n"
|
||||
"LINE\r\n"
|
||||
" 8\r\n" // Layer code
|
||||
"%d\r\n"
|
||||
" 10\r\n" // xA
|
||||
"%.6f\r\n"
|
||||
" 20\r\n" // yA
|
||||
"%.6f\r\n"
|
||||
" 30\r\n" // zA
|
||||
"%.6f\r\n"
|
||||
" 11\r\n" // xB
|
||||
"%.6f\r\n"
|
||||
" 21\r\n" // yB
|
||||
"%.6f\r\n"
|
||||
" 31\r\n" // zB
|
||||
"%.6f\r\n",
|
||||
0,
|
||||
sb->ctrl[0].x, sb->ctrl[0].y, sb->ctrl[0].z,
|
||||
sb->ctrl[1].x, sb->ctrl[1].y, sb->ctrl[1].z);
|
||||
} else if(sb->IsInPlane(n, 0) && sb->IsCircle(n, &c, &r)) {
|
||||
double theta0 = atan2(sb->ctrl[0].y - c.y, sb->ctrl[0].x - c.x),
|
||||
theta1 = atan2(sb->ctrl[2].y - c.y, sb->ctrl[2].x - c.x),
|
||||
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
||||
if(dtheta < 0) {
|
||||
SWAP(double, theta0, theta1);
|
||||
}
|
||||
|
||||
fprintf(f,
|
||||
" 0\r\n"
|
||||
"ARC\r\n"
|
||||
" 8\r\n" // Layer code
|
||||
"%d\r\n"
|
||||
" 10\r\n" // x
|
||||
"%.6f\r\n"
|
||||
" 20\r\n" // y
|
||||
"%.6f\r\n"
|
||||
" 30\r\n" // z
|
||||
"%.6f\r\n"
|
||||
" 40\r\n" // radius
|
||||
"%.6f\r\n"
|
||||
" 50\r\n" // start angle
|
||||
"%.6f\r\n"
|
||||
" 51\r\n" // end angle
|
||||
"%.6f\r\n",
|
||||
0,
|
||||
c.x, c.y, 0.0,
|
||||
r,
|
||||
theta0*180/PI, theta1*180/PI);
|
||||
} else {
|
||||
BezierAsPwl(sb);
|
||||
}
|
||||
}
|
||||
|
||||
void DxfFileWriter::FinishAndCloseFile(void) {
|
||||
fprintf(f,
|
||||
" 0\r\n"
|
||||
"ENDSEC\r\n"
|
||||
" 0\r\n"
|
||||
"EOF\r\n" );
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Routines for EPS output
|
||||
//-----------------------------------------------------------------------------
|
||||
void EpsFileWriter::StartFile(void) {
|
||||
fprintf(f,
|
||||
"%%!PS-Adobe-2.0\r\n"
|
||||
"%%%%Creator: SolveSpace\r\n"
|
||||
"%%%%Title: title\r\n"
|
||||
"%%%%Pages: 0\r\n"
|
||||
"%%%%PageOrder: Ascend\r\n"
|
||||
"%%%%BoundingBox: 0 0 %d %d\r\n"
|
||||
"%%%%HiResBoundingBox: 0 0 %.3f %.3f\r\n"
|
||||
"%%%%EndComments\r\n"
|
||||
"\r\n"
|
||||
"gsave\r\n"
|
||||
"\r\n",
|
||||
(int)ceil(MmToPts(ptMax.x - ptMin.x)),
|
||||
(int)ceil(MmToPts(ptMax.y - ptMin.y)),
|
||||
MmToPts(ptMax.x - ptMin.x),
|
||||
MmToPts(ptMax.y - ptMin.y));
|
||||
}
|
||||
|
||||
void EpsFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
fprintf(f, "newpath\r\n");
|
||||
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
||||
}
|
||||
void EpsFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
fprintf(f, " %.3f setlinewidth\r\n"
|
||||
" %.3f %.3f %.3f setrgbcolor\r\n"
|
||||
" 1 setlinejoin\r\n" // rounded
|
||||
" 1 setlinecap\r\n" // rounded
|
||||
" gsave stroke grestore\r\n",
|
||||
MmToPts(lineWidth),
|
||||
REDf(strokeRgb), GREENf(strokeRgb), BLUEf(strokeRgb));
|
||||
if(filled) {
|
||||
fprintf(f, " %.3f %.3f %.3f setrgbcolor\r\n"
|
||||
" gsave fill grestore\r\n",
|
||||
REDf(fillRgb), GREENf(fillRgb), BLUEf(fillRgb));
|
||||
}
|
||||
}
|
||||
|
||||
void EpsFileWriter::MaybeMoveTo(Vector st, Vector fi) {
|
||||
if(!prevPt.Equals(st)) {
|
||||
fprintf(f, " %.3f %.3f moveto\r\n",
|
||||
MmToPts(st.x - ptMin.x), MmToPts(st.y - ptMin.y));
|
||||
}
|
||||
prevPt = fi;
|
||||
}
|
||||
|
||||
void EpsFileWriter::Triangle(STriangle *tr) {
|
||||
fprintf(f,
|
||||
"%.3f %.3f %.3f setrgbcolor\r\n"
|
||||
"newpath\r\n"
|
||||
" %.3f %.3f moveto\r\n"
|
||||
" %.3f %.3f lineto\r\n"
|
||||
" %.3f %.3f lineto\r\n"
|
||||
" closepath\r\n"
|
||||
"gsave fill grestore\r\n",
|
||||
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
|
||||
MmToPts(tr->a.x - ptMin.x), MmToPts(tr->a.y - ptMin.y),
|
||||
MmToPts(tr->b.x - ptMin.x), MmToPts(tr->b.y - ptMin.y),
|
||||
MmToPts(tr->c.x - ptMin.x), MmToPts(tr->c.y - ptMin.y));
|
||||
|
||||
// same issue with cracks, stroke it to avoid them
|
||||
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
|
||||
fprintf(f,
|
||||
"1 setlinejoin\r\n"
|
||||
"1 setlinecap\r\n"
|
||||
"%.3f setlinewidth\r\n"
|
||||
"gsave stroke grestore\r\n",
|
||||
MmToPts(sw));
|
||||
}
|
||||
|
||||
void EpsFileWriter::Bezier(SBezier *sb) {
|
||||
Vector c, n = Vector::From(0, 0, 1);
|
||||
double r;
|
||||
if(sb->deg == 1) {
|
||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
|
||||
fprintf(f, " %.3f %.3f lineto\r\n",
|
||||
MmToPts(sb->ctrl[1].x - ptMin.x),
|
||||
MmToPts(sb->ctrl[1].y - ptMin.y));
|
||||
} else if(sb->IsCircle(n, &c, &r)) {
|
||||
Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2];
|
||||
double theta0 = atan2(p0.y - c.y, p0.x - c.x),
|
||||
theta1 = atan2(p1.y - c.y, p1.x - c.x),
|
||||
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
||||
MaybeMoveTo(p0, p1);
|
||||
fprintf(f,
|
||||
" %.3f %.3f %.3f %.3f %.3f %s\r\n",
|
||||
MmToPts(c.x - ptMin.x), MmToPts(c.y - ptMin.y),
|
||||
MmToPts(r),
|
||||
theta0*180/PI, theta1*180/PI,
|
||||
dtheta < 0 ? "arcn" : "arc");
|
||||
} else if(sb->deg == 3 && !sb->IsRational()) {
|
||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
|
||||
fprintf(f,
|
||||
" %.3f %.3f %.3f %.3f %.3f %.3f curveto\r\n",
|
||||
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y),
|
||||
MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y),
|
||||
MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y));
|
||||
} else {
|
||||
BezierAsNonrationalCubic(sb);
|
||||
}
|
||||
}
|
||||
|
||||
void EpsFileWriter::FinishAndCloseFile(void) {
|
||||
fprintf(f,
|
||||
"\r\n"
|
||||
"grestore\r\n"
|
||||
"\r\n");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Routines for PDF output, some extra complexity because we have to generate
|
||||
// a correct xref table.
|
||||
//-----------------------------------------------------------------------------
|
||||
void PdfFileWriter::StartFile(void) {
|
||||
if((ptMax.x - ptMin.x) > 200*25.4 ||
|
||||
(ptMax.y - ptMin.y) > 200*25.4)
|
||||
{
|
||||
Message("PDF page size exceeds 200 by 200 inches; many viewers may "
|
||||
"reject this file.");
|
||||
}
|
||||
|
||||
fprintf(f,
|
||||
"%%PDF-1.1\r\n"
|
||||
"%%%c%c%c%c\r\n",
|
||||
0xe2, 0xe3, 0xcf, 0xd3);
|
||||
|
||||
xref[1] = ftell(f);
|
||||
fprintf(f,
|
||||
"1 0 obj\r\n"
|
||||
" << /Type /Catalog\r\n"
|
||||
" /Outlines 2 0 R\r\n"
|
||||
" /Pages 3 0 R\r\n"
|
||||
" >>\r\n"
|
||||
"endobj\r\n");
|
||||
|
||||
xref[2] = ftell(f);
|
||||
fprintf(f,
|
||||
"2 0 obj\r\n"
|
||||
" << /Type /Outlines\r\n"
|
||||
" /Count 0\r\n"
|
||||
" >>\r\n"
|
||||
"endobj\r\n");
|
||||
|
||||
xref[3] = ftell(f);
|
||||
fprintf(f,
|
||||
"3 0 obj\r\n"
|
||||
" << /Type /Pages\r\n"
|
||||
" /Kids [4 0 R]\r\n"
|
||||
" /Count 1\r\n"
|
||||
" >>\r\n"
|
||||
"endobj\r\n");
|
||||
|
||||
xref[4] = ftell(f);
|
||||
fprintf(f,
|
||||
"4 0 obj\r\n"
|
||||
" << /Type /Page\r\n"
|
||||
" /Parent 3 0 R\r\n"
|
||||
" /MediaBox [0 0 %.3f %.3f]\r\n"
|
||||
" /Contents 5 0 R\r\n"
|
||||
" /Resources << /ProcSet 7 0 R\r\n"
|
||||
" /Font << /F1 8 0 R >>\r\n"
|
||||
" >>\r\n"
|
||||
" >>\r\n"
|
||||
"endobj\r\n",
|
||||
MmToPts(ptMax.x - ptMin.x),
|
||||
MmToPts(ptMax.y - ptMin.y));
|
||||
|
||||
xref[5] = ftell(f);
|
||||
fprintf(f,
|
||||
"5 0 obj\r\n"
|
||||
" << /Length 6 0 R >>\r\n"
|
||||
"stream\r\n");
|
||||
bodyStart = ftell(f);
|
||||
}
|
||||
|
||||
void PdfFileWriter::FinishAndCloseFile(void) {
|
||||
DWORD bodyEnd = ftell(f);
|
||||
|
||||
fprintf(f,
|
||||
"endstream\r\n"
|
||||
"endobj\r\n");
|
||||
|
||||
xref[6] = ftell(f);
|
||||
fprintf(f,
|
||||
"6 0 obj\r\n"
|
||||
" %d\r\n"
|
||||
"endobj\r\n",
|
||||
bodyEnd - bodyStart);
|
||||
|
||||
xref[7] = ftell(f);
|
||||
fprintf(f,
|
||||
"7 0 obj\r\n"
|
||||
" [/PDF /Text]\r\n"
|
||||
"endobj\r\n");
|
||||
|
||||
xref[8] = ftell(f);
|
||||
fprintf(f,
|
||||
"8 0 obj\r\n"
|
||||
" << /Type /Font\r\n"
|
||||
" /Subtype /Type1\r\n"
|
||||
" /Name /F1\r\n"
|
||||
" /BaseFont /Helvetica\r\n"
|
||||
" /Encoding /WinAnsiEncoding\r\n"
|
||||
" >>\r\n"
|
||||
"endobj\r\n");
|
||||
|
||||
xref[9] = ftell(f);
|
||||
fprintf(f,
|
||||
"9 0 obj\r\n"
|
||||
" << /Creator (SolveSpace)\r\n"
|
||||
" >>\r\n");
|
||||
|
||||
DWORD xrefStart = ftell(f);
|
||||
fprintf(f,
|
||||
"xref\r\n"
|
||||
"0 10\r\n"
|
||||
"0000000000 65535 f\r\n");
|
||||
|
||||
int i;
|
||||
for(i = 1; i <= 9; i++) {
|
||||
fprintf(f, "%010d %05d n\r\n", xref[i], 0);
|
||||
}
|
||||
|
||||
fprintf(f,
|
||||
"\r\n"
|
||||
"trailer\r\n"
|
||||
" << /Size 10\r\n"
|
||||
" /Root 1 0 R\r\n"
|
||||
" /Info 9 0 R\r\n"
|
||||
" >>\r\n"
|
||||
"startxref\r\n"
|
||||
"%d\r\n"
|
||||
"%%%%EOF\r\n",
|
||||
xrefStart);
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
void PdfFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
fprintf(f, "1 J 1 j " // round endcaps and joins
|
||||
"%.3f w "
|
||||
"%.3f %.3f %.3f RG\r\n",
|
||||
MmToPts(lineWidth),
|
||||
REDf(strokeRgb), GREENf(strokeRgb), BLUEf(strokeRgb));
|
||||
if(filled) {
|
||||
fprintf(f, "%.3f %.3f %.3f rg\r\n",
|
||||
REDf(fillRgb), GREENf(fillRgb), BLUEf(fillRgb));
|
||||
}
|
||||
|
||||
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
||||
}
|
||||
void PdfFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
if(filled) {
|
||||
fprintf(f, "b\r\n");
|
||||
} else {
|
||||
fprintf(f, "S\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
void PdfFileWriter::MaybeMoveTo(Vector st, Vector fi) {
|
||||
if(!prevPt.Equals(st)) {
|
||||
fprintf(f, "%.3f %.3f m\r\n",
|
||||
MmToPts(st.x - ptMin.x), MmToPts(st.y - ptMin.y));
|
||||
}
|
||||
prevPt = fi;
|
||||
}
|
||||
|
||||
void PdfFileWriter::Triangle(STriangle *tr) {
|
||||
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
|
||||
|
||||
fprintf(f,
|
||||
"1 J 1 j\r\n"
|
||||
"%.3f %.3f %.3f RG\r\n"
|
||||
"%.3f %.3f %.3f rg\r\n"
|
||||
"%.3f w\r\n"
|
||||
"%.3f %.3f m\r\n"
|
||||
"%.3f %.3f l\r\n"
|
||||
"%.3f %.3f l\r\n"
|
||||
"b\r\n",
|
||||
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
|
||||
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
|
||||
MmToPts(sw),
|
||||
MmToPts(tr->a.x - ptMin.x), MmToPts(tr->a.y - ptMin.y),
|
||||
MmToPts(tr->b.x - ptMin.x), MmToPts(tr->b.y - ptMin.y),
|
||||
MmToPts(tr->c.x - ptMin.x), MmToPts(tr->c.y - ptMin.y));
|
||||
}
|
||||
|
||||
void PdfFileWriter::Bezier(SBezier *sb) {
|
||||
if(sb->deg == 1) {
|
||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
|
||||
fprintf(f,
|
||||
"%.3f %.3f l\r\n",
|
||||
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y));
|
||||
} else if(sb->deg == 3 && !sb->IsRational()) {
|
||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
|
||||
fprintf(f,
|
||||
"%.3f %.3f %.3f %.3f %.3f %.3f c\r\n",
|
||||
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y),
|
||||
MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y),
|
||||
MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y));
|
||||
} else {
|
||||
BezierAsNonrationalCubic(sb);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Routines for SVG output
|
||||
//-----------------------------------------------------------------------------
|
||||
void SvgFileWriter::StartFile(void) {
|
||||
fprintf(f,
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" "
|
||||
"\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\r\n"
|
||||
"<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
||||
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
|
||||
"width='%.3fmm' height='%.3fmm' "
|
||||
"viewBox=\"0 0 %.3f %.3f\">\r\n"
|
||||
"\r\n"
|
||||
"<title>Exported SVG</title>\r\n"
|
||||
"\r\n",
|
||||
(ptMax.x - ptMin.x) + 1, (ptMax.y - ptMin.y) + 1,
|
||||
(ptMax.x - ptMin.x) + 1, (ptMax.y - ptMin.y) + 1);
|
||||
// A little bit of extra space for the stroke width.
|
||||
}
|
||||
|
||||
void SvgFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
fprintf(f, "<path d='");
|
||||
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
||||
}
|
||||
void SvgFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
char fill[100];
|
||||
if(filled) {
|
||||
sprintf(fill, "#%02x%02x%02x",
|
||||
RED(fillRgb), GREEN(fillRgb), BLUE(fillRgb));
|
||||
} else {
|
||||
strcpy(fill, "none");
|
||||
}
|
||||
fprintf(f, "' stroke-width='%.3f' stroke='#%02x%02x%02x' "
|
||||
"stroke-linecap='round' stroke-linejoin='round' "
|
||||
"fill='%s' />\r\n",
|
||||
lineWidth, RED(strokeRgb), GREEN(strokeRgb), BLUE(strokeRgb),
|
||||
fill);
|
||||
}
|
||||
|
||||
void SvgFileWriter::MaybeMoveTo(Vector st, Vector fi) {
|
||||
// SVG uses a coordinate system with the origin at top left, +y down
|
||||
if(!prevPt.Equals(st)) {
|
||||
fprintf(f, "M%.3f %.3f ", (st.x - ptMin.x), (ptMax.y - st.y));
|
||||
}
|
||||
prevPt = fi;
|
||||
}
|
||||
|
||||
void SvgFileWriter::Triangle(STriangle *tr) {
|
||||
// crispEdges turns of anti-aliasing, which tends to cause hairline
|
||||
// cracks between triangles; but there still is some cracking, so
|
||||
// specify a stroke width too, hope for around a pixel
|
||||
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
|
||||
fprintf(f,
|
||||
"<polygon points='%.3f,%.3f %.3f,%.3f %.3f,%.3f' "
|
||||
"stroke='#%02x%02x%02x' stroke-width='%.3f' "
|
||||
"style='fill:#%02x%02x%02x' shape-rendering='crispEdges'/>\r\n",
|
||||
(tr->a.x - ptMin.x), (ptMax.y - tr->a.y),
|
||||
(tr->b.x - ptMin.x), (ptMax.y - tr->b.y),
|
||||
(tr->c.x - ptMin.x), (ptMax.y - tr->c.y),
|
||||
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color),
|
||||
sw,
|
||||
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color));
|
||||
}
|
||||
|
||||
void SvgFileWriter::Bezier(SBezier *sb) {
|
||||
Vector c, n = Vector::From(0, 0, 1);
|
||||
double r;
|
||||
if(sb->deg == 1) {
|
||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
|
||||
fprintf(f, "L%.3f,%.3f ",
|
||||
(sb->ctrl[1].x - ptMin.x), (ptMax.y - sb->ctrl[1].y));
|
||||
} else if(sb->IsCircle(n, &c, &r)) {
|
||||
Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2];
|
||||
double theta0 = atan2(p0.y - c.y, p0.x - c.x),
|
||||
theta1 = atan2(p1.y - c.y, p1.x - c.x),
|
||||
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
||||
// The arc must be less than 180 degrees, or else it couldn't have
|
||||
// been represented as a single rational Bezier. So large-arc-flag
|
||||
// must be false. sweep-flag is determined by the sign of dtheta.
|
||||
// Note that clockwise and counter-clockwise are backwards in SVG's
|
||||
// mirrored csys.
|
||||
MaybeMoveTo(p0, p1);
|
||||
fprintf(f, "A%.3f,%.3f 0 0,%d %.3f,%.3f ",
|
||||
r, r,
|
||||
(dtheta < 0) ? 1 : 0,
|
||||
p1.x - ptMin.x, ptMax.y - p1.y);
|
||||
} else if(!sb->IsRational()) {
|
||||
if(sb->deg == 2) {
|
||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[2]);
|
||||
fprintf(f, "Q%.3f,%.3f %.3f,%.3f ",
|
||||
sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y,
|
||||
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y);
|
||||
} else if(sb->deg == 3) {
|
||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
|
||||
fprintf(f, "C%.3f,%.3f %.3f,%.3f %.3f,%.3f ",
|
||||
sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y,
|
||||
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y,
|
||||
sb->ctrl[3].x - ptMin.x, ptMax.y - sb->ctrl[3].y);
|
||||
}
|
||||
} else {
|
||||
BezierAsNonrationalCubic(sb);
|
||||
}
|
||||
}
|
||||
|
||||
void SvgFileWriter::FinishAndCloseFile(void) {
|
||||
fprintf(f, "\r\n</svg>\r\n");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Routines for HPGL output
|
||||
//-----------------------------------------------------------------------------
|
||||
double HpglFileWriter::MmToHpglUnits(double mm) {
|
||||
return mm*40;
|
||||
}
|
||||
|
||||
void HpglFileWriter::StartFile(void) {
|
||||
fprintf(f, "IN;\r\n");
|
||||
fprintf(f, "SP1;\r\n");
|
||||
}
|
||||
|
||||
void HpglFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
}
|
||||
void HpglFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
}
|
||||
|
||||
void HpglFileWriter::Triangle(STriangle *tr) {
|
||||
}
|
||||
|
||||
void HpglFileWriter::Bezier(SBezier *sb) {
|
||||
if(sb->deg == 1) {
|
||||
fprintf(f, "PU%d,%d;\r\n",
|
||||
(int)MmToHpglUnits(sb->ctrl[0].x),
|
||||
(int)MmToHpglUnits(sb->ctrl[0].y));
|
||||
fprintf(f, "PD%d,%d;\r\n",
|
||||
(int)MmToHpglUnits(sb->ctrl[1].x),
|
||||
(int)MmToHpglUnits(sb->ctrl[1].y));
|
||||
} else {
|
||||
BezierAsPwl(sb);
|
||||
}
|
||||
}
|
||||
|
||||
void HpglFileWriter::FinishAndCloseFile(void) {
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Routines for G Code output. Slightly complicated by our ability to generate
|
||||
// multiple passes, and to specify the feeds and depth; those parameters get
|
||||
// set in the configuration screen.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GCodeFileWriter::StartFile(void) {
|
||||
ZERO(&sel);
|
||||
}
|
||||
void GCodeFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
}
|
||||
void GCodeFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
}
|
||||
void GCodeFileWriter::Triangle(STriangle *tr) {
|
||||
}
|
||||
|
||||
void GCodeFileWriter::Bezier(SBezier *sb) {
|
||||
if(sb->deg == 1) {
|
||||
sel.AddEdge(sb->ctrl[0], sb->ctrl[1]);
|
||||
} else {
|
||||
BezierAsPwl(sb);
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeFileWriter::FinishAndCloseFile(void) {
|
||||
SPolygon sp;
|
||||
ZERO(&sp);
|
||||
sel.AssemblePolygon(&sp, NULL);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < SS.gCode.passes; i++) {
|
||||
double depth = (SS.gCode.depth / SS.gCode.passes)*(i+1);
|
||||
|
||||
SContour *sc;
|
||||
for(sc = sp.l.First(); sc; sc = sp.l.NextAfter(sc)) {
|
||||
if(sc->l.n < 2) continue;
|
||||
|
||||
SPoint *pt = sc->l.First();
|
||||
fprintf(f, "G00 X%s Y%s\r\n",
|
||||
SS.MmToString(pt->p.x), SS.MmToString(pt->p.y));
|
||||
fprintf(f, "G01 Z%s F%s\r\n",
|
||||
SS.MmToString(depth), SS.MmToString(SS.gCode.plungeFeed));
|
||||
|
||||
pt = sc->l.NextAfter(pt);
|
||||
for(; pt; pt = sc->l.NextAfter(pt)) {
|
||||
fprintf(f, "G01 X%s Y%s F%s\r\n",
|
||||
SS.MmToString(pt->p.x), SS.MmToString(pt->p.y),
|
||||
SS.MmToString(SS.gCode.feed));
|
||||
}
|
||||
// Move up to a clearance plane 5mm above the work.
|
||||
fprintf(f, "G00 Z%s\r\n",
|
||||
SS.MmToString(SS.gCode.depth < 0 ? +5 : -5));
|
||||
}
|
||||
}
|
||||
|
||||
sp.Clear();
|
||||
sel.Clear();
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Routine for STEP output; just a wrapper around the general STEP stuff that
|
||||
// can also be used for surfaces or 3d curves.
|
||||
//-----------------------------------------------------------------------------
|
||||
void Step2dFileWriter::StartFile(void) {
|
||||
ZERO(&sfw);
|
||||
sfw.f = f;
|
||||
sfw.WriteHeader();
|
||||
}
|
||||
|
||||
void Step2dFileWriter::Triangle(STriangle *tr) {
|
||||
}
|
||||
|
||||
void Step2dFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
}
|
||||
void Step2dFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
||||
bool filled, DWORD fillRgb)
|
||||
{
|
||||
}
|
||||
|
||||
void Step2dFileWriter::Bezier(SBezier *sb) {
|
||||
int c = sfw.ExportCurve(sb);
|
||||
sfw.curves.Add(&c);
|
||||
}
|
||||
|
||||
void Step2dFileWriter::FinishAndCloseFile(void) {
|
||||
sfw.WriteWireframe();
|
||||
sfw.WriteFooter();
|
||||
fclose(f);
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@
|
|||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "slvs.h"
|
||||
|
@ -42,7 +41,7 @@ void Example3d(void)
|
|||
sys.param[sys.params++] = Slvs_MakeParam(6, g, 20.0);
|
||||
sys.entity[sys.entities++] = Slvs_MakePoint3d(102, g, 4, 5, 6);
|
||||
// and a line segment connecting them.
|
||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g,
|
||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g,
|
||||
SLVS_FREE_IN_3D, 101, 102);
|
||||
|
||||
// The distance between the points should be 30.0 units.
|
||||
|
@ -117,7 +116,7 @@ void Example2d(void)
|
|||
sys.entity[sys.entities++] = Slvs_MakePoint2d(302, g, 200, 13, 14);
|
||||
|
||||
// And we create a line segment with those endpoints.
|
||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g,
|
||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g,
|
||||
200, 301, 302);
|
||||
|
||||
// Now three more points.
|
||||
|
|
8
exposed/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
add_executable(CDemo
|
||||
CDemo.c)
|
||||
|
||||
target_link_libraries(CDemo
|
||||
slvs)
|
|
@ -77,7 +77,7 @@ TYPES OF ENTITIES
|
|||
SLVS_E_POINT_IN_3D
|
||||
|
||||
A point in 3d. Defined by three parameters:
|
||||
|
||||
|
||||
param[0] the point's x coordinate
|
||||
param[1] y
|
||||
param[1] z
|
||||
|
@ -122,7 +122,7 @@ SLVS_E_NORMAL_IN_3D
|
|||
|
||||
defines a 3x3 rotation matrix. So U, V, and N all have unit length,
|
||||
and are orthogonal so that
|
||||
|
||||
|
||||
U cross V = N
|
||||
V cross N = U
|
||||
N cross U = V
|
||||
|
@ -181,7 +181,7 @@ SLVS_E_WORKPLANE
|
|||
|
||||
|
||||
SLVS_E_LINE_SEGMENT
|
||||
|
||||
|
||||
A line segment between two endpoints
|
||||
|
||||
point[0]
|
||||
|
@ -211,7 +211,7 @@ SLVS_E_CIRCLE
|
|||
normal
|
||||
|
||||
The circle is centered at
|
||||
|
||||
|
||||
point[0]
|
||||
|
||||
The circle's radius is
|
||||
|
@ -356,7 +356,7 @@ SLVS_C_SYMMETRIC_LINE**
|
|||
SLVS_C_AT_MIDPOINT*
|
||||
|
||||
The point ptA lies at the midpoint of the line entityA.
|
||||
|
||||
|
||||
SLVS_C_HORIZONTAL
|
||||
SLVS_C_VERTICAL**
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
DEFINES = /D_WIN32_WINNT=0x500 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN32_LEAN_AND_MEAN /DWIN32 /DLIBRARY
|
||||
# Use the multi-threaded static libc because libpng and zlib do; not sure if anything bad
|
||||
# happens if those mix, but don't want to risk it.
|
||||
CFLAGS = /W3 /nologo -MT -Iextlib -I..\..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /I.. /Zi /EHs /O2 /GS-
|
||||
|
||||
HEADERS = ..\solvespace.h ..\dsc.h ..\sketch.h ..\expr.h slvs.h
|
||||
|
||||
OBJDIR = obj
|
||||
|
||||
SSOBJS = $(OBJDIR)\util.obj \
|
||||
$(OBJDIR)\entity.obj \
|
||||
$(OBJDIR)\expr.obj \
|
||||
$(OBJDIR)\constrainteq.obj \
|
||||
$(OBJDIR)\system.obj \
|
||||
|
||||
|
||||
W32OBJS = $(OBJDIR)\w32util.obj \
|
||||
|
||||
|
||||
LIBOBJS = $(OBJDIR)\lib.obj \
|
||||
|
||||
|
||||
LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib
|
||||
|
||||
all: $(OBJDIR)/CDemo.exe
|
||||
@cp $(OBJDIR)/CDemo.exe .
|
||||
CDemo.exe
|
||||
|
||||
clean:
|
||||
rm -f obj/*
|
||||
|
||||
$(OBJDIR)/slvs.dll: $(SSOBJS) $(LIBOBJS) $(W32OBJS)
|
||||
@$(CC) /LD -Fe$(OBJDIR)/slvs.dll $(SSOBJS) $(LIBOBJS) $(W32OBJS) $(LIBS)
|
||||
@cp $(OBJDIR)/slvs.dll .
|
||||
@echo slvs.dll
|
||||
|
||||
$(OBJDIR)/CDemo.exe: CDemo.c $(OBJDIR)/slvs.dll
|
||||
@$(CC) $(CFLAGS) -Fe$(OBJDIR)/CDemo.exe CDemo.c $(OBJDIR)/slvs.lib $(LIBS)
|
||||
@echo CDemo.exe
|
||||
|
||||
$(SSOBJS): ..\$(@B).cpp $(HEADERS)
|
||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\$(@B).cpp
|
||||
|
||||
$(W32OBJS): ..\win32\$(@B).cpp $(HEADERS)
|
||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\win32\$(@B).cpp
|
||||
|
||||
$(LIBOBJS): $(@B).cpp $(HEADERS)
|
||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ Imports System.Runtime.InteropServices
|
|||
Module VbDemo
|
||||
|
||||
' Call our example functions, which set up some kind of sketch, solve
|
||||
' it, and then print the result.
|
||||
' it, and then print the result.
|
||||
Sub Main()
|
||||
Console.WriteLine("EXAMPLE IN 3d (by objects):")
|
||||
Example3dWithObjects()
|
||||
|
@ -42,13 +42,13 @@ Module VbDemo
|
|||
' classes allow us to represent entities (e.g., lines and points)
|
||||
' as .net objects. So we create an Slvs object, which will contain
|
||||
' the entire sketch, with all the entities and constraints.
|
||||
'
|
||||
'
|
||||
' We then create entity objects (for example, points and lines)
|
||||
' associated with that sketch, indicating the initial positions of
|
||||
' those entities and any hierarchical relationships among them (e.g.,
|
||||
' defining a line entity in terms of endpoint entities). We also add
|
||||
' constraints associated with those entities.
|
||||
'
|
||||
'
|
||||
' Finally, we solve, and print the new positions of the entities. If the
|
||||
' solution succeeded, then the entities will satisfy the constraints. If
|
||||
' not, then the solver will suggest problematic constraints that, if
|
||||
|
@ -598,7 +598,7 @@ Module VbDemo
|
|||
Return Result
|
||||
End Function
|
||||
|
||||
' After a call to Solve(), this returns the number of unconstrained
|
||||
' After a call to Solve(), this returns the number of unconstrained
|
||||
' degrees of freedom for the sketch.
|
||||
Public Function GetDof() As Integer
|
||||
Return Dof
|
||||
|
|
3569
extlib/png.h
1481
extlib/pngconf.h
373
extlib/si/si.h
|
@ -1,373 +0,0 @@
|
|||
/*----------------------------------------------------------------------
|
||||
* si.h -- SpaceWare input library header
|
||||
*
|
||||
* $Id: si.h,v 1.22 1998/03/12 11:07:03 mfarr Exp $
|
||||
*
|
||||
* SpaceWare input library
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* (C) 1998-2001 3Dconnexion. All rights reserved.
|
||||
* Permission to use, copy, modify, and distribute this software for all
|
||||
* purposes and without fees is hereby grated provided that this copyright
|
||||
* notice appears in all copies. Permission to modify this software is granted
|
||||
* and 3Dconnexion will support such modifications only is said modifications are
|
||||
* approved by 3Dconnexion.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _SI_H_
|
||||
#define _SI_H_
|
||||
|
||||
static char incFileNameCvsId[]="(C) 1996 Spacetec IMC Corporation: $Id: si.h,v 1.22 1998/03/12 11:07:03 mfarr Exp $";
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "spwmacro.h"
|
||||
#include "spwdata.h"
|
||||
#include "siSync.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "spwerror.h"
|
||||
|
||||
/*
|
||||
* UI modes
|
||||
*/
|
||||
#define SI_UI_ALL_CONTROLS 0xffffffffL
|
||||
#define SI_UI_NO_CONTROLS 0x00000000L
|
||||
|
||||
/*
|
||||
* These UI modes are left here for legacy applications.
|
||||
*/
|
||||
#define SI_UI_FILTERS 0x00000001L
|
||||
#define SI_UI_FUNC_BUTTONS 0x00000002L
|
||||
#define SI_UI_RESET_BUTTONS 0x00000004L
|
||||
#define SI_UI_SENSITIVITY 0x00000008L
|
||||
#define SI_UI_TUNING 0x00000010L
|
||||
#define SI_UI_DIALOG_POPUP 0x00000020L
|
||||
|
||||
/*
|
||||
* Device types and classes
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SI_ALL_TYPES = -1,
|
||||
SI_UNKNOWN_DEVICE = 0,
|
||||
SI_SPACEBALL_2003 = 1,
|
||||
SI_SPACEBALL_3003 = 2,
|
||||
SI_SPACE_CONTROLLER = 3,
|
||||
SI_AVENGER = 4,
|
||||
SI_SPACEORB_360 = 5,
|
||||
SI_NAVIGATOR = 6,
|
||||
SI_SPACEBALL_2003A = 7,
|
||||
SI_SPACEBALL_2003B = 8,
|
||||
SI_SPACEBALL_2003C = 9,
|
||||
SI_SPACEBALL_3003A = 10,
|
||||
SI_SPACEBALL_3003B = 11,
|
||||
SI_SPACEBALL_3003C = 12,
|
||||
SI_SPACEBALL_4000 = 13,
|
||||
SI_SPACEMOUSE_CLASSIC = 14,
|
||||
SI_SPACEMOUSE_PLUS = 15,
|
||||
SI_SPACEMOUSE_XT = 16,
|
||||
SI_CYBERMAN = 17,
|
||||
SI_CADMAN = 18,
|
||||
SI_SPACEMOUSE_CLASSIC_PROMO = 19,
|
||||
SI_SERIAL_CADMAN = 20,
|
||||
SI_SPACEBALL_5000 = 21,
|
||||
SI_TEST_NO_DEVICE = 22,
|
||||
SI_3DX_KEYBOARD_BLACK = 23,
|
||||
SI_3DX_KEYBOARD_WHITE = 24,
|
||||
SI_TRAVELER = 25,
|
||||
SI_TRAVELER1 = 26,
|
||||
SI_SPACEBALL_5000A = 27,
|
||||
SI_SPACEDRAGON = 28,
|
||||
SI_SPACEPILOT = 29,
|
||||
SI_NUM_DEV_TYPES /* Leave this last, add before it */
|
||||
} SiDevType;
|
||||
|
||||
/*
|
||||
* These defintions of device classes are left in for legacy applications.
|
||||
*/
|
||||
#define SI_HIGH_END 63
|
||||
#define SI_MED_END 62
|
||||
#define SI_LOW_END 61
|
||||
|
||||
/*
|
||||
* Data retrieval mode, only SI_EVENT is currently supported.
|
||||
*/
|
||||
#define SI_EVENT 0x0001
|
||||
#define SI_POLL 0x0002
|
||||
|
||||
/*
|
||||
* Get event flags
|
||||
*/
|
||||
#define SI_AVERAGE_EVENTS 0x0001
|
||||
|
||||
/*
|
||||
* This is an INTERNAL flag used by the polling mechanism, user applications
|
||||
* should NOT send this flag.
|
||||
*/
|
||||
#define SI_POLLED_REQUEST 0x0100
|
||||
|
||||
/*
|
||||
* SpaceWare event types
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SI_BUTTON_EVENT = 1,
|
||||
SI_MOTION_EVENT,
|
||||
SI_COMBO_EVENT, /* Not implemented */
|
||||
SI_ZERO_EVENT,
|
||||
SI_EXCEPTION_EVENT,
|
||||
SI_OUT_OF_BAND,
|
||||
SI_ORIENTATION_EVENT,
|
||||
SI_KEYBOARD_EVENT,
|
||||
SI_LPFK_EVENT,
|
||||
SI_APP_EVENT, /* Application functions */
|
||||
SI_SYNC_EVENT, /* GUI synchronization events */
|
||||
SI_BUTTON_PRESS_EVENT, /* Single button events (replace SI_BUTTON_EVENT) */
|
||||
SI_BUTTON_RELEASE_EVENT /* Single button events (replace SI_BUTTON_EVENT) */
|
||||
} SiEventType;
|
||||
|
||||
/*
|
||||
* Data modes
|
||||
*/
|
||||
#define SI_MODE_NORMALIZE 0x0001
|
||||
#define SI_MODE_COMPRESS 0x0002
|
||||
#define SI_MODE_SENSITIVITY 0x0004
|
||||
#define SI_MODE_TUNING 0x0008
|
||||
|
||||
/*
|
||||
* Motion data offsets
|
||||
*/
|
||||
#define SI_TX 0 /* Translation X value */
|
||||
#define SI_TY 1 /* Translation Y value */
|
||||
#define SI_TZ 2 /* Translation Z value */
|
||||
#define SI_RX 3 /* Rotation X value */
|
||||
#define SI_RY 4 /* Rotation Y value */
|
||||
#define SI_RZ 5 /* Rotation Z value */
|
||||
|
||||
/*
|
||||
* Reserved buttons
|
||||
*/
|
||||
|
||||
#define SI_RESET_DEVICE_BIT 0x00000001L
|
||||
#define SI_APP_FIT_BIT 0x80000000L
|
||||
#define SI_APP_DIALOG_BIT 0x40000000L
|
||||
|
||||
#define SI_RESET_DEVICE_BUTTON 0
|
||||
#define SI_APP_FIT_BUTTON 31
|
||||
#define SI_APP_DIALOG_BUTTON 30
|
||||
|
||||
/*
|
||||
* Miscellaneous
|
||||
*/
|
||||
#define SI_END_ARGS 0
|
||||
#define SI_NO_HANDLE ((SiHdl) NULL)
|
||||
#define SI_ALL_HANDLES ((SiHdl) NULL)
|
||||
#define SI_ANY_HANDLE ((SiHdl) NULL)
|
||||
#define SI_NO_TRANSCTL ((SiTransCtl) NULL)
|
||||
#define SI_NO_MASK ((SiTypeMask *) NULL)
|
||||
#define SI_ANY_DEVICE -1
|
||||
#define SI_NO_DEVICE -1
|
||||
#define SI_NO_TYPE -1
|
||||
#define SI_NO_LIST -1
|
||||
#define SI_NO_BUTTON -1
|
||||
#define SI_STRSIZE 128
|
||||
#define SI_MAXBUF 128
|
||||
#define SI_KEY_MAXBUF 5120
|
||||
|
||||
typedef int SiDevID; /* Device ID */
|
||||
typedef void *SiHdl; /* SpaceWare handle */
|
||||
typedef void *SiTransCtl; /* SpaceWare transport control handle */
|
||||
|
||||
typedef struct /* Open data */
|
||||
{
|
||||
|
||||
HWND hWnd; /* Window handle for SpaceWare messages. */
|
||||
SiTransCtl transCtl; /* SpaceWare transport control handle. Reserved */
|
||||
/* for the s80 transport mechanism. */
|
||||
DWORD processID; /* The process ID for this application. */
|
||||
char exeFile[MAX_PATH]; /* The executable name of the process. */
|
||||
SPWint32 libFlag; /* Library version flag. */
|
||||
} SiOpenData;
|
||||
|
||||
typedef struct /* Get event Data */
|
||||
{
|
||||
|
||||
UINT msg;
|
||||
WPARAM wParam;
|
||||
LPARAM lParam;
|
||||
} SiGetEventData;
|
||||
|
||||
typedef struct /* Device type mask */
|
||||
{
|
||||
unsigned char mask[8];
|
||||
} SiTypeMask;
|
||||
|
||||
typedef struct /* Device port information */
|
||||
{
|
||||
SiDevID devID; /* Device ID */
|
||||
int devType; /* Device type */
|
||||
int devClass; /* Device class */
|
||||
char devName[SI_STRSIZE]; /* Device name */
|
||||
char portName[SI_STRSIZE]; /* Port name */
|
||||
} SiDevPort;
|
||||
|
||||
typedef struct /* Device information */
|
||||
{
|
||||
char firmware[SI_STRSIZE]; /* Firmware version */
|
||||
int devType; /* Device type */
|
||||
int numButtons; /* Number of buttons */
|
||||
int numDegrees; /* Number of degrees of freedom */
|
||||
SPWbool canBeep; /* Device beeps */
|
||||
int majorVersion; /* Major version number */
|
||||
int minorVersion; /* Minor version number */
|
||||
} SiDevInfo;
|
||||
|
||||
typedef struct /* Button information */
|
||||
{
|
||||
char name[SI_STRSIZE]; /* Contains the name of a button for display in an app's GUI */
|
||||
} SiButtonName;
|
||||
|
||||
typedef struct /* Button information */
|
||||
{
|
||||
char name[SI_STRSIZE]; /* Contains the name of a device for display in an app's GUI */
|
||||
} SiDeviceName;
|
||||
|
||||
typedef struct /* Version information */
|
||||
{
|
||||
int major; /* Major version number */
|
||||
int minor; /* Minor version number */
|
||||
int build; /* Build number */
|
||||
char version[SI_STRSIZE]; /* Version string */
|
||||
char date[SI_STRSIZE]; /* Date string */
|
||||
} SiVerInfo;
|
||||
|
||||
typedef struct /* Sensitivity parameters */
|
||||
{
|
||||
char dummy;
|
||||
} SiSensitivity;
|
||||
|
||||
typedef struct /* Tuning parameters */
|
||||
{
|
||||
char dummy;
|
||||
} SiTuning;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SPWuint8 code; /* Out of band message code */
|
||||
union {
|
||||
SPWuint8 message[SI_MAXBUF-1]; /* The actual message */
|
||||
void *pvoid[SI_MAXBUF/8]; /* void ptrs. Enough room for 64bit ptrs */
|
||||
};
|
||||
} SiSpwOOB;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SPWuint8 string[SI_KEY_MAXBUF]; /* String for keyboard data */
|
||||
} SiKeyboardData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SPWuint32 lpfk; /* LPFK number to send */
|
||||
} SiLpfkData;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SI_LEFT = 0,
|
||||
SI_RIGHT
|
||||
} SiOrientation;
|
||||
|
||||
typedef struct /* Bitmasks of button states */
|
||||
{
|
||||
SPWuint32 last; /* Buttons pressed as of last event */
|
||||
SPWuint32 current; /* Buttons pressed as of this event */
|
||||
SPWuint32 pressed; /* Buttons pressed this event */
|
||||
SPWuint32 released; /* Buttons released this event */
|
||||
} SiButtonData;
|
||||
|
||||
/*
|
||||
* SI_BUTTON_PRESS_EVENT & SI_BUTTON_RELEASE_EVENT are hardware button
|
||||
* events. Meaning that they are meant to be sent when a specific hardware
|
||||
* button is pressed. The correlation between the actual hardware button
|
||||
* and the resulting button number could be broken by careful editing of
|
||||
* a config file, but it is intended that the correlation be intact.
|
||||
* This is basically the same as SI_BUTTON_EVENT, but allows
|
||||
* more than 29 buttons because it isn't limited to a 32-bit mask.
|
||||
* Different entries in the config file determine whether SI_BUTTON_PRESS/RELEASE_EVENTs
|
||||
* or SI_BUTTON_EVENTs will be generated.
|
||||
* This event was introduced in 3DxWare 5.2.
|
||||
*/
|
||||
typedef struct /* Data for SI_BUTTON_PRESS/RELEASE_EVENT */
|
||||
{
|
||||
SPWuint32 buttonNumber; /* The button number that went down/up in a *
|
||||
* SI_BUTTON_PRESS/RELEASE_EVENT event */
|
||||
} SiHWButtonData;
|
||||
|
||||
typedef struct /* Data for SI_APP_EVENT */
|
||||
{
|
||||
SPWuint32 functionNumber; /* The Application-specific function number
|
||||
* invoked by the user in a SI_APP_EVENT */
|
||||
} SiAppEventData;
|
||||
|
||||
typedef struct /* SpaceWare data */
|
||||
{
|
||||
SiButtonData bData; /* Button data */
|
||||
long mData[6]; /* Motion data (index via SI_TX, etc) */
|
||||
long period; /* Period (milliseconds) */
|
||||
} SiSpwData;
|
||||
|
||||
typedef struct /* SpaceWare event */
|
||||
{
|
||||
int type; /* Event type */
|
||||
union
|
||||
{
|
||||
SiSpwData spwData; /* Button, motion, or combo data */
|
||||
SiSpwOOB spwOOB; /* Out of band message */
|
||||
SiOrientation spwOrientation;/* Which hand orientation is the device */
|
||||
char exData[SI_MAXBUF]; /* Exception data */
|
||||
SiKeyboardData spwKeyData; /* String for keyboard data */
|
||||
SiLpfkData spwLpfkData; /* LPFK data */
|
||||
SiSyncPacket siSyncPacket; /* GUI SyncPacket sent to applications */
|
||||
SiHWButtonData hwButtonEvent;/* ButtonNumber that goes with *
|
||||
* SI_BUTTON_PRESS/RELEASE_EVENT */
|
||||
SiAppEventData appEventData; /* Application event function data that *
|
||||
* goes with an SI_APP_EVENT event */
|
||||
} u;
|
||||
} SiSpwEvent;
|
||||
|
||||
typedef struct /* Event handler (for SiDispatch) */
|
||||
{
|
||||
int (*func) (SiOpenData *, SiGetEventData *, SiSpwEvent *, void *);
|
||||
void *data;
|
||||
} SiEventHandler;
|
||||
|
||||
typedef struct /* SpaceWare event handlers */
|
||||
{
|
||||
SiEventHandler button; /* Button event handler */
|
||||
SiEventHandler motion; /* Motion event handler */
|
||||
SiEventHandler combo; /* Combo event handler */
|
||||
SiEventHandler zero; /* Zero event handler */
|
||||
SiEventHandler exception; /* Exception event handler */
|
||||
} SiSpwHandlers;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
enum SpwRetVal SiAndTypeMask (SiTypeMask *pTMaskA, SiTypeMask *pTMaskB);
|
||||
int SiGetPortList (SiDevPort **ppPort);
|
||||
void SiFreePortList (SiDevPort *pPort);
|
||||
void SiTune2003 (SiSpwEvent *pEvent);
|
||||
void SiTuneSC (SiSpwEvent *pEvent);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SI_H_ */
|
|
@ -1,206 +0,0 @@
|
|||
/*----------------------------------------------------------------------
|
||||
* siSync.h -- 3DxWare GUI Synchronization header
|
||||
*
|
||||
* Written: September 2004
|
||||
* Author: Jim Wick
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* (c) 1998-2005 3Dconnexion. All rights reserved.
|
||||
* Permission to use, copy, modify, and distribute this software for all
|
||||
* purposes and without fees is hereby grated provided that this copyright
|
||||
* notice appears in all copies. Permission to modify this software is granted
|
||||
* and 3Dconnexion will support such modifications only is said modifications are
|
||||
* approved by 3Dconnexion.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _SISYNC_H_
|
||||
#define _SISYNC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
#define SI_SYNC_PACKET_ID 27711
|
||||
#define SI_SYNC_VERSION_MAJOR 1
|
||||
#define SI_SYNC_VERSION_MINOR 0
|
||||
|
||||
|
||||
/*
|
||||
* Absolute Internal Function Numbers
|
||||
* These are function numbers that will never change.
|
||||
* For use with Set BUTTON_ASSIGNMENT_ABSOLUTE packets, and some INVOKE items.
|
||||
* Some functions (keys) can not be INVOKED because there is a separate
|
||||
* press and release and that difference is not exposed.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SI_SYNC_FUNCTION_MENU_TOGGLE = 12,
|
||||
SI_SYNC_FUNCTION_TRANS_TOGGLE = 13,
|
||||
SI_SYNC_FUNCTION_ROT_TOGGLE = 14,
|
||||
SI_SYNC_FUNCTION_HPV_TOGGLE = 15,
|
||||
SI_SYNC_FUNCTION_DEC_SENS = 16,
|
||||
SI_SYNC_FUNCTION_INC_SENS = 17,
|
||||
SI_SYNC_FUNCTION_RESTORE_DEF = 18,
|
||||
SI_SYNC_FUNCTION_PAN = 19,
|
||||
SI_SYNC_FUNCTION_ZOOM = 20,
|
||||
SI_SYNC_FUNCTION_TX = 21,
|
||||
SI_SYNC_FUNCTION_TY = 22,
|
||||
SI_SYNC_FUNCTION_TZ = 23,
|
||||
SI_SYNC_FUNCTION_RX = 24,
|
||||
SI_SYNC_FUNCTION_RY = 25,
|
||||
SI_SYNC_FUNCTION_RZ = 26,
|
||||
SI_SYNC_FUNCTION_REZERO_DEVICE = 27,
|
||||
SI_SYNC_FUNCTION_SAVE = 33,
|
||||
SI_SYNC_FUNCTION_RELOAD = 57,
|
||||
SI_SYNC_FUNCTION_SHIFT_KEY = 60,
|
||||
SI_SYNC_FUNCTION_CTRL_KEY = 61,
|
||||
SI_SYNC_FUNCTION_ALT_KEY = 62,
|
||||
SI_SYNC_FUNCTION_RESTORE_SENS = 63,
|
||||
SI_SYNC_FUNCTION_SPACE_KEY = 64,
|
||||
SI_SYNC_FUNCTION_CTRL_SHIFT_KEY = 65,
|
||||
SI_SYNC_FUNCTION_CTRL_ALT_KEY = 66,
|
||||
SI_SYNC_FUNCTION_SHIFT_ALT_KEY = 67,
|
||||
SI_SYNC_FUNCTION_TAB_KEY = 68,
|
||||
SI_SYNC_FUNCTION_RETURN_KEY = 69,
|
||||
SI_SYNC_FUNCTION_DEC_TRANS_SENS = 70,
|
||||
SI_SYNC_FUNCTION_INC_TRANS_SENS = 71,
|
||||
SI_SYNC_FUNCTION_DEC_ROT_SENS = 72,
|
||||
SI_SYNC_FUNCTION_INC_ROT_SENS = 73,
|
||||
SI_SYNC_FUNCTION_DEC_PAN_SENS = 74,
|
||||
SI_SYNC_FUNCTION_INC_PAN_SENS = 75,
|
||||
SI_SYNC_FUNCTION_DEC_ZOOM_SENS = 76,
|
||||
SI_SYNC_FUNCTION_INC_ZOOM_SENS = 77,
|
||||
SI_SYNC_FUNCTION_ESC_KEY = 78,
|
||||
SI_SYNC_FUNCTION_3DX_HELP = 94,
|
||||
SI_SYNC_FUNCTION_APP_HELP = 95,
|
||||
SI_SYNC_FUNCTION_DIALOG_TOGGLE_FN= 96,
|
||||
SI_SYNC_FUNCTION_FIT_FN = 97
|
||||
} SiSyncAbsFunctionNumber;
|
||||
|
||||
|
||||
/*
|
||||
* Sync Op Codes
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SI_SYNC_OP_COMMAND = 1,
|
||||
SI_SYNC_OP_GET = 2,
|
||||
SI_SYNC_OP_SET = 3
|
||||
} SiSyncOpCode;
|
||||
|
||||
/*
|
||||
* Sync Item Codes
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SI_SYNC_ITEM_VERSION = 1,
|
||||
SI_SYNC_ITEM_QUERY = 2,
|
||||
SI_SYNC_ITEM_SAVE_CONFIG = 3,
|
||||
SI_SYNC_ITEM_NUMBER_OF_FUNCTIONS = 4,
|
||||
SI_SYNC_ITEM_FUNCTION = 5,
|
||||
SI_SYNC_ITEM_BUTTON_ASSIGNMENT = 6,
|
||||
SI_SYNC_ITEM_BUTTON_ASSIGNMENT_ABSOLUTE = 7,
|
||||
SI_SYNC_ITEM_BUTTON_NAME = 8,
|
||||
SI_SYNC_ITEM_AXIS_LABEL = 9,
|
||||
SI_SYNC_ITEM_ORIENTATION = 10,
|
||||
SI_SYNC_ITEM_FILTER = 11,
|
||||
SI_SYNC_ITEM_AXES_STATE = 12,
|
||||
SI_SYNC_ITEM_INFO_LINE = 13,
|
||||
SI_SYNC_ITEM_SCALE_OVERALL = 14,
|
||||
SI_SYNC_ITEM_SCALE_TX = 15,
|
||||
SI_SYNC_ITEM_SCALE_TY = 16,
|
||||
SI_SYNC_ITEM_SCALE_TZ = 17,
|
||||
SI_SYNC_ITEM_SCALE_RX = 18,
|
||||
SI_SYNC_ITEM_SCALE_RY = 19,
|
||||
SI_SYNC_ITEM_SCALE_RZ = 20,
|
||||
SI_SYNC_ITEM_INVOKE_ABSOLUTE_FUNCTION = 21,
|
||||
SI_SYNC_ITEM_BUTTON_STATE = 22
|
||||
} SiSyncItemCode;
|
||||
|
||||
/*
|
||||
* Filters
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SI_SYNC_FILTER_TRANSLATIONS = 1,
|
||||
SI_SYNC_FILTER_ROTATIONS = 2,
|
||||
SI_SYNC_FILTER_DOMINANT = 3
|
||||
} SiSyncFilter;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SI_SYNC_FILTER_OFF = 0,
|
||||
SI_SYNC_FILTER_ON = 1,
|
||||
SI_SYNC_FILTER_IN_BETWEEN = 2
|
||||
} SiSyncFilterValue;
|
||||
|
||||
/*
|
||||
* Axes State
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SI_SYNC_AXES_STATE_TX = (1<<0),
|
||||
SI_SYNC_AXES_STATE_TY = (1<<1),
|
||||
SI_SYNC_AXES_STATE_TZ = (1<<2),
|
||||
SI_SYNC_AXES_STATE_RX = (1<<3),
|
||||
SI_SYNC_AXES_STATE_RY = (1<<4),
|
||||
SI_SYNC_AXES_STATE_RZ = (1<<5)
|
||||
} SiSyncAxesStateBits;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int state; /* VURZYX (Tx = LSB (& 1<<0) */
|
||||
} SiSyncAxesState;
|
||||
|
||||
/*
|
||||
* Button State
|
||||
* For indicating the state of whatever the button sets (in the LCD at this point).
|
||||
* E.g., to show that Translations are currently OFF for the Translations Toggle button.
|
||||
* OFF: reverse video, flag is not set
|
||||
* ON: normal video, flag is set
|
||||
* DISABLED: (greyed), status of flag is invalid at this time
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
SI_SYNC_BUTTON_STATE_OFF = 0,
|
||||
SI_SYNC_BUTTON_STATE_ON = 1,
|
||||
SI_SYNC_BUTTON_STATE_DISABLED = 2,
|
||||
} SiSyncButtonState;
|
||||
|
||||
|
||||
/*
|
||||
* Private / implementation structures
|
||||
*
|
||||
* We suggest you leave these hidden and use the accessor functions rather than
|
||||
* directly accessing the structures.
|
||||
*/
|
||||
#include "siSyncPriv.h"
|
||||
|
||||
|
||||
/*
|
||||
* Accessor Function headers
|
||||
*/
|
||||
SPWuint32 SiSyncGetSize(SiSyncPacket p);
|
||||
void SiSyncSetSize(SiSyncPacket *p, SPWuint32 size);
|
||||
|
||||
SPWuint32 SiSyncGetHashCode(SiSyncPacket p);
|
||||
void SiSyncSetHashCode(SiSyncPacket *p, SPWuint32 hashCode);
|
||||
|
||||
SiSyncOpCode SiSyncGetOpCode(SiSyncPacket p);
|
||||
void SiSyncSetOpCode(SiSyncPacket *p, SPWuint32 opCode);
|
||||
|
||||
SiSyncItemCode SiSyncGetItemCode(SiSyncPacket p);
|
||||
void SiSyncSetItemCode(SiSyncPacket *p, SPWuint32 itemCode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SI_SYNC_H_ */
|
|
@ -1,127 +0,0 @@
|
|||
/*----------------------------------------------------------------------
|
||||
* siSyncPriv.h -- 3DxWare GUI Synchronization Private header
|
||||
*
|
||||
* Written: June 2005
|
||||
* Author: Jim Wick
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* (c) 1998-2005 3Dconnexion. All rights reserved.
|
||||
* Permission to use, copy, modify, and distribute this software for all
|
||||
* purposes and without fees is hereby grated provided that this copyright
|
||||
* notice appears in all copies. Permission to modify this software is granted
|
||||
* and 3Dconnexion will support such modifications only is said modifications are
|
||||
* approved by 3Dconnexion.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _SISYNCPRIV_H_
|
||||
#define _SISYNCPRIV_H_
|
||||
|
||||
|
||||
/*
|
||||
* All packets start with the same fields.
|
||||
* Many packets have data following the itemCode.
|
||||
*/
|
||||
typedef struct /* Sync Packet */
|
||||
{
|
||||
SPWuint32 size; /* total packet size */
|
||||
SPWuint32 hashCode; /* Hash code that syncs a question with an answer */
|
||||
SiSyncOpCode opCode; /* OpCode */
|
||||
SiSyncItemCode itemCode; /* itemCode */
|
||||
/* There will, generally, be more data starting here.
|
||||
* There will not be any pointers, the data will be in here.
|
||||
*/
|
||||
} SiSyncPacketHeader;
|
||||
|
||||
/*
|
||||
* I've enumerated all the possible packets here, not because they are all different,
|
||||
* but mostly just for documentation. So the developer knows what parameters are
|
||||
* expected with which packet type.
|
||||
*/
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetVersionPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 major; SPWint32 minor; } SiSyncSetVersionPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncCommandQueryPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncCommandSaveConfigPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetNumberOfFunctionsPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 n; } SiSyncSetNumberOfFunctionsPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; } SiSyncGetFunctionPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; SPWint32 n; WCHAR name[1];} SiSyncSetFunctionPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; } SiSyncGetButtonAssignmentPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; SPWint32 n; } SiSyncSetButtonAssignmentPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; SPWint32 n; } SiSyncSetButtonAssignmentAbsolutePacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; WCHAR name[1]; } SiSyncSetButtonNamePacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; } SiSyncGetAxisLabelPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; WCHAR name[1]; } SiSyncSetAxisLabelPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetOrientationPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 a[6]; } SiSyncSetOrientationPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SiSyncFilter i; } SiSyncGetFilterPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SiSyncFilter i; SiSyncFilterValue v; } SiSyncSetFilterPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetAxesStatePacket;
|
||||
typedef struct { SiSyncPacketHeader h; SiSyncAxesState a; } SiSyncSetAxesStatePacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 duration; WCHAR s[1]; } SiSyncSetInfoLinePacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetScaleOverallPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWfloat32 v; } SiSyncSetScaleOverallPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetScaleTxPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWfloat32 v; } SiSyncSetScaleTxPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetScaleTyPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWfloat32 v; } SiSyncSetScaleTyPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetScaleTzPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWfloat32 v; } SiSyncSetScaleTzPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetScaleRxPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWfloat32 v; } SiSyncSetScaleRxPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetScaleRyPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWfloat32 v; } SiSyncSetScaleRyPacket;
|
||||
typedef struct { SiSyncPacketHeader h; } SiSyncGetScaleRzPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWfloat32 v; } SiSyncSetScaleRzPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SiSyncAbsFunctionNumber i; } SiSyncAbsFunctionPacket;
|
||||
typedef struct { SiSyncPacketHeader h; SPWint32 i; SPWbool state; } SiSyncSetButtonStatePacket;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
SiSyncPacketHeader h;
|
||||
SiSyncGetVersionPacket gv;
|
||||
SiSyncSetVersionPacket sv;
|
||||
SiSyncCommandQueryPacket cq;
|
||||
SiSyncCommandSaveConfigPacket cs;
|
||||
SiSyncGetNumberOfFunctionsPacket gnf;
|
||||
SiSyncSetNumberOfFunctionsPacket snf;
|
||||
SiSyncGetFunctionPacket gf;
|
||||
SiSyncSetFunctionPacket sf;
|
||||
SiSyncGetButtonAssignmentPacket gba;
|
||||
SiSyncSetButtonAssignmentPacket sba;
|
||||
SiSyncSetButtonAssignmentAbsolutePacket sbaa;
|
||||
SiSyncSetButtonNamePacket sbn;
|
||||
SiSyncGetAxisLabelPacket ga;
|
||||
SiSyncSetAxisLabelPacket sa;
|
||||
SiSyncGetOrientationPacket go;
|
||||
SiSyncSetOrientationPacket so;
|
||||
SiSyncGetFilterPacket gfi;
|
||||
SiSyncSetFilterPacket sfi;
|
||||
SiSyncGetAxesStatePacket gas;
|
||||
SiSyncSetAxesStatePacket sas;
|
||||
SiSyncSetInfoLinePacket si;
|
||||
SiSyncGetScaleOverallPacket gso;
|
||||
SiSyncSetScaleOverallPacket sso;
|
||||
SiSyncGetScaleTxPacket gtx;
|
||||
SiSyncSetScaleTxPacket stx;
|
||||
SiSyncGetScaleTyPacket gty;
|
||||
SiSyncSetScaleTyPacket sty;
|
||||
SiSyncGetScaleTzPacket gtz;
|
||||
SiSyncSetScaleTzPacket stz;
|
||||
SiSyncGetScaleRxPacket grx;
|
||||
SiSyncSetScaleRxPacket srx;
|
||||
SiSyncGetScaleRyPacket gry;
|
||||
SiSyncSetScaleRyPacket sry;
|
||||
SiSyncGetScaleRzPacket grz;
|
||||
SiSyncSetScaleRzPacket srz;
|
||||
SiSyncAbsFunctionPacket absf;
|
||||
SiSyncSetButtonStatePacket sbs;
|
||||
};
|
||||
} SiSyncPacket;
|
||||
|
||||
|
||||
#endif /* _SI_SYNCPRIV_H_ */
|
|
@ -1,121 +0,0 @@
|
|||
/*-----------------------------------------------------------------------------
|
||||
*
|
||||
* siapp.h -- Si static library interface header file
|
||||
*
|
||||
* $Id: siapp.h,v 1.3 2001/01/16 01:18:49 HJin Exp $
|
||||
*
|
||||
* Contains function headers and type definitions for siapp.c.
|
||||
*
|
||||
*-----------------------------------------------------------------------------
|
||||
*
|
||||
* (c) 1998-2005 3Dconnexion. All rights reserved.
|
||||
* Permission to use, copy, modify, and distribute this software for all
|
||||
* purposes and without fees is hereby grated provided that this copyright
|
||||
* notice appears in all copies. Permission to modify this software is granted
|
||||
* and 3Dconnexion will support such modifications only if said modifications are
|
||||
* approved by 3Dconnexion.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SIAPP_H
|
||||
#define SIAPP_H
|
||||
|
||||
|
||||
static char SiAppCvsId[]="(c) 1998-2005 3Dconnexion: $Id: siapp.h,v 1.3 2001/01/16 01:18:49 HJin Exp $";
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* some enumerated types used in siapp.c */
|
||||
|
||||
enum InitResult
|
||||
{
|
||||
NOT_LOADED,
|
||||
FAILED,
|
||||
LOADED
|
||||
};
|
||||
|
||||
enum ErrorCode
|
||||
{
|
||||
NO_DLL_ERROR=0,
|
||||
DLL_LOAD_FAILURE,
|
||||
DLL_FUNCTION_LOAD_FAILURE,
|
||||
DLL_VAR_LOAD_FAILURE
|
||||
};
|
||||
|
||||
|
||||
/* externally used functions */
|
||||
|
||||
enum SpwRetVal SiInitialize(void);
|
||||
void SiTerminate(void);
|
||||
int SiGetNumDevices (void);
|
||||
SiDevID SiDeviceIndex (int idx);
|
||||
int SiDispatch (SiHdl hdl, SiGetEventData *pData,
|
||||
SiSpwEvent *pEvent, SiSpwHandlers *pDHandlers);
|
||||
void SiOpenWinInit (SiOpenData *pData, HWND hWnd);
|
||||
SiHdl SiOpen (char *pAppName, SiDevID devID, SiTypeMask *pTMask, int mode,
|
||||
SiOpenData *pData);
|
||||
enum SpwRetVal SiClose (SiHdl hdl);
|
||||
void SiGetEventWinInit (SiGetEventData *pData,
|
||||
UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
enum SpwRetVal SiGetEvent (SiHdl hdl, int flags, SiGetEventData *pData,
|
||||
SiSpwEvent *pEvent);
|
||||
enum SpwRetVal SiBeep (SiHdl hdl, char *string);
|
||||
enum SpwRetVal SiRezero (SiHdl hdl);
|
||||
enum SpwRetVal SiGrabDevice (SiHdl hdl, SPWbool exclusive);
|
||||
enum SpwRetVal SiReleaseDevice (SiHdl hdl);
|
||||
int SiButtonPressed (SiSpwEvent *pEvent);
|
||||
int SiButtonReleased (SiSpwEvent *pEvent);
|
||||
enum SpwRetVal SiSetUiMode (SiHdl hdl, SPWuint32 mode);
|
||||
enum SpwRetVal SiSetTypeMask (SiTypeMask *pTMask, int type1, ...);
|
||||
enum SpwRetVal SiGetDevicePort (SiDevID devID, SiDevPort *pPort);
|
||||
enum SpwRetVal SiGetDriverInfo (SiVerInfo *pInfo);
|
||||
void SiGetLibraryInfo (SiVerInfo *pInfo);
|
||||
enum SpwRetVal SiGetDeviceInfo (SiHdl hdl, SiDevInfo *pInfo);
|
||||
char * SpwErrorString (enum SpwRetVal val);
|
||||
enum SpwRetVal SiSyncSendQuery(SiHdl hdl);
|
||||
enum SpwRetVal SiSyncGetVersion(SiHdl hdl, SPWuint32 *pmajor, SPWuint32 *pminor);
|
||||
enum SpwRetVal SiSyncGetNumberOfFunctions(SiHdl hdl, SPWuint32 *pnumberOfFunctions);
|
||||
enum SpwRetVal SiSyncGetFunction(SiHdl hdl, SPWuint32 index, SPWint32 *pabsoluteFunctionNumber, WCHAR name[], SPWuint32 *pmaxNameLen);
|
||||
enum SpwRetVal SiSyncGetButtonAssignment(SiHdl hdl, SPWuint32 buttonNumber, SPWint32 *passignedFunctionIndex);
|
||||
enum SpwRetVal SiSyncSetButtonAssignment(SiHdl hdl, SPWuint32 buttonNumber, SPWint32 functionIndex);
|
||||
enum SpwRetVal SiSyncSetButtonAssignmentAbsolute(SiHdl hdl, SPWuint32 buttonNumber, SPWint32 absoluteFunctionNumber );
|
||||
enum SpwRetVal SiSyncSetButtonName(SiHdl hdl, SPWuint32 buttonNumber, WCHAR name[]);
|
||||
enum SpwRetVal SiSyncGetAxisLabel (SiHdl hdl, SPWuint32 axisNumber, WCHAR name[], SPWuint32 *pmaxNameLen );
|
||||
enum SpwRetVal SiSyncSetAxisLabel (SiHdl hdl, SPWuint32 axisNumber, WCHAR name[] );
|
||||
enum SpwRetVal SiSyncGetOrientation (SiHdl hdl, SPWint32 axes[6] );
|
||||
enum SpwRetVal SiSyncSetOrientation (SiHdl hdl, SPWint32 axes[6] );
|
||||
enum SpwRetVal SiSyncGetFilter (SiHdl hdl, SiSyncFilter i, SiSyncFilterValue *pv );
|
||||
enum SpwRetVal SiSyncSetFilter (SiHdl hdl, SiSyncFilter i, SiSyncFilterValue v );
|
||||
enum SpwRetVal SiSyncGetAxesState (SiHdl hdl, SiSyncAxesState *pa );
|
||||
enum SpwRetVal SiSyncSetAxesState (SiHdl hdl, SiSyncAxesState a );
|
||||
enum SpwRetVal SiSyncSetInfoLine (SiHdl hdl, SPWint32 duration, WCHAR text[] );
|
||||
enum SpwRetVal SiSyncGetScaleOverall (SiHdl hdl, SPWfloat32 *pv );
|
||||
enum SpwRetVal SiSyncSetScaleOverall (SiHdl hdl, SPWfloat32 v );
|
||||
enum SpwRetVal SiSyncGetScaleTx (SiHdl hdl, SPWfloat32 *pv );
|
||||
enum SpwRetVal SiSyncSetScaleTx (SiHdl hdl, SPWfloat32 v );
|
||||
enum SpwRetVal SiSyncGetScaleTy (SiHdl hdl, SPWfloat32 *pv );
|
||||
enum SpwRetVal SiSyncSetScaleTy (SiHdl hdl, SPWfloat32 v );
|
||||
enum SpwRetVal SiSyncGetScaleTz (SiHdl hdl, SPWfloat32 *pv );
|
||||
enum SpwRetVal SiSyncSetScaleTz (SiHdl hdl, SPWfloat32 v );
|
||||
enum SpwRetVal SiSyncGetScaleRx (SiHdl hdl, SPWfloat32 *pv );
|
||||
enum SpwRetVal SiSyncSetScaleRx (SiHdl hdl, SPWfloat32 v );
|
||||
enum SpwRetVal SiSyncGetScaleRy (SiHdl hdl, SPWfloat32 *pv );
|
||||
enum SpwRetVal SiSyncSetScaleRy (SiHdl hdl, SPWfloat32 v );
|
||||
enum SpwRetVal SiSyncGetScaleRz (SiHdl hdl, SPWfloat32 *pv );
|
||||
enum SpwRetVal SiSyncSetScaleRz (SiHdl hdl, SPWfloat32 v );
|
||||
enum SpwRetVal SiSyncInvokeAbsoluteFunction (SiHdl hdl, SiSyncAbsFunctionNumber i );
|
||||
enum SpwRetVal SiSyncSetButtonState (SiHdl hdl, SPWuint32 buttonNumber, SiSyncButtonState state );
|
||||
enum SpwRetVal SiGetButtonName (SiHdl hdl, SPWuint32 buttonNumber, SiButtonName *pname);
|
||||
enum SpwRetVal SiGetDeviceName (SiHdl hdl, SiDeviceName *pname);
|
||||
enum SpwRetVal SiGetDeviceImageFileName (SiHdl hdl, char name[], SPWuint32 *pmaxNameLen);
|
||||
HICON SiGetCompanyIcon(void);
|
||||
enum SpwRetVal SiGetCompanyLogoFileName (char name[], SPWuint32 *pmaxNameLen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* #ifndef SIAPP_H */
|
|
@ -1,63 +0,0 @@
|
|||
/*----------------------------------------------------------------------
|
||||
* spwdata.h -- datatypes
|
||||
*
|
||||
*
|
||||
* $Id: spwdata.h,v 1.4 1996/10/08 23:01:39 chris Exp $
|
||||
*
|
||||
* This contains the only acceptable type definitions for 3Dconnexion
|
||||
* products. Needs more work.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* (c) 1996-2005 3Dconnexion. All rights reserved.
|
||||
*
|
||||
* The computer codes included in this file, including source code and
|
||||
* object code, constitutes the proprietary and confidential information of
|
||||
* 3Dconnexion, and are provided pursuant to a license
|
||||
* agreement. These computer codes are protected by international, federal
|
||||
* and state law, including United States Copyright Law and international
|
||||
* treaty provisions. Except as expressly authorized by the license
|
||||
* agreement, or as expressly permitted under applicable laws of member
|
||||
* states of the European Union and then only to the extent so permitted,
|
||||
* no part of these computer codes may be reproduced or transmitted in any
|
||||
* form or by any means, electronic or mechanical, modified, decompiled,
|
||||
* disassembled, reverse engineered, sold, transferred, rented or utilized
|
||||
* for any unauthorized purpose without the express written permission of
|
||||
* 3Dconnexion.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SPWDATA_H
|
||||
#define SPWDATA_H
|
||||
|
||||
static char spwdataCvsId[]="(C) 1996-2005 3Dconnexion: $Id: spwdata.h,v 1.4 1996/10/08 23:01:39 chris Exp $";
|
||||
|
||||
#include <tchar.h>
|
||||
|
||||
#define tchar_t _TCHAR
|
||||
#define char_t char
|
||||
#define uint32_t unsigned long
|
||||
#define sint32_t long
|
||||
#define boolean_t unsigned char
|
||||
#define void_t void
|
||||
#define window_handle_t HWND
|
||||
|
||||
|
||||
typedef long SPWint32;
|
||||
typedef short SPWint16;
|
||||
typedef char SPWint8;
|
||||
typedef int SPWbool;
|
||||
typedef unsigned long SPWuint32;
|
||||
typedef unsigned short SPWuint16;
|
||||
typedef unsigned char SPWuint8;
|
||||
typedef _TCHAR SPWchar;
|
||||
typedef _TCHAR* SPWstring;
|
||||
typedef float SPWfloat32;
|
||||
typedef double SPWfloat64;
|
||||
|
||||
|
||||
|
||||
#endif /* SPWDATA_H */
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/*----------------------------------------------------------------------
|
||||
* spwerror.h -- Standard Spacetec IMC function return values
|
||||
*
|
||||
* $Id: spwerror.h,v 1.10.4.1 1998/05/26 17:30:21 equerze Exp $
|
||||
*
|
||||
* This file contains all the Spacetec IMC standard error return
|
||||
* return values for functions
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* (C) 1998-2001 3Dconnexion. All rights reserved.
|
||||
* Permission to use, copy, modify, and distribute this software for all
|
||||
* purposes and without fees is hereby grated provided that this copyright
|
||||
* notice appears in all copies. Permission to modify this software is granted
|
||||
* and 3Dconnexion will support such modifications only is said modifications are
|
||||
* approved by 3Dconnexion.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SPWERROR_H_
|
||||
#define _SPWERROR_H_
|
||||
|
||||
#include "spwdata.h"
|
||||
|
||||
static char spwerrorCvsId[]="(C) 1996 Spacetec IMC Corporation: $Id: spwerror.h,v 1.10.4.1 1998/05/26 17:30:21 equerze Exp $";
|
||||
|
||||
enum SpwRetVal /* Error return values. */
|
||||
{
|
||||
SPW_NO_ERROR, /* No error. */
|
||||
SPW_ERROR, /* Error -- function failed. */
|
||||
SI_BAD_HANDLE, /* Invalid SpaceWare handle. */
|
||||
SI_BAD_ID, /* Invalid device ID. */
|
||||
SI_BAD_VALUE, /* Invalid argument value. */
|
||||
SI_IS_EVENT, /* Event is a SpaceWare event. */
|
||||
SI_SKIP_EVENT, /* Skip this SpaceWare event. */
|
||||
SI_NOT_EVENT, /* Event is not a SpaceWare event. */
|
||||
SI_NO_DRIVER, /* SpaceWare driver is not running. */
|
||||
SI_NO_RESPONSE, /* SpaceWare driver is not responding. */
|
||||
SI_UNSUPPORTED, /* The function is unsupported by this version. */
|
||||
SI_UNINITIALIZED, /* SpaceWare input library is uninitialized. */
|
||||
SI_WRONG_DRIVER, /* Driver is incorrect for this SpaceWare version.*/
|
||||
SI_INTERNAL_ERROR, /* Internal SpaceWare error. */
|
||||
SI_BAD_PROTOCOL, /* The transport protocol is unknown. */
|
||||
SI_OUT_OF_MEMORY, /* Unable to malloc space required. */
|
||||
SPW_DLL_LOAD_ERROR, /* Could not load siapp dlls */
|
||||
SI_NOT_OPEN, /* Spaceball device not open */
|
||||
SI_ITEM_NOT_FOUND, /* Item not found */
|
||||
SI_UNSUPPORTED_DEVICE, /* The device is not supported */
|
||||
SI_NOT_ENOUGH_MEMORY, /* Not enough memory (but not a malloc problem) */
|
||||
SI_SYNC_WRONG_HASHCODE /* Wrong hash code sent to a Sync function */
|
||||
};
|
||||
|
||||
typedef enum SpwRetVal SpwReturnValue;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SPWERROR_H_ */
|
|
@ -1,48 +0,0 @@
|
|||
/*----------------------------------------------------------------------
|
||||
* spwmacro.h -- cpp macros we ALWAYS use.
|
||||
*
|
||||
<<<<<<< spwmacro.h
|
||||
* $Id: spwmacro.h,v 1.3 2001/01/16 01:18:40 HJin Exp $
|
||||
=======
|
||||
* $Id: spwmacro.h,v 1.3 2001/01/16 01:18:40 HJin Exp $
|
||||
>>>>>>> 1.1.1.1.4.1
|
||||
*
|
||||
* We always seem to use the same macros.
|
||||
* This is the place we define them.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef SPWMACRO_H
|
||||
#define SPWMACRO_H
|
||||
|
||||
|
||||
#define SPW_FALSE (0)
|
||||
#define SPW_TRUE (!SPW_FALSE)
|
||||
|
||||
#define SPW_MAX(a,b) (((a)>(b))?(a):(b))
|
||||
#define SPW_MIN(a,b) (((a)<(b))?(a):(b))
|
||||
|
||||
#define SPW_ABS(a) (((a)<0)?(-(a)):(a))
|
||||
|
||||
#define SPW_SIGN(a) ((a)>=0?1:-1)
|
||||
|
||||
#define SPW_BIND(min,n,max) (SPW_MIN((max),SPW_MAX((min),(n))))
|
||||
|
||||
#define SPW_NUM_ELEMENTS_IN(a) (sizeof(a)/sizeof((a)[0]))
|
||||
|
||||
#define SPW_PI 3.14159265358979324f
|
||||
|
||||
#define SPW_DEG_TO_RAD(d) ((d)*SPW_PI/180.0f)
|
||||
#define SPW_RAD_TO_DEG(r) ((r)*180.0f/SPW_PI)
|
||||
|
||||
#define SPW_LENGTH_OF(a) (sizeof(a)/sizeof((a)[0]))
|
||||
|
||||
#define SPW_END_OF(a) (&(a)[SPW_LENGTH_OF(a)-1])
|
||||
|
||||
#define SPW_SQ(a) ((a)*(a))
|
||||
|
||||
#define SPW_ABSDIFF(a, b) (fabs((double) (a) - (b)))
|
||||
|
||||
|
||||
#endif
|
332
extlib/zconf.h
|
@ -1,332 +0,0 @@
|
|||
/* zconf.h -- configuration of the zlib compression library
|
||||
* Copyright (C) 1995-2005 Jean-loup Gailly.
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* @(#) $Id$ */
|
||||
|
||||
#ifndef ZCONF_H
|
||||
#define ZCONF_H
|
||||
|
||||
/*
|
||||
* If you *really* need a unique prefix for all types and library functions,
|
||||
* compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
|
||||
*/
|
||||
#ifdef Z_PREFIX
|
||||
# define deflateInit_ z_deflateInit_
|
||||
# define deflate z_deflate
|
||||
# define deflateEnd z_deflateEnd
|
||||
# define inflateInit_ z_inflateInit_
|
||||
# define inflate z_inflate
|
||||
# define inflateEnd z_inflateEnd
|
||||
# define deflateInit2_ z_deflateInit2_
|
||||
# define deflateSetDictionary z_deflateSetDictionary
|
||||
# define deflateCopy z_deflateCopy
|
||||
# define deflateReset z_deflateReset
|
||||
# define deflateParams z_deflateParams
|
||||
# define deflateBound z_deflateBound
|
||||
# define deflatePrime z_deflatePrime
|
||||
# define inflateInit2_ z_inflateInit2_
|
||||
# define inflateSetDictionary z_inflateSetDictionary
|
||||
# define inflateSync z_inflateSync
|
||||
# define inflateSyncPoint z_inflateSyncPoint
|
||||
# define inflateCopy z_inflateCopy
|
||||
# define inflateReset z_inflateReset
|
||||
# define inflateBack z_inflateBack
|
||||
# define inflateBackEnd z_inflateBackEnd
|
||||
# define compress z_compress
|
||||
# define compress2 z_compress2
|
||||
# define compressBound z_compressBound
|
||||
# define uncompress z_uncompress
|
||||
# define adler32 z_adler32
|
||||
# define crc32 z_crc32
|
||||
# define get_crc_table z_get_crc_table
|
||||
# define zError z_zError
|
||||
|
||||
# define alloc_func z_alloc_func
|
||||
# define free_func z_free_func
|
||||
# define in_func z_in_func
|
||||
# define out_func z_out_func
|
||||
# define Byte z_Byte
|
||||
# define uInt z_uInt
|
||||
# define uLong z_uLong
|
||||
# define Bytef z_Bytef
|
||||
# define charf z_charf
|
||||
# define intf z_intf
|
||||
# define uIntf z_uIntf
|
||||
# define uLongf z_uLongf
|
||||
# define voidpf z_voidpf
|
||||
# define voidp z_voidp
|
||||
#endif
|
||||
|
||||
#if defined(__MSDOS__) && !defined(MSDOS)
|
||||
# define MSDOS
|
||||
#endif
|
||||
#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
|
||||
# define OS2
|
||||
#endif
|
||||
#if defined(_WINDOWS) && !defined(WINDOWS)
|
||||
# define WINDOWS
|
||||
#endif
|
||||
#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
|
||||
# ifndef WIN32
|
||||
# define WIN32
|
||||
# endif
|
||||
#endif
|
||||
#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
|
||||
# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
|
||||
# ifndef SYS16BIT
|
||||
# define SYS16BIT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compile with -DMAXSEG_64K if the alloc function cannot allocate more
|
||||
* than 64k bytes at a time (needed on systems with 16-bit int).
|
||||
*/
|
||||
#ifdef SYS16BIT
|
||||
# define MAXSEG_64K
|
||||
#endif
|
||||
#ifdef MSDOS
|
||||
# define UNALIGNED_OK
|
||||
#endif
|
||||
|
||||
#ifdef __STDC_VERSION__
|
||||
# ifndef STDC
|
||||
# define STDC
|
||||
# endif
|
||||
# if __STDC_VERSION__ >= 199901L
|
||||
# ifndef STDC99
|
||||
# define STDC99
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
|
||||
# define STDC
|
||||
#endif
|
||||
#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
|
||||
# define STDC
|
||||
#endif
|
||||
#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
|
||||
# define STDC
|
||||
#endif
|
||||
#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
|
||||
# define STDC
|
||||
#endif
|
||||
|
||||
#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
|
||||
# define STDC
|
||||
#endif
|
||||
|
||||
#ifndef STDC
|
||||
# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
|
||||
# define const /* note: need a more gentle solution here */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Some Mac compilers merge all .h files incorrectly: */
|
||||
#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
|
||||
# define NO_DUMMY_DECL
|
||||
#endif
|
||||
|
||||
/* Maximum value for memLevel in deflateInit2 */
|
||||
#ifndef MAX_MEM_LEVEL
|
||||
# ifdef MAXSEG_64K
|
||||
# define MAX_MEM_LEVEL 8
|
||||
# else
|
||||
# define MAX_MEM_LEVEL 9
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Maximum value for windowBits in deflateInit2 and inflateInit2.
|
||||
* WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
|
||||
* created by gzip. (Files created by minigzip can still be extracted by
|
||||
* gzip.)
|
||||
*/
|
||||
#ifndef MAX_WBITS
|
||||
# define MAX_WBITS 15 /* 32K LZ77 window */
|
||||
#endif
|
||||
|
||||
/* The memory requirements for deflate are (in bytes):
|
||||
(1 << (windowBits+2)) + (1 << (memLevel+9))
|
||||
that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
|
||||
plus a few kilobytes for small objects. For example, if you want to reduce
|
||||
the default memory requirements from 256K to 128K, compile with
|
||||
make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
|
||||
Of course this will generally degrade compression (there's no free lunch).
|
||||
|
||||
The memory requirements for inflate are (in bytes) 1 << windowBits
|
||||
that is, 32K for windowBits=15 (default value) plus a few kilobytes
|
||||
for small objects.
|
||||
*/
|
||||
|
||||
/* Type declarations */
|
||||
|
||||
#ifndef OF /* function prototypes */
|
||||
# ifdef STDC
|
||||
# define OF(args) args
|
||||
# else
|
||||
# define OF(args) ()
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* The following definitions for FAR are needed only for MSDOS mixed
|
||||
* model programming (small or medium model with some far allocations).
|
||||
* This was tested only with MSC; for other MSDOS compilers you may have
|
||||
* to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
|
||||
* just define FAR to be empty.
|
||||
*/
|
||||
#ifdef SYS16BIT
|
||||
# if defined(M_I86SM) || defined(M_I86MM)
|
||||
/* MSC small or medium model */
|
||||
# define SMALL_MEDIUM
|
||||
# ifdef _MSC_VER
|
||||
# define FAR _far
|
||||
# else
|
||||
# define FAR far
|
||||
# endif
|
||||
# endif
|
||||
# if (defined(__SMALL__) || defined(__MEDIUM__))
|
||||
/* Turbo C small or medium model */
|
||||
# define SMALL_MEDIUM
|
||||
# ifdef __BORLANDC__
|
||||
# define FAR _far
|
||||
# else
|
||||
# define FAR far
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32)
|
||||
/* If building or using zlib as a DLL, define ZLIB_DLL.
|
||||
* This is not mandatory, but it offers a little performance increase.
|
||||
*/
|
||||
# ifdef ZLIB_DLL
|
||||
# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
|
||||
# ifdef ZLIB_INTERNAL
|
||||
# define ZEXTERN extern __declspec(dllexport)
|
||||
# else
|
||||
# define ZEXTERN extern __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
# endif /* ZLIB_DLL */
|
||||
/* If building or using zlib with the WINAPI/WINAPIV calling convention,
|
||||
* define ZLIB_WINAPI.
|
||||
* Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
|
||||
*/
|
||||
# ifdef ZLIB_WINAPI
|
||||
# ifdef FAR
|
||||
# undef FAR
|
||||
# endif
|
||||
# include <windows.h>
|
||||
/* No need for _export, use ZLIB.DEF instead. */
|
||||
/* For complete Windows compatibility, use WINAPI, not __stdcall. */
|
||||
# define ZEXPORT WINAPI
|
||||
# ifdef WIN32
|
||||
# define ZEXPORTVA WINAPIV
|
||||
# else
|
||||
# define ZEXPORTVA FAR CDECL
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__BEOS__)
|
||||
# ifdef ZLIB_DLL
|
||||
# ifdef ZLIB_INTERNAL
|
||||
# define ZEXPORT __declspec(dllexport)
|
||||
# define ZEXPORTVA __declspec(dllexport)
|
||||
# else
|
||||
# define ZEXPORT __declspec(dllimport)
|
||||
# define ZEXPORTVA __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ZEXTERN
|
||||
# define ZEXTERN extern
|
||||
#endif
|
||||
#ifndef ZEXPORT
|
||||
# define ZEXPORT
|
||||
#endif
|
||||
#ifndef ZEXPORTVA
|
||||
# define ZEXPORTVA
|
||||
#endif
|
||||
|
||||
#ifndef FAR
|
||||
# define FAR
|
||||
#endif
|
||||
|
||||
#if !defined(__MACTYPES__)
|
||||
typedef unsigned char Byte; /* 8 bits */
|
||||
#endif
|
||||
typedef unsigned int uInt; /* 16 bits or more */
|
||||
typedef unsigned long uLong; /* 32 bits or more */
|
||||
|
||||
#ifdef SMALL_MEDIUM
|
||||
/* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
|
||||
# define Bytef Byte FAR
|
||||
#else
|
||||
typedef Byte FAR Bytef;
|
||||
#endif
|
||||
typedef char FAR charf;
|
||||
typedef int FAR intf;
|
||||
typedef uInt FAR uIntf;
|
||||
typedef uLong FAR uLongf;
|
||||
|
||||
#ifdef STDC
|
||||
typedef void const *voidpc;
|
||||
typedef void FAR *voidpf;
|
||||
typedef void *voidp;
|
||||
#else
|
||||
typedef Byte const *voidpc;
|
||||
typedef Byte FAR *voidpf;
|
||||
typedef Byte *voidp;
|
||||
#endif
|
||||
|
||||
#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
|
||||
# include <sys/types.h> /* for off_t */
|
||||
# include <unistd.h> /* for SEEK_* and off_t */
|
||||
# ifdef VMS
|
||||
# include <unixio.h> /* for off_t */
|
||||
# endif
|
||||
# define z_off_t off_t
|
||||
#endif
|
||||
#ifndef SEEK_SET
|
||||
# define SEEK_SET 0 /* Seek from beginning of file. */
|
||||
# define SEEK_CUR 1 /* Seek from current position. */
|
||||
# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
|
||||
#endif
|
||||
#ifndef z_off_t
|
||||
# define z_off_t long
|
||||
#endif
|
||||
|
||||
#if defined(__OS400__)
|
||||
# define NO_vsnprintf
|
||||
#endif
|
||||
|
||||
#if defined(__MVS__)
|
||||
# define NO_vsnprintf
|
||||
# ifdef FAR
|
||||
# undef FAR
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* MVS linker does not support external names larger than 8 bytes */
|
||||
#if defined(__MVS__)
|
||||
# pragma map(deflateInit_,"DEIN")
|
||||
# pragma map(deflateInit2_,"DEIN2")
|
||||
# pragma map(deflateEnd,"DEEND")
|
||||
# pragma map(deflateBound,"DEBND")
|
||||
# pragma map(inflateInit_,"ININ")
|
||||
# pragma map(inflateInit2_,"ININ2")
|
||||
# pragma map(inflateEnd,"INEND")
|
||||
# pragma map(inflateSync,"INSY")
|
||||
# pragma map(inflateSetDictionary,"INSEDI")
|
||||
# pragma map(compressBound,"CMBND")
|
||||
# pragma map(inflate_table,"INTABL")
|
||||
# pragma map(inflate_fast,"INFA")
|
||||
# pragma map(inflate_copyright,"INCOPY")
|
||||
#endif
|
||||
|
||||
#endif /* ZCONF_H */
|
1357
extlib/zlib.h
BIN
extlib/zlib.lib
676
file.cpp
|
@ -1,676 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Routines to write and read our .slvs file format.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
#define VERSION_STRING "±²³SolveSpaceREVa"
|
||||
|
||||
static int StrStartsWith(char *str, char *start) {
|
||||
return memcmp(str, start, strlen(start)) == 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Clear and free all the dynamic memory associated with our currently-loaded
|
||||
// sketch. This does not leave the program in an acceptable state (with the
|
||||
// references created, and so on), so anyone calling this must fix that later.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SolveSpace::ClearExisting(void) {
|
||||
UndoClearStack(&redo);
|
||||
UndoClearStack(&undo);
|
||||
|
||||
Group *g;
|
||||
for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) {
|
||||
g->Clear();
|
||||
}
|
||||
|
||||
SK.constraint.Clear();
|
||||
SK.request.Clear();
|
||||
SK.group.Clear();
|
||||
SK.style.Clear();
|
||||
|
||||
SK.entity.Clear();
|
||||
SK.param.Clear();
|
||||
}
|
||||
|
||||
hGroup SolveSpace::CreateDefaultDrawingGroup(void) {
|
||||
Group g;
|
||||
ZERO(&g);
|
||||
|
||||
// And an empty group, for the first stuff the user draws.
|
||||
g.visible = true;
|
||||
g.type = Group::DRAWING_WORKPLANE;
|
||||
g.subtype = Group::WORKPLANE_BY_POINT_ORTHO;
|
||||
g.predef.q = Quaternion::From(1, 0, 0, 0);
|
||||
hRequest hr = Request::HREQUEST_REFERENCE_XY;
|
||||
g.predef.origin = hr.entity(1);
|
||||
g.name.strcpy("sketch-in-plane");
|
||||
SK.group.AddAndAssignId(&g);
|
||||
SK.GetGroup(g.h)->activeWorkplane = g.h.entity(0);
|
||||
return g.h;
|
||||
}
|
||||
|
||||
void SolveSpace::NewFile(void) {
|
||||
ClearExisting();
|
||||
|
||||
// Our initial group, that contains the references.
|
||||
Group g;
|
||||
memset(&g, 0, sizeof(g));
|
||||
g.visible = true;
|
||||
g.name.strcpy("#references");
|
||||
g.type = Group::DRAWING_3D;
|
||||
g.h = Group::HGROUP_REFERENCES;
|
||||
SK.group.Add(&g);
|
||||
|
||||
// Let's create three two-d coordinate systems, for the coordinate
|
||||
// planes; these are our references, present in every sketch.
|
||||
Request r;
|
||||
ZERO(&r);
|
||||
r.type = Request::WORKPLANE;
|
||||
r.group = Group::HGROUP_REFERENCES;
|
||||
r.workplane = Entity::FREE_IN_3D;
|
||||
|
||||
r.h = Request::HREQUEST_REFERENCE_XY;
|
||||
SK.request.Add(&r);
|
||||
|
||||
r.h = Request::HREQUEST_REFERENCE_YZ;
|
||||
SK.request.Add(&r);
|
||||
|
||||
r.h = Request::HREQUEST_REFERENCE_ZX;
|
||||
SK.request.Add(&r);
|
||||
|
||||
CreateDefaultDrawingGroup();
|
||||
}
|
||||
|
||||
const SolveSpace::SaveTable SolveSpace::SAVED[] = {
|
||||
{ 'g', "Group.h.v", 'x', &(SS.sv.g.h.v) },
|
||||
{ 'g', "Group.type", 'd', &(SS.sv.g.type) },
|
||||
{ 'g', "Group.order", 'd', &(SS.sv.g.order) },
|
||||
{ 'g', "Group.name", 'N', &(SS.sv.g.name) },
|
||||
{ 'g', "Group.activeWorkplane.v", 'x', &(SS.sv.g.activeWorkplane.v) },
|
||||
{ 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) },
|
||||
{ 'g', "Group.opB.v", 'x', &(SS.sv.g.opB.v) },
|
||||
{ 'g', "Group.valA", 'f', &(SS.sv.g.valA) },
|
||||
{ 'g', "Group.valB", 'f', &(SS.sv.g.valB) },
|
||||
{ 'g', "Group.valC", 'f', &(SS.sv.g.valB) },
|
||||
{ 'g', "Group.color", 'x', &(SS.sv.g.color) },
|
||||
{ 'g', "Group.subtype", 'd', &(SS.sv.g.subtype) },
|
||||
{ 'g', "Group.skipFirst", 'b', &(SS.sv.g.skipFirst) },
|
||||
{ 'g', "Group.meshCombine", 'd', &(SS.sv.g.meshCombine) },
|
||||
{ 'g', "Group.forceToMesh", 'd', &(SS.sv.g.forceToMesh) },
|
||||
{ 'g', "Group.predef.q.w", 'f', &(SS.sv.g.predef.q.w) },
|
||||
{ 'g', "Group.predef.q.vx", 'f', &(SS.sv.g.predef.q.vx) },
|
||||
{ 'g', "Group.predef.q.vy", 'f', &(SS.sv.g.predef.q.vy) },
|
||||
{ 'g', "Group.predef.q.vz", 'f', &(SS.sv.g.predef.q.vz) },
|
||||
{ 'g', "Group.predef.origin.v", 'x', &(SS.sv.g.predef.origin.v) },
|
||||
{ 'g', "Group.predef.entityB.v", 'x', &(SS.sv.g.predef.entityB.v) },
|
||||
{ 'g', "Group.predef.entityC.v", 'x', &(SS.sv.g.predef.entityC.v) },
|
||||
{ 'g', "Group.predef.swapUV", 'b', &(SS.sv.g.predef.swapUV) },
|
||||
{ 'g', "Group.predef.negateU", 'b', &(SS.sv.g.predef.negateU) },
|
||||
{ 'g', "Group.predef.negateV", 'b', &(SS.sv.g.predef.negateV) },
|
||||
{ 'g', "Group.visible", 'b', &(SS.sv.g.visible) },
|
||||
{ 'g', "Group.suppress", 'b', &(SS.sv.g.suppress) },
|
||||
{ 'g', "Group.relaxConstraints", 'b', &(SS.sv.g.relaxConstraints) },
|
||||
{ 'g', "Group.allDimsReference", 'b', &(SS.sv.g.allDimsReference) },
|
||||
{ 'g', "Group.scale", 'f', &(SS.sv.g.scale) },
|
||||
{ 'g', "Group.remap", 'M', &(SS.sv.g.remap) },
|
||||
{ 'g', "Group.impFile", 'P', &(SS.sv.g.impFile) },
|
||||
{ 'g', "Group.impFileRel", 'P', &(SS.sv.g.impFileRel) },
|
||||
|
||||
{ 'p', "Param.h.v.", 'x', &(SS.sv.p.h.v) },
|
||||
{ 'p', "Param.val", 'f', &(SS.sv.p.val) },
|
||||
|
||||
{ 'r', "Request.h.v", 'x', &(SS.sv.r.h.v) },
|
||||
{ 'r', "Request.type", 'd', &(SS.sv.r.type) },
|
||||
{ 'r', "Request.extraPoints", 'd', &(SS.sv.r.extraPoints) },
|
||||
{ 'r', "Request.workplane.v", 'x', &(SS.sv.r.workplane.v) },
|
||||
{ 'r', "Request.group.v", 'x', &(SS.sv.r.group.v) },
|
||||
{ 'r', "Request.construction", 'b', &(SS.sv.r.construction) },
|
||||
{ 'r', "Request.style", 'x', &(SS.sv.r.style) },
|
||||
{ 'r', "Request.str", 'N', &(SS.sv.r.str) },
|
||||
{ 'r', "Request.font", 'N', &(SS.sv.r.font) },
|
||||
|
||||
{ 'e', "Entity.h.v", 'x', &(SS.sv.e.h.v) },
|
||||
{ 'e', "Entity.type", 'd', &(SS.sv.e.type) },
|
||||
{ 'e', "Entity.construction", 'b', &(SS.sv.e.construction) },
|
||||
{ 'e', "Entity.style", 'x', &(SS.sv.e.style) },
|
||||
{ 'e', "Entity.str", 'N', &(SS.sv.e.str) },
|
||||
{ 'e', "Entity.font", 'N', &(SS.sv.e.font) },
|
||||
{ 'e', "Entity.point[0].v", 'x', &(SS.sv.e.point[0].v) },
|
||||
{ 'e', "Entity.point[1].v", 'x', &(SS.sv.e.point[1].v) },
|
||||
{ 'e', "Entity.point[2].v", 'x', &(SS.sv.e.point[2].v) },
|
||||
{ 'e', "Entity.point[3].v", 'x', &(SS.sv.e.point[3].v) },
|
||||
{ 'e', "Entity.point[4].v", 'x', &(SS.sv.e.point[4].v) },
|
||||
{ 'e', "Entity.point[5].v", 'x', &(SS.sv.e.point[5].v) },
|
||||
{ 'e', "Entity.point[6].v", 'x', &(SS.sv.e.point[6].v) },
|
||||
{ 'e', "Entity.point[7].v", 'x', &(SS.sv.e.point[7].v) },
|
||||
{ 'e', "Entity.point[8].v", 'x', &(SS.sv.e.point[8].v) },
|
||||
{ 'e', "Entity.point[9].v", 'x', &(SS.sv.e.point[9].v) },
|
||||
{ 'e', "Entity.point[10].v", 'x', &(SS.sv.e.point[10].v) },
|
||||
{ 'e', "Entity.point[11].v", 'x', &(SS.sv.e.point[11].v) },
|
||||
{ 'e', "Entity.extraPoints", 'd', &(SS.sv.e.extraPoints) },
|
||||
{ 'e', "Entity.normal.v", 'x', &(SS.sv.e.normal.v) },
|
||||
{ 'e', "Entity.distance.v", 'x', &(SS.sv.e.distance.v) },
|
||||
{ 'e', "Entity.workplane.v", 'x', &(SS.sv.e.workplane.v) },
|
||||
{ 'e', "Entity.actPoint.x", 'f', &(SS.sv.e.actPoint.x) },
|
||||
{ 'e', "Entity.actPoint.y", 'f', &(SS.sv.e.actPoint.y) },
|
||||
{ 'e', "Entity.actPoint.z", 'f', &(SS.sv.e.actPoint.z) },
|
||||
{ 'e', "Entity.actNormal.w", 'f', &(SS.sv.e.actNormal.w) },
|
||||
{ 'e', "Entity.actNormal.vx", 'f', &(SS.sv.e.actNormal.vx) },
|
||||
{ 'e', "Entity.actNormal.vy", 'f', &(SS.sv.e.actNormal.vy) },
|
||||
{ 'e', "Entity.actNormal.vz", 'f', &(SS.sv.e.actNormal.vz) },
|
||||
{ 'e', "Entity.actDistance", 'f', &(SS.sv.e.actDistance) },
|
||||
{ 'e', "Entity.actVisible", 'b', &(SS.sv.e.actVisible), },
|
||||
|
||||
|
||||
{ 'c', "Constraint.h.v", 'x', &(SS.sv.c.h.v) },
|
||||
{ 'c', "Constraint.type", 'd', &(SS.sv.c.type) },
|
||||
{ 'c', "Constraint.group.v", 'x', &(SS.sv.c.group.v) },
|
||||
{ 'c', "Constraint.workplane.v", 'x', &(SS.sv.c.workplane.v) },
|
||||
{ 'c', "Constraint.valA", 'f', &(SS.sv.c.valA) },
|
||||
{ 'c', "Constraint.ptA.v", 'x', &(SS.sv.c.ptA.v) },
|
||||
{ 'c', "Constraint.ptB.v", 'x', &(SS.sv.c.ptB.v) },
|
||||
{ 'c', "Constraint.entityA.v", 'x', &(SS.sv.c.entityA.v) },
|
||||
{ 'c', "Constraint.entityB.v", 'x', &(SS.sv.c.entityB.v) },
|
||||
{ 'c', "Constraint.entityC.v", 'x', &(SS.sv.c.entityC.v) },
|
||||
{ 'c', "Constraint.entityD.v", 'x', &(SS.sv.c.entityD.v) },
|
||||
{ 'c', "Constraint.other", 'b', &(SS.sv.c.other) },
|
||||
{ 'c', "Constraint.other2", 'b', &(SS.sv.c.other2) },
|
||||
{ 'c', "Constraint.reference", 'b', &(SS.sv.c.reference) },
|
||||
{ 'c', "Constraint.comment", 'N', &(SS.sv.c.comment) },
|
||||
{ 'c', "Constraint.disp.offset.x", 'f', &(SS.sv.c.disp.offset.x) },
|
||||
{ 'c', "Constraint.disp.offset.y", 'f', &(SS.sv.c.disp.offset.y) },
|
||||
{ 'c', "Constraint.disp.offset.z", 'f', &(SS.sv.c.disp.offset.z) },
|
||||
{ 'c', "Constraint.disp.style", 'x', &(SS.sv.c.disp.style) },
|
||||
|
||||
{ 's', "Style.h.v", 'x', &(SS.sv.s.h.v) },
|
||||
{ 's', "Style.name", 'N', &(SS.sv.s.name) },
|
||||
{ 's', "Style.width", 'f', &(SS.sv.s.width) },
|
||||
{ 's', "Style.widthAs", 'd', &(SS.sv.s.widthAs) },
|
||||
{ 's', "Style.textHeight", 'f', &(SS.sv.s.textHeight) },
|
||||
{ 's', "Style.textHeightAs", 'd', &(SS.sv.s.textHeightAs) },
|
||||
{ 's', "Style.textAngle", 'f', &(SS.sv.s.textAngle) },
|
||||
{ 's', "Style.textOrigin", 'x', &(SS.sv.s.textOrigin) },
|
||||
{ 's', "Style.color", 'x', &(SS.sv.s.color) },
|
||||
{ 's', "Style.fillColor", 'x', &(SS.sv.s.fillColor) },
|
||||
{ 's', "Style.filled", 'b', &(SS.sv.s.filled) },
|
||||
{ 's', "Style.visible", 'b', &(SS.sv.s.visible) },
|
||||
{ 's', "Style.exportable", 'b', &(SS.sv.s.exportable) },
|
||||
|
||||
{ 0, NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
void SolveSpace::SaveUsingTable(int type) {
|
||||
int i;
|
||||
for(i = 0; SAVED[i].type != 0; i++) {
|
||||
if(SAVED[i].type != type) continue;
|
||||
|
||||
int fmt = SAVED[i].fmt;
|
||||
void *p = SAVED[i].ptr;
|
||||
// Any items that aren't specified are assumed to be zero
|
||||
if(fmt == 'd' && *((int *)p) == 0) continue;
|
||||
if(fmt == 'x' && *((DWORD *)p) == 0) continue;
|
||||
if(fmt == 'f' && *((double *)p) == 0.0) continue;
|
||||
if(fmt == 'N' && strlen(((NameStr *)p)->str) == 0) continue;
|
||||
|
||||
fprintf(fh, "%s=", SAVED[i].desc);
|
||||
switch(fmt) {
|
||||
case 'd': fprintf(fh, "%d", *((int *)p)); break;
|
||||
case 'b': fprintf(fh, "%d", *((bool *)p) ? 1 : 0); break;
|
||||
case 'x': fprintf(fh, "%08x", *((DWORD *)p)); break;
|
||||
case 'f': fprintf(fh, "%.20f", *((double *)p)); break;
|
||||
case 'N': fprintf(fh, "%s", ((NameStr *)p)->str); break;
|
||||
case 'P': fprintf(fh, "%s", (char *)p); break;
|
||||
|
||||
case 'M': {
|
||||
int j;
|
||||
fprintf(fh, "{\n");
|
||||
IdList<EntityMap,EntityId> *m = (IdList<EntityMap,EntityId> *)p;
|
||||
for(j = 0; j < m->n; j++) {
|
||||
EntityMap *em = &(m->elem[j]);
|
||||
fprintf(fh, " %d %08x %d\n",
|
||||
em->h.v, em->input.v, em->copyNumber);
|
||||
}
|
||||
fprintf(fh, "}");
|
||||
break;
|
||||
}
|
||||
|
||||
default: oops();
|
||||
}
|
||||
fprintf(fh, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool SolveSpace::SaveToFile(char *filename) {
|
||||
// Make sure all the entities are regenerated up to date, since they
|
||||
// will be exported.
|
||||
SS.GenerateAll(0, INT_MAX);
|
||||
|
||||
fh = fopen(filename, "wb");
|
||||
if(!fh) {
|
||||
Error("Couldn't write to file '%s'", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(fh, "%s\n\n\n", VERSION_STRING);
|
||||
|
||||
int i, j;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
sv.g = SK.group.elem[i];
|
||||
SaveUsingTable('g');
|
||||
fprintf(fh, "AddGroup\n\n");
|
||||
}
|
||||
|
||||
for(i = 0; i < SK.param.n; i++) {
|
||||
sv.p = SK.param.elem[i];
|
||||
SaveUsingTable('p');
|
||||
fprintf(fh, "AddParam\n\n");
|
||||
}
|
||||
|
||||
for(i = 0; i < SK.request.n; i++) {
|
||||
sv.r = SK.request.elem[i];
|
||||
SaveUsingTable('r');
|
||||
fprintf(fh, "AddRequest\n\n");
|
||||
}
|
||||
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
(SK.entity.elem[i]).CalculateNumerical(true);
|
||||
sv.e = SK.entity.elem[i];
|
||||
SaveUsingTable('e');
|
||||
fprintf(fh, "AddEntity\n\n");
|
||||
}
|
||||
|
||||
for(i = 0; i < SK.constraint.n; i++) {
|
||||
sv.c = SK.constraint.elem[i];
|
||||
SaveUsingTable('c');
|
||||
fprintf(fh, "AddConstraint\n\n");
|
||||
}
|
||||
|
||||
for(i = 0; i < SK.style.n; i++) {
|
||||
sv.s = SK.style.elem[i];
|
||||
if(sv.s.h.v >= Style::FIRST_CUSTOM) {
|
||||
SaveUsingTable('s');
|
||||
fprintf(fh, "AddStyle\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
// A group will have either a mesh or a shell, but not both; but the code
|
||||
// to print either of those just does nothing if the mesh/shell is empty.
|
||||
|
||||
SMesh *m = &(SK.group.elem[SK.group.n-1].runningMesh);
|
||||
for(i = 0; i < m->l.n; i++) {
|
||||
STriangle *tr = &(m->l.elem[i]);
|
||||
fprintf(fh, "Triangle %08x %08x "
|
||||
"%.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f %.20f\n",
|
||||
tr->meta.face, tr->meta.color,
|
||||
CO(tr->a), CO(tr->b), CO(tr->c));
|
||||
}
|
||||
|
||||
SShell *s = &(SK.group.elem[SK.group.n-1].runningShell);
|
||||
SSurface *srf;
|
||||
for(srf = s->surface.First(); srf; srf = s->surface.NextAfter(srf)) {
|
||||
fprintf(fh, "Surface %08x %08x %08x %d %d\n",
|
||||
srf->h.v, srf->color, srf->face, srf->degm, srf->degn);
|
||||
for(i = 0; i <= srf->degm; i++) {
|
||||
for(j = 0; j <= srf->degn; j++) {
|
||||
fprintf(fh, "SCtrl %d %d %.20f %.20f %.20f Weight %20.20f\n",
|
||||
i, j, CO(srf->ctrl[i][j]), srf->weight[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
STrimBy *stb;
|
||||
for(stb = srf->trim.First(); stb; stb = srf->trim.NextAfter(stb)) {
|
||||
fprintf(fh, "TrimBy %08x %d %.20f %.20f %.20f %.20f %.20f %.20f\n",
|
||||
stb->curve.v, stb->backwards ? 1 : 0,
|
||||
CO(stb->start), CO(stb->finish));
|
||||
}
|
||||
|
||||
fprintf(fh, "AddSurface\n");
|
||||
}
|
||||
SCurve *sc;
|
||||
for(sc = s->curve.First(); sc; sc = s->curve.NextAfter(sc)) {
|
||||
fprintf(fh, "Curve %08x %d %d %08x %08x\n",
|
||||
sc->h.v,
|
||||
sc->isExact ? 1 : 0, sc->exact.deg,
|
||||
sc->surfA.v, sc->surfB.v);
|
||||
|
||||
if(sc->isExact) {
|
||||
for(i = 0; i <= sc->exact.deg; i++) {
|
||||
fprintf(fh, "CCtrl %d %.20f %.20f %.20f Weight %.20f\n",
|
||||
i, CO(sc->exact.ctrl[i]), sc->exact.weight[i]);
|
||||
}
|
||||
}
|
||||
SCurvePt *scpt;
|
||||
for(scpt = sc->pts.First(); scpt; scpt = sc->pts.NextAfter(scpt)) {
|
||||
fprintf(fh, "CurvePt %d %.20f %.20f %.20f\n",
|
||||
scpt->vertex ? 1 : 0, CO(scpt->p));
|
||||
}
|
||||
|
||||
fprintf(fh, "AddCurve\n");
|
||||
}
|
||||
|
||||
fclose(fh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SolveSpace::LoadUsingTable(char *key, char *val) {
|
||||
int i;
|
||||
for(i = 0; SAVED[i].type != 0; i++) {
|
||||
if(strcmp(SAVED[i].desc, key)==0) {
|
||||
void *p = SAVED[i].ptr;
|
||||
switch(SAVED[i].fmt) {
|
||||
case 'd': *((int *)p) = atoi(val); break;
|
||||
case 'b': *((bool *)p) = (atoi(val) != 0); break;
|
||||
case 'x': sscanf(val, "%x", (DWORD *)p); break;
|
||||
case 'f': *((double *)p) = atof(val); break;
|
||||
case 'N': ((NameStr *)p)->strcpy(val); break;
|
||||
|
||||
case 'P':
|
||||
if(strlen(val)+1 < MAX_PATH) strcpy((char *)p, val);
|
||||
break;
|
||||
|
||||
case 'M': {
|
||||
IdList<EntityMap,EntityId> *m =
|
||||
(IdList<EntityMap,EntityId> *)p;
|
||||
// Don't clear this list! When the group gets added, it
|
||||
// makes a shallow copy, so that would result in us
|
||||
// freeing memory that we want to keep around. Just
|
||||
// zero it out so that new memory is allocated.
|
||||
memset(m, 0, sizeof(*m));
|
||||
for(;;) {
|
||||
EntityMap em;
|
||||
char line2[1024];
|
||||
fgets(line2, sizeof(line2), fh);
|
||||
if(sscanf(line2, "%d %x %d", &(em.h.v), &(em.input.v),
|
||||
&(em.copyNumber)) == 3)
|
||||
{
|
||||
m->Add(&em);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: oops();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(SAVED[i].type == 0) {
|
||||
fileLoadError = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool SolveSpace::LoadFromFile(char *filename) {
|
||||
allConsistent = false;
|
||||
fileLoadError = false;
|
||||
|
||||
fh = fopen(filename, "rb");
|
||||
if(!fh) {
|
||||
Error("Couldn't read from file '%s'", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
ClearExisting();
|
||||
|
||||
memset(&sv, 0, sizeof(sv));
|
||||
sv.g.scale = 1; // default is 1, not 0; so legacy files need this
|
||||
|
||||
char line[1024];
|
||||
while(fgets(line, sizeof(line), fh)) {
|
||||
char *s = strchr(line, '\n');
|
||||
if(s) *s = '\0';
|
||||
// We should never get files with \r characters in them, but mailers
|
||||
// will sometimes mangle attachments.
|
||||
s = strchr(line, '\r');
|
||||
if(s) *s = '\0';
|
||||
|
||||
if(*line == '\0') continue;
|
||||
|
||||
char *e = strchr(line, '=');
|
||||
if(e) {
|
||||
*e = '\0';
|
||||
char *key = line, *val = e+1;
|
||||
LoadUsingTable(key, val);
|
||||
} else if(strcmp(line, "AddGroup")==0) {
|
||||
SK.group.Add(&(sv.g));
|
||||
ZERO(&(sv.g));
|
||||
sv.g.scale = 1; // default is 1, not 0; so legacy files need this
|
||||
} else if(strcmp(line, "AddParam")==0) {
|
||||
// params are regenerated, but we want to preload the values
|
||||
// for initial guesses
|
||||
SK.param.Add(&(sv.p));
|
||||
ZERO(&(sv.p));
|
||||
} else if(strcmp(line, "AddEntity")==0) {
|
||||
// entities are regenerated
|
||||
} else if(strcmp(line, "AddRequest")==0) {
|
||||
SK.request.Add(&(sv.r));
|
||||
ZERO(&(sv.r));
|
||||
} else if(strcmp(line, "AddConstraint")==0) {
|
||||
SK.constraint.Add(&(sv.c));
|
||||
ZERO(&(sv.c));
|
||||
} else if(strcmp(line, "AddStyle")==0) {
|
||||
SK.style.Add(&(sv.s));
|
||||
ZERO(&(sv.s));
|
||||
} else if(strcmp(line, VERSION_STRING)==0) {
|
||||
// do nothing, version string
|
||||
} else if(StrStartsWith(line, "Triangle ") ||
|
||||
StrStartsWith(line, "Surface ") ||
|
||||
StrStartsWith(line, "SCtrl ") ||
|
||||
StrStartsWith(line, "TrimBy ") ||
|
||||
StrStartsWith(line, "Curve ") ||
|
||||
StrStartsWith(line, "CCtrl ") ||
|
||||
StrStartsWith(line, "CurvePt ") ||
|
||||
strcmp(line, "AddSurface")==0 ||
|
||||
strcmp(line, "AddCurve")==0)
|
||||
{
|
||||
// ignore the mesh or shell, since we regenerate that
|
||||
} else {
|
||||
fileLoadError = true;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fh);
|
||||
|
||||
if(fileLoadError) {
|
||||
Error("Unrecognized data in file. This file may be corrupt, or "
|
||||
"from a new version of the program.");
|
||||
// At least leave the program in a non-crashing state.
|
||||
if(SK.group.n == 0) {
|
||||
NewFile();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SolveSpace::LoadEntitiesFromFile(char *file, EntityList *le,
|
||||
SMesh *m, SShell *sh)
|
||||
{
|
||||
SSurface srf;
|
||||
ZERO(&srf);
|
||||
SCurve crv;
|
||||
ZERO(&crv);
|
||||
|
||||
fh = fopen(file, "rb");
|
||||
if(!fh) return false;
|
||||
|
||||
le->Clear();
|
||||
memset(&sv, 0, sizeof(sv));
|
||||
|
||||
char line[1024];
|
||||
while(fgets(line, sizeof(line), fh)) {
|
||||
char *s = strchr(line, '\n');
|
||||
if(s) *s = '\0';
|
||||
// We should never get files with \r characters in them, but mailers
|
||||
// will sometimes mangle attachments.
|
||||
s = strchr(line, '\r');
|
||||
if(s) *s = '\0';
|
||||
|
||||
if(*line == '\0') continue;
|
||||
|
||||
char *e = strchr(line, '=');
|
||||
if(e) {
|
||||
*e = '\0';
|
||||
char *key = line, *val = e+1;
|
||||
LoadUsingTable(key, val);
|
||||
} else if(strcmp(line, "AddGroup")==0) {
|
||||
// Don't leak memory; these get allocated whether we want them
|
||||
// or not.
|
||||
sv.g.remap.Clear();
|
||||
} else if(strcmp(line, "AddParam")==0) {
|
||||
|
||||
} else if(strcmp(line, "AddEntity")==0) {
|
||||
le->Add(&(sv.e));
|
||||
memset(&(sv.e), 0, sizeof(sv.e));
|
||||
} else if(strcmp(line, "AddRequest")==0) {
|
||||
|
||||
} else if(strcmp(line, "AddConstraint")==0) {
|
||||
|
||||
} else if(strcmp(line, "AddStyle")==0) {
|
||||
|
||||
} else if(strcmp(line, VERSION_STRING)==0) {
|
||||
|
||||
} else if(StrStartsWith(line, "Triangle ")) {
|
||||
STriangle tr; ZERO(&tr);
|
||||
if(sscanf(line, "Triangle %x %x "
|
||||
"%lf %lf %lf %lf %lf %lf %lf %lf %lf",
|
||||
&(tr.meta.face), &(tr.meta.color),
|
||||
&(tr.a.x), &(tr.a.y), &(tr.a.z),
|
||||
&(tr.b.x), &(tr.b.y), &(tr.b.z),
|
||||
&(tr.c.x), &(tr.c.y), &(tr.c.z)) != 11)
|
||||
{
|
||||
oops();
|
||||
}
|
||||
m->AddTriangle(&tr);
|
||||
} else if(StrStartsWith(line, "Surface ")) {
|
||||
if(sscanf(line, "Surface %x %x %x %d %d",
|
||||
&(srf.h.v), &(srf.color), &(srf.face),
|
||||
&(srf.degm), &(srf.degn)) != 5)
|
||||
{
|
||||
oops();
|
||||
}
|
||||
} else if(StrStartsWith(line, "SCtrl ")) {
|
||||
int i, j;
|
||||
Vector c;
|
||||
double w;
|
||||
if(sscanf(line, "SCtrl %d %d %lf %lf %lf Weight %lf",
|
||||
&i, &j, &(c.x), &(c.y), &(c.z), &w) != 6)
|
||||
{
|
||||
oops();
|
||||
}
|
||||
srf.ctrl[i][j] = c;
|
||||
srf.weight[i][j] = w;
|
||||
} else if(StrStartsWith(line, "TrimBy ")) {
|
||||
STrimBy stb;
|
||||
ZERO(&stb);
|
||||
int backwards;
|
||||
if(sscanf(line, "TrimBy %x %d %lf %lf %lf %lf %lf %lf",
|
||||
&(stb.curve.v), &backwards,
|
||||
&(stb.start.x), &(stb.start.y), &(stb.start.z),
|
||||
&(stb.finish.x), &(stb.finish.y), &(stb.finish.z)) != 8)
|
||||
{
|
||||
oops();
|
||||
}
|
||||
stb.backwards = (backwards != 0);
|
||||
srf.trim.Add(&stb);
|
||||
} else if(strcmp(line, "AddSurface")==0) {
|
||||
sh->surface.Add(&srf);
|
||||
ZERO(&srf);
|
||||
} else if(StrStartsWith(line, "Curve ")) {
|
||||
int isExact;
|
||||
if(sscanf(line, "Curve %x %d %d %x %x",
|
||||
&(crv.h.v),
|
||||
&(isExact),
|
||||
&(crv.exact.deg),
|
||||
&(crv.surfA.v), &(crv.surfB.v)) != 5)
|
||||
{
|
||||
oops();
|
||||
}
|
||||
crv.isExact = (isExact != 0);
|
||||
} else if(StrStartsWith(line, "CCtrl ")) {
|
||||
int i;
|
||||
Vector c;
|
||||
double w;
|
||||
if(sscanf(line, "CCtrl %d %lf %lf %lf Weight %lf",
|
||||
&i, &(c.x), &(c.y), &(c.z), &w) != 5)
|
||||
{
|
||||
oops();
|
||||
}
|
||||
crv.exact.ctrl[i] = c;
|
||||
crv.exact.weight[i] = w;
|
||||
} else if(StrStartsWith(line, "CurvePt ")) {
|
||||
SCurvePt scpt;
|
||||
int vertex;
|
||||
if(sscanf(line, "CurvePt %d %lf %lf %lf",
|
||||
&vertex,
|
||||
&(scpt.p.x), &(scpt.p.y), &(scpt.p.z)) != 4)
|
||||
{
|
||||
oops();
|
||||
}
|
||||
scpt.vertex = (vertex != 0);
|
||||
crv.pts.Add(&scpt);
|
||||
} else if(strcmp(line, "AddCurve")==0) {
|
||||
sh->curve.Add(&crv);
|
||||
ZERO(&crv);
|
||||
} else {
|
||||
oops();
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fh);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SolveSpace::ReloadAllImported(void) {
|
||||
allConsistent = false;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
Group *g = &(SK.group.elem[i]);
|
||||
if(g->type != Group::IMPORTED) continue;
|
||||
|
||||
g->impEntity.Clear();
|
||||
g->impMesh.Clear();
|
||||
g->impShell.Clear();
|
||||
|
||||
FILE *test = fopen(g->impFile, "rb");
|
||||
if(test) {
|
||||
fclose(test); // okay, exists
|
||||
} else {
|
||||
// It doesn't exist. Perhaps the entire tree has moved, and we
|
||||
// can use the relative filename to get us back.
|
||||
if(SS.saveFile[0]) {
|
||||
char fromRel[MAX_PATH];
|
||||
strcpy(fromRel, g->impFileRel);
|
||||
MakePathAbsolute(SS.saveFile, fromRel);
|
||||
test = fopen(fromRel, "rb");
|
||||
if(test) {
|
||||
fclose(test);
|
||||
// It worked, this is our new absolute path
|
||||
strcpy(g->impFile, fromRel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(LoadEntitiesFromFile(g->impFile,
|
||||
&(g->impEntity), &(g->impMesh), &(g->impShell)))
|
||||
{
|
||||
if(SS.saveFile[0]) {
|
||||
// Record the imported file's name relative to our filename;
|
||||
// if the entire tree moves, then everything will still work
|
||||
strcpy(g->impFileRel, g->impFile);
|
||||
MakePathRelative(SS.saveFile, g->impFileRel);
|
||||
} else {
|
||||
// We're not yet saved, so can't make it absolute
|
||||
strcpy(g->impFileRel, g->impFile);
|
||||
}
|
||||
} else {
|
||||
Error("Failed to load imported file '%s'", g->impFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1774
font.table
489
generate.cpp
|
@ -1,489 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Generate our model based on its parametric description, by solving each
|
||||
// sketch, generating surfaces from the resulting entities, performing any
|
||||
// requested surface operations (e.g. Booleans) with our model so far, and
|
||||
// then repeating this process for each subsequent group.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
void SolveSpace::MarkGroupDirtyByEntity(hEntity he) {
|
||||
Entity *e = SK.GetEntity(he);
|
||||
MarkGroupDirty(e->group);
|
||||
}
|
||||
|
||||
void SolveSpace::MarkGroupDirty(hGroup hg) {
|
||||
int i;
|
||||
bool go = false;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
Group *g = &(SK.group.elem[i]);
|
||||
if(g->h.v == hg.v) {
|
||||
go = true;
|
||||
}
|
||||
if(go) {
|
||||
g->clean = false;
|
||||
}
|
||||
}
|
||||
unsaved = true;
|
||||
}
|
||||
|
||||
bool SolveSpace::PruneOrphans(void) {
|
||||
int i;
|
||||
for(i = 0; i < SK.request.n; i++) {
|
||||
Request *r = &(SK.request.elem[i]);
|
||||
if(GroupExists(r->group)) continue;
|
||||
|
||||
(deleted.requests)++;
|
||||
SK.request.RemoveById(r->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
for(i = 0; i < SK.constraint.n; i++) {
|
||||
Constraint *c = &(SK.constraint.elem[i]);
|
||||
if(GroupExists(c->group)) continue;
|
||||
|
||||
(deleted.constraints)++;
|
||||
(deleted.nonTrivialConstraints)++;
|
||||
|
||||
SK.constraint.RemoveById(c->h);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SolveSpace::GroupsInOrder(hGroup before, hGroup after) {
|
||||
if(before.v == 0) return true;
|
||||
if(after.v == 0) return true;
|
||||
|
||||
int beforep = -1, afterp = -1;
|
||||
int i;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
Group *g = &(SK.group.elem[i]);
|
||||
if(g->h.v == before.v) beforep = i;
|
||||
if(g->h.v == after.v) afterp = i;
|
||||
}
|
||||
if(beforep < 0 || afterp < 0) return false;
|
||||
if(beforep >= afterp) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SolveSpace::GroupExists(hGroup hg) {
|
||||
// A nonexistent group is not acceptable
|
||||
return SK.group.FindByIdNoOops(hg) ? true : false;
|
||||
}
|
||||
bool SolveSpace::EntityExists(hEntity he) {
|
||||
// A nonexstient entity is acceptable, though, usually just means it
|
||||
// doesn't apply.
|
||||
if(he.v == Entity::NO_ENTITY.v) return true;
|
||||
return SK.entity.FindByIdNoOops(he) ? true : false;
|
||||
}
|
||||
|
||||
bool SolveSpace::PruneGroups(hGroup hg) {
|
||||
Group *g = SK.GetGroup(hg);
|
||||
if(GroupsInOrder(g->opA, hg) &&
|
||||
EntityExists(g->predef.origin) &&
|
||||
EntityExists(g->predef.entityB) &&
|
||||
EntityExists(g->predef.entityC))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
(deleted.groups)++;
|
||||
SK.group.RemoveById(g->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SolveSpace::PruneRequests(hGroup hg) {
|
||||
int i;
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
if(e->group.v != hg.v) continue;
|
||||
|
||||
if(EntityExists(e->workplane)) continue;
|
||||
|
||||
if(!e->h.isFromRequest()) oops();
|
||||
|
||||
(deleted.requests)++;
|
||||
SK.request.RemoveById(e->h.request());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SolveSpace::PruneConstraints(hGroup hg) {
|
||||
int i;
|
||||
for(i = 0; i < SK.constraint.n; i++) {
|
||||
Constraint *c = &(SK.constraint.elem[i]);
|
||||
if(c->group.v != hg.v) continue;
|
||||
|
||||
if(EntityExists(c->workplane) &&
|
||||
EntityExists(c->ptA) &&
|
||||
EntityExists(c->ptB) &&
|
||||
EntityExists(c->entityA) &&
|
||||
EntityExists(c->entityB) &&
|
||||
EntityExists(c->entityC) &&
|
||||
EntityExists(c->entityD))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
(deleted.constraints)++;
|
||||
if(c->type != Constraint::POINTS_COINCIDENT &&
|
||||
c->type != Constraint::HORIZONTAL &&
|
||||
c->type != Constraint::VERTICAL)
|
||||
{
|
||||
(deleted.nonTrivialConstraints)++;
|
||||
}
|
||||
|
||||
SK.constraint.RemoveById(c->h);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SolveSpace::GenerateAll(void) {
|
||||
int i;
|
||||
int firstDirty = INT_MAX, lastVisible = 0;
|
||||
// Start from the first dirty group, and solve until the active group,
|
||||
// since all groups after the active group are hidden.
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
Group *g = &(SK.group.elem[i]);
|
||||
g->order = i;
|
||||
if((!g->clean) || (g->solved.how != System::SOLVED_OKAY)) {
|
||||
firstDirty = min(firstDirty, i);
|
||||
}
|
||||
if(g->h.v == SS.GW.activeGroup.v) {
|
||||
lastVisible = i;
|
||||
}
|
||||
}
|
||||
if(firstDirty == INT_MAX || lastVisible == 0) {
|
||||
// All clean; so just regenerate the entities, and don't solve anything.
|
||||
GenerateAll(-1, -1);
|
||||
} else {
|
||||
SS.nakedEdges.Clear();
|
||||
GenerateAll(firstDirty, lastVisible);
|
||||
}
|
||||
}
|
||||
|
||||
void SolveSpace::GenerateAll(int first, int last, bool andFindFree) {
|
||||
int i, j;
|
||||
|
||||
// Remove any requests or constraints that refer to a nonexistent
|
||||
// group; can check those immediately, since we know what the list
|
||||
// of groups should be.
|
||||
while(PruneOrphans())
|
||||
;
|
||||
|
||||
// Don't lose our numerical guesses when we regenerate.
|
||||
IdList<Param,hParam> prev;
|
||||
SK.param.MoveSelfInto(&prev);
|
||||
SK.entity.Clear();
|
||||
|
||||
SDWORD inTime = GetMilliseconds();
|
||||
|
||||
bool displayedStatusMessage = false;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
Group *g = &(SK.group.elem[i]);
|
||||
|
||||
SDWORD now = GetMilliseconds();
|
||||
// Display the status message if we've taken more than 400 ms, or
|
||||
// if we've taken 200 ms but we're not even halfway done, or if
|
||||
// we've already started displaying the status message.
|
||||
if( (now - inTime > 400) ||
|
||||
((now - inTime > 200) && i < (SK.group.n / 2)) ||
|
||||
displayedStatusMessage)
|
||||
{
|
||||
displayedStatusMessage = true;
|
||||
char msg[1024];
|
||||
sprintf(msg, "generating group %d/%d", i, SK.group.n);
|
||||
|
||||
int w, h;
|
||||
GetGraphicsWindowSize(&w, &h);
|
||||
glDrawBuffer(GL_FRONT);
|
||||
glViewport(0, 0, w, h);
|
||||
glLoadIdentity();
|
||||
glTranslated(-1, 1, 0);
|
||||
glScaled(2.0/w, 2.0/h, 1.0);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
double left = 80, top = -20, width = 240, height = 24;
|
||||
glColor3d(0.9, 0.8, 0.8);
|
||||
glxAxisAlignedQuad(left, left+width, top, top-height);
|
||||
glLineWidth(1);
|
||||
glColor3d(0.0, 0.0, 0.0);
|
||||
glxAxisAlignedLineLoop(left, left+width, top, top-height);
|
||||
|
||||
glxCreateBitmapFont();
|
||||
glColor3d(0, 0, 0);
|
||||
glPushMatrix();
|
||||
glTranslated(left+8, top-20, 0);
|
||||
glScaled(1, -1, 1);
|
||||
glxBitmapText(msg, Vector::From(0, 0, 0));
|
||||
glPopMatrix();
|
||||
glFlush();
|
||||
glDrawBuffer(GL_BACK);
|
||||
}
|
||||
|
||||
// The group may depend on entities or other groups, to define its
|
||||
// workplane geometry or for its operands. Those must already exist
|
||||
// in a previous group, so check them before generating.
|
||||
if(PruneGroups(g->h))
|
||||
goto pruned;
|
||||
|
||||
for(j = 0; j < SK.request.n; j++) {
|
||||
Request *r = &(SK.request.elem[j]);
|
||||
if(r->group.v != g->h.v) continue;
|
||||
|
||||
r->Generate(&(SK.entity), &(SK.param));
|
||||
}
|
||||
g->Generate(&(SK.entity), &(SK.param));
|
||||
|
||||
// The requests and constraints depend on stuff in this or the
|
||||
// previous group, so check them after generating.
|
||||
if(PruneRequests(g->h) || PruneConstraints(g->h))
|
||||
goto pruned;
|
||||
|
||||
// Use the previous values for params that we've seen before, as
|
||||
// initial guesses for the solver.
|
||||
for(j = 0; j < SK.param.n; j++) {
|
||||
Param *newp = &(SK.param.elem[j]);
|
||||
if(newp->known) continue;
|
||||
|
||||
Param *prevp = prev.FindByIdNoOops(newp->h);
|
||||
if(prevp) newp->val = prevp->val;
|
||||
}
|
||||
|
||||
if(g->h.v == Group::HGROUP_REFERENCES.v) {
|
||||
ForceReferences();
|
||||
g->solved.how = System::SOLVED_OKAY;
|
||||
g->clean = true;
|
||||
} else {
|
||||
if(i >= first && i <= last) {
|
||||
// The group falls inside the range, so really solve it,
|
||||
// and then regenerate the mesh based on the solved stuff.
|
||||
SolveGroup(g->h, andFindFree);
|
||||
g->GenerateLoops();
|
||||
g->GenerateShellAndMesh();
|
||||
g->clean = true;
|
||||
} else {
|
||||
// The group falls outside the range, so just assume that
|
||||
// it's good wherever we left it. The mesh is unchanged,
|
||||
// and the parameters must be marked as known.
|
||||
for(j = 0; j < SK.param.n; j++) {
|
||||
Param *newp = &(SK.param.elem[j]);
|
||||
|
||||
Param *prevp = prev.FindByIdNoOops(newp->h);
|
||||
if(prevp) newp->known = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// And update any reference dimensions with their new values
|
||||
for(i = 0; i < SK.constraint.n; i++) {
|
||||
Constraint *c = &(SK.constraint.elem[i]);
|
||||
if(c->reference) {
|
||||
c->ModifyToSatisfy();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the point that we're tracing exists.
|
||||
if(traced.point.v && !SK.entity.FindByIdNoOops(traced.point)) {
|
||||
traced.point = Entity::NO_ENTITY;
|
||||
}
|
||||
// And if we're tracing a point, add its new value to the path
|
||||
if(traced.point.v) {
|
||||
Entity *pt = SK.GetEntity(traced.point);
|
||||
traced.path.AddPoint(pt->PointGetNum());
|
||||
}
|
||||
|
||||
prev.Clear();
|
||||
InvalidateGraphics();
|
||||
|
||||
// Remove nonexistent selection items, for same reason we waited till
|
||||
// the end to put up a dialog box.
|
||||
GW.ClearNonexistentSelectionItems();
|
||||
|
||||
if(deleted.requests > 0 || deleted.constraints > 0 || deleted.groups > 0) {
|
||||
// All sorts of interesting things could have happened; for example,
|
||||
// the active group or active workplane could have been deleted. So
|
||||
// clear all that out.
|
||||
if(deleted.groups > 0) {
|
||||
SS.TW.ClearSuper();
|
||||
}
|
||||
later.showTW = true;
|
||||
GW.ClearSuper();
|
||||
|
||||
// People get annoyed if I complain whenever they delete any request,
|
||||
// and I otherwise will, since those always come with pt-coincident
|
||||
// constraints.
|
||||
if(deleted.requests > 0 || deleted.nonTrivialConstraints > 0 ||
|
||||
deleted.groups > 0)
|
||||
{
|
||||
// Don't display any errors until we've regenerated fully. The
|
||||
// sketch is not necessarily in a consistent state until we've
|
||||
// pruned any orphaned etc. objects, and the message loop for the
|
||||
// messagebox could allow us to repaint and crash. But now we must
|
||||
// be fine.
|
||||
Message("Additional sketch elements were deleted, because they "
|
||||
"depend on the element that was just deleted explicitly. "
|
||||
"These include: \n"
|
||||
" %d request%s\n"
|
||||
" %d constraint%s\n"
|
||||
" %d group%s\n\n"
|
||||
"Choose Edit -> Undo to undelete all elements.",
|
||||
deleted.requests, deleted.requests == 1 ? "" : "s",
|
||||
deleted.constraints, deleted.constraints == 1 ? "" : "s",
|
||||
deleted.groups, deleted.groups == 1 ? "" : "s");
|
||||
}
|
||||
memset(&deleted, 0, sizeof(deleted));
|
||||
}
|
||||
|
||||
FreeAllTemporary();
|
||||
allConsistent = true;
|
||||
return;
|
||||
|
||||
pruned:
|
||||
// Restore the numerical guesses
|
||||
SK.param.Clear();
|
||||
prev.MoveSelfInto(&(SK.param));
|
||||
// Try again
|
||||
GenerateAll(first, last);
|
||||
}
|
||||
|
||||
void SolveSpace::ForceReferences(void) {
|
||||
// Force the values of the parameters that define the three reference
|
||||
// coordinate systems.
|
||||
static const struct {
|
||||
hRequest hr;
|
||||
Quaternion q;
|
||||
} Quat[] = {
|
||||
{ Request::HREQUEST_REFERENCE_XY, { 1, 0, 0, 0, } },
|
||||
{ Request::HREQUEST_REFERENCE_YZ, { 0.5, 0.5, 0.5, 0.5, } },
|
||||
{ Request::HREQUEST_REFERENCE_ZX, { 0.5, -0.5, -0.5, -0.5, } },
|
||||
};
|
||||
for(int i = 0; i < 3; i++) {
|
||||
hRequest hr = Quat[i].hr;
|
||||
Entity *wrkpl = SK.GetEntity(hr.entity(0));
|
||||
// The origin for our coordinate system, always zero
|
||||
Entity *origin = SK.GetEntity(wrkpl->point[0]);
|
||||
origin->PointForceTo(Vector::From(0, 0, 0));
|
||||
SK.GetParam(origin->param[0])->known = true;
|
||||
SK.GetParam(origin->param[1])->known = true;
|
||||
SK.GetParam(origin->param[2])->known = true;
|
||||
// The quaternion that defines the rotation, from the table.
|
||||
Entity *normal = SK.GetEntity(wrkpl->normal);
|
||||
normal->NormalForceTo(Quat[i].q);
|
||||
SK.GetParam(normal->param[0])->known = true;
|
||||
SK.GetParam(normal->param[1])->known = true;
|
||||
SK.GetParam(normal->param[2])->known = true;
|
||||
SK.GetParam(normal->param[3])->known = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SolveSpace::MarkDraggedParams(void) {
|
||||
sys.dragged.Clear();
|
||||
|
||||
for(int i = -1; i < SS.GW.pending.points.n; i++) {
|
||||
hEntity hp;
|
||||
if(i == -1) {
|
||||
hp = SS.GW.pending.point;
|
||||
} else {
|
||||
hp = SS.GW.pending.points.elem[i];
|
||||
}
|
||||
if(!hp.v) continue;
|
||||
|
||||
// The pending point could be one in a group that has not yet
|
||||
// been processed, in which case the lookup will fail; but
|
||||
// that's not an error.
|
||||
Entity *pt = SK.entity.FindByIdNoOops(hp);
|
||||
if(pt) {
|
||||
switch(pt->type) {
|
||||
case Entity::POINT_N_TRANS:
|
||||
case Entity::POINT_IN_3D:
|
||||
sys.dragged.Add(&(pt->param[0]));
|
||||
sys.dragged.Add(&(pt->param[1]));
|
||||
sys.dragged.Add(&(pt->param[2]));
|
||||
break;
|
||||
|
||||
case Entity::POINT_IN_2D:
|
||||
sys.dragged.Add(&(pt->param[0]));
|
||||
sys.dragged.Add(&(pt->param[1]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(SS.GW.pending.circle.v) {
|
||||
Entity *circ = SK.entity.FindByIdNoOops(SS.GW.pending.circle);
|
||||
if(circ) {
|
||||
Entity *dist = SK.GetEntity(circ->distance);
|
||||
switch(dist->type) {
|
||||
case Entity::DISTANCE:
|
||||
sys.dragged.Add(&(dist->param[0]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(SS.GW.pending.normal.v) {
|
||||
Entity *norm = SK.entity.FindByIdNoOops(SS.GW.pending.normal);
|
||||
if(norm) {
|
||||
switch(norm->type) {
|
||||
case Entity::NORMAL_IN_3D:
|
||||
sys.dragged.Add(&(norm->param[0]));
|
||||
sys.dragged.Add(&(norm->param[1]));
|
||||
sys.dragged.Add(&(norm->param[2]));
|
||||
sys.dragged.Add(&(norm->param[3]));
|
||||
break;
|
||||
// other types are locked, so not draggable
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SolveSpace::SolveGroup(hGroup hg, bool andFindFree) {
|
||||
int i;
|
||||
// Clear out the system to be solved.
|
||||
sys.entity.Clear();
|
||||
sys.param.Clear();
|
||||
sys.eq.Clear();
|
||||
// And generate all the params for requests in this group
|
||||
for(i = 0; i < SK.request.n; i++) {
|
||||
Request *r = &(SK.request.elem[i]);
|
||||
if(r->group.v != hg.v) continue;
|
||||
|
||||
r->Generate(&(sys.entity), &(sys.param));
|
||||
}
|
||||
// And for the group itself
|
||||
Group *g = SK.GetGroup(hg);
|
||||
g->Generate(&(sys.entity), &(sys.param));
|
||||
// Set the initial guesses for all the params
|
||||
for(i = 0; i < sys.param.n; i++) {
|
||||
Param *p = &(sys.param.elem[i]);
|
||||
p->known = false;
|
||||
p->val = SK.GetParam(p->h)->val;
|
||||
}
|
||||
|
||||
MarkDraggedParams();
|
||||
g->solved.remove.Clear();
|
||||
int how = sys.Solve(g, &(g->solved.dof),
|
||||
&(g->solved.remove), true, andFindFree);
|
||||
if((how != System::SOLVED_OKAY) ||
|
||||
(how == System::SOLVED_OKAY && g->solved.how != System::SOLVED_OKAY))
|
||||
{
|
||||
TextWindow::ReportHowGroupSolved(g->h);
|
||||
}
|
||||
g->solved.how = how;
|
||||
FreeAllTemporary();
|
||||
}
|
||||
|
||||
bool SolveSpace::AllGroupsOkay(void) {
|
||||
int i;
|
||||
bool allOk = true;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
if(SK.group.elem[i].solved.how != System::SOLVED_OKAY) {
|
||||
allOk = false;
|
||||
}
|
||||
}
|
||||
return allOk;
|
||||
}
|
||||
|
607
glhelper.cpp
|
@ -1,607 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Helper functions that ultimately draw stuff with gl.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
// A public-domain Hershey vector font ("Simplex").
|
||||
#include "font.table"
|
||||
|
||||
// A bitmap font.
|
||||
#include "bitmapfont.table"
|
||||
|
||||
static bool ColorLocked;
|
||||
static bool DepthOffsetLocked;
|
||||
|
||||
#define FONT_SCALE(h) ((h)/22.0)
|
||||
double glxStrWidth(char *str, double h)
|
||||
{
|
||||
int w = 0;
|
||||
for(; *str; str++) {
|
||||
int c = *str;
|
||||
if(c < 32 || c > 126) c = 32;
|
||||
c -= 32;
|
||||
|
||||
w += Font[c].width;
|
||||
}
|
||||
return w*FONT_SCALE(h)/SS.GW.scale;
|
||||
}
|
||||
double glxStrHeight(double h)
|
||||
{
|
||||
// The characters have height ~22, as they appear in the table.
|
||||
return 22.0*FONT_SCALE(h)/SS.GW.scale;
|
||||
}
|
||||
void glxWriteTextRefCenter(char *str, double h, Vector t, Vector u, Vector v,
|
||||
glxLineFn *fn, void *fndata)
|
||||
{
|
||||
u = u.WithMagnitude(1);
|
||||
v = v.WithMagnitude(1);
|
||||
|
||||
double scale = FONT_SCALE(h)/SS.GW.scale;
|
||||
double fh = glxStrHeight(h);
|
||||
double fw = glxStrWidth(str, h);
|
||||
|
||||
t = t.Plus(u.ScaledBy(-fw/2));
|
||||
t = t.Plus(v.ScaledBy(-fh/2));
|
||||
|
||||
// Undo the (+5, +5) offset that glxWriteText applies.
|
||||
t = t.Plus(u.ScaledBy(-5*scale));
|
||||
t = t.Plus(v.ScaledBy(-5*scale));
|
||||
|
||||
glxWriteText(str, h, t, u, v, fn, fndata);
|
||||
}
|
||||
|
||||
static void LineDrawCallback(void *fndata, Vector a, Vector b)
|
||||
{
|
||||
glLineWidth(1);
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(a);
|
||||
glxVertex3v(b);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void glxWriteText(char *str, double h, Vector t, Vector u, Vector v,
|
||||
glxLineFn *fn, void *fndata)
|
||||
{
|
||||
if(!fn) fn = LineDrawCallback;
|
||||
u = u.WithMagnitude(1);
|
||||
v = v.WithMagnitude(1);
|
||||
|
||||
double scale = FONT_SCALE(h)/SS.GW.scale;
|
||||
int xo = 5;
|
||||
int yo = 5;
|
||||
|
||||
for(; *str; str++) {
|
||||
int c = *str;
|
||||
if(c < 32 || c > 126) c = 32;
|
||||
|
||||
c -= 32;
|
||||
|
||||
int j;
|
||||
Vector prevp = Vector::From(VERY_POSITIVE, 0, 0);
|
||||
for(j = 0; j < Font[c].points; j++) {
|
||||
int x = Font[c].coord[j*2];
|
||||
int y = Font[c].coord[j*2+1];
|
||||
|
||||
if(x == PEN_UP && y == PEN_UP) {
|
||||
prevp.x = VERY_POSITIVE;
|
||||
} else {
|
||||
Vector p = t;
|
||||
p = p.Plus(u.ScaledBy((xo + x)*scale));
|
||||
p = p.Plus(v.ScaledBy((yo + y)*scale));
|
||||
if(prevp.x != VERY_POSITIVE) {
|
||||
fn(fndata, prevp, p);
|
||||
}
|
||||
prevp = p;
|
||||
}
|
||||
}
|
||||
|
||||
xo += Font[c].width;
|
||||
}
|
||||
}
|
||||
|
||||
void glxVertex3v(Vector u)
|
||||
{
|
||||
glVertex3f((GLfloat)u.x, (GLfloat)u.y, (GLfloat)u.z);
|
||||
}
|
||||
|
||||
void glxAxisAlignedQuad(double l, double r, double t, double b)
|
||||
{
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2d(l, t);
|
||||
glVertex2d(l, b);
|
||||
glVertex2d(r, b);
|
||||
glVertex2d(r, t);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void glxAxisAlignedLineLoop(double l, double r, double t, double b)
|
||||
{
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glVertex2d(l, t);
|
||||
glVertex2d(l, b);
|
||||
glVertex2d(r, b);
|
||||
glVertex2d(r, t);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void FatLineEndcap(Vector p, Vector u, Vector v)
|
||||
{
|
||||
// A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10
|
||||
static const double Circle[11][2] = {
|
||||
{ 0.0000, 1.0000 },
|
||||
{ -0.3090, 0.9511 },
|
||||
{ -0.5878, 0.8090 },
|
||||
{ -0.8090, 0.5878 },
|
||||
{ -0.9511, 0.3090 },
|
||||
{ -1.0000, 0.0000 },
|
||||
{ -0.9511, -0.3090 },
|
||||
{ -0.8090, -0.5878 },
|
||||
{ -0.5878, -0.8090 },
|
||||
{ -0.3090, -0.9511 },
|
||||
{ 0.0000, -1.0000 },
|
||||
};
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
for(int i = 0; i <= 10; i++) {
|
||||
double c = Circle[i][0], s = Circle[i][1];
|
||||
glxVertex3v(p.Plus(u.ScaledBy(c)).Plus(v.ScaledBy(s)));
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void glxFatLine(Vector a, Vector b, double width)
|
||||
{
|
||||
// The half-width of the line we're drawing.
|
||||
double hw = width / 2;
|
||||
Vector ab = b.Minus(a);
|
||||
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
|
||||
Vector abn = (ab.Cross(gn)).WithMagnitude(1);
|
||||
abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
|
||||
// So now abn is normal to the projection of ab into the screen, so the
|
||||
// line will always have constant thickness as the view is rotated.
|
||||
|
||||
abn = abn.WithMagnitude(hw);
|
||||
ab = gn.Cross(abn);
|
||||
ab = ab. WithMagnitude(hw);
|
||||
|
||||
// The body of a line is a quad
|
||||
glBegin(GL_QUADS);
|
||||
glxVertex3v(a.Minus(abn));
|
||||
glxVertex3v(b.Minus(abn));
|
||||
glxVertex3v(b.Plus (abn));
|
||||
glxVertex3v(a.Plus (abn));
|
||||
glEnd();
|
||||
// And the line has two semi-circular end caps.
|
||||
FatLineEndcap(a, ab, abn);
|
||||
FatLineEndcap(b, ab.ScaledBy(-1), abn);
|
||||
}
|
||||
|
||||
|
||||
void glxLockColorTo(DWORD rgb)
|
||||
{
|
||||
ColorLocked = false;
|
||||
glColor3d(REDf(rgb), GREENf(rgb), BLUEf(rgb));
|
||||
ColorLocked = true;
|
||||
}
|
||||
|
||||
void glxUnlockColor(void)
|
||||
{
|
||||
ColorLocked = false;
|
||||
}
|
||||
|
||||
void glxColorRGB(DWORD rgb)
|
||||
{
|
||||
// Is there a bug in some graphics drivers where this is not equivalent
|
||||
// to glColor3d? There seems to be...
|
||||
glxColorRGBa(rgb, 1.0);
|
||||
}
|
||||
|
||||
void glxColorRGBa(DWORD rgb, double a)
|
||||
{
|
||||
if(!ColorLocked) glColor4d(REDf(rgb), GREENf(rgb), BLUEf(rgb), a);
|
||||
}
|
||||
|
||||
static void Stipple(BOOL forSel)
|
||||
{
|
||||
static BOOL Init;
|
||||
const int BYTES = (32*32)/8;
|
||||
static GLubyte HoverMask[BYTES];
|
||||
static GLubyte SelMask[BYTES];
|
||||
if(!Init) {
|
||||
int x, y;
|
||||
for(x = 0; x < 32; x++) {
|
||||
for(y = 0; y < 32; y++) {
|
||||
int i = y*4 + x/8, b = x % 8;
|
||||
int ym = y % 4, xm = x % 4;
|
||||
for(int k = 0; k < 2; k++) {
|
||||
if(xm >= 1 && xm <= 2 && ym >= 1 && ym <= 2) {
|
||||
(k == 0 ? SelMask : HoverMask)[i] |= (0x80 >> b);
|
||||
}
|
||||
ym = (ym + 2) % 4; xm = (xm + 2) % 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
Init = TRUE;
|
||||
}
|
||||
|
||||
glEnable(GL_POLYGON_STIPPLE);
|
||||
if(forSel) {
|
||||
glPolygonStipple(SelMask);
|
||||
} else {
|
||||
glPolygonStipple(HoverMask);
|
||||
}
|
||||
}
|
||||
|
||||
static void StippleTriangle(STriangle *tr, BOOL s, DWORD rgb)
|
||||
{
|
||||
glEnd();
|
||||
glDisable(GL_LIGHTING);
|
||||
glxColorRGB(rgb);
|
||||
Stipple(s);
|
||||
glBegin(GL_TRIANGLES);
|
||||
glxVertex3v(tr->a);
|
||||
glxVertex3v(tr->b);
|
||||
glxVertex3v(tr->c);
|
||||
glEnd();
|
||||
glEnable(GL_LIGHTING);
|
||||
glDisable(GL_POLYGON_STIPPLE);
|
||||
glBegin(GL_TRIANGLES);
|
||||
}
|
||||
|
||||
void glxFillMesh(int specColor, SMesh *m, DWORD h, DWORD s1, DWORD s2)
|
||||
{
|
||||
DWORD rgbHovered = Style::Color(Style::HOVERED),
|
||||
rgbSelected = Style::Color(Style::SELECTED);
|
||||
|
||||
glEnable(GL_NORMALIZE);
|
||||
int prevColor = -1;
|
||||
glBegin(GL_TRIANGLES);
|
||||
for(int i = 0; i < m->l.n; i++) {
|
||||
STriangle *tr = &(m->l.elem[i]);
|
||||
|
||||
int color;
|
||||
if(specColor < 0) {
|
||||
color = tr->meta.color;
|
||||
} else {
|
||||
color = specColor;
|
||||
}
|
||||
if(color != prevColor) {
|
||||
GLfloat mpf[] = { REDf(color), GREENf(color), BLUEf(color), 1.0 };
|
||||
glEnd();
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf);
|
||||
prevColor = color;
|
||||
glBegin(GL_TRIANGLES);
|
||||
}
|
||||
|
||||
if(0 || tr->an.EqualsExactly(Vector::From(0, 0, 0))) {
|
||||
// Compute the normal from the vertices
|
||||
Vector n = tr->Normal();
|
||||
glNormal3d(n.x, n.y, n.z);
|
||||
glxVertex3v(tr->a);
|
||||
glxVertex3v(tr->b);
|
||||
glxVertex3v(tr->c);
|
||||
} else {
|
||||
// Use the exact normals that are specified
|
||||
glNormal3d((tr->an).x, (tr->an).y, (tr->an).z);
|
||||
glxVertex3v(tr->a);
|
||||
|
||||
glNormal3d((tr->bn).x, (tr->bn).y, (tr->bn).z);
|
||||
glxVertex3v(tr->b);
|
||||
|
||||
glNormal3d((tr->cn).x, (tr->cn).y, (tr->cn).z);
|
||||
glxVertex3v(tr->c);
|
||||
}
|
||||
|
||||
if((s1 != 0 && tr->meta.face == s1) ||
|
||||
(s2 != 0 && tr->meta.face == s2))
|
||||
{
|
||||
StippleTriangle(tr, TRUE, rgbSelected);
|
||||
}
|
||||
if(h != 0 && tr->meta.face == h) {
|
||||
StippleTriangle(tr, FALSE, rgbHovered);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void GLX_CALLBACK Vertex(Vector *p)
|
||||
{
|
||||
glxVertex3v(*p);
|
||||
}
|
||||
void glxFillPolygon(SPolygon *p)
|
||||
{
|
||||
GLUtesselator *gt = gluNewTess();
|
||||
gluTessCallback(gt, GLU_TESS_BEGIN, (glxCallbackFptr *)glBegin);
|
||||
gluTessCallback(gt, GLU_TESS_END, (glxCallbackFptr *)glEnd);
|
||||
gluTessCallback(gt, GLU_TESS_VERTEX, (glxCallbackFptr *)Vertex);
|
||||
|
||||
glxTesselatePolygon(gt, p);
|
||||
|
||||
gluDeleteTess(gt);
|
||||
}
|
||||
|
||||
static void GLX_CALLBACK Combine(double coords[3], void *vertexData[4],
|
||||
float weight[4], void **outData)
|
||||
{
|
||||
Vector *n = (Vector *)AllocTemporary(sizeof(Vector));
|
||||
n->x = coords[0];
|
||||
n->y = coords[1];
|
||||
n->z = coords[2];
|
||||
|
||||
*outData = n;
|
||||
}
|
||||
void glxTesselatePolygon(GLUtesselator *gt, SPolygon *p)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
gluTessCallback(gt, GLU_TESS_COMBINE, (glxCallbackFptr *)Combine);
|
||||
gluTessProperty(gt, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
|
||||
|
||||
Vector normal = p->normal;
|
||||
glNormal3d(normal.x, normal.y, normal.z);
|
||||
gluTessNormal(gt, normal.x, normal.y, normal.z);
|
||||
|
||||
gluTessBeginPolygon(gt, NULL);
|
||||
for(i = 0; i < p->l.n; i++) {
|
||||
SContour *sc = &(p->l.elem[i]);
|
||||
gluTessBeginContour(gt);
|
||||
for(j = 0; j < (sc->l.n-1); j++) {
|
||||
SPoint *sp = &(sc->l.elem[j]);
|
||||
double ap[3];
|
||||
ap[0] = sp->p.x;
|
||||
ap[1] = sp->p.y;
|
||||
ap[2] = sp->p.z;
|
||||
gluTessVertex(gt, ap, &(sp->p));
|
||||
}
|
||||
gluTessEndContour(gt);
|
||||
}
|
||||
gluTessEndPolygon(gt);
|
||||
}
|
||||
|
||||
void glxDebugPolygon(SPolygon *p)
|
||||
{
|
||||
int i, j;
|
||||
glLineWidth(2);
|
||||
glPointSize(7);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
for(i = 0; i < p->l.n; i++) {
|
||||
SContour *sc = &(p->l.elem[i]);
|
||||
for(j = 0; j < (sc->l.n-1); j++) {
|
||||
Vector a = (sc->l.elem[j]).p;
|
||||
Vector b = (sc->l.elem[j+1]).p;
|
||||
|
||||
glxLockColorTo(RGB(0, 0, 255));
|
||||
Vector d = (a.Minus(b)).WithMagnitude(-0);
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(a.Plus(d));
|
||||
glxVertex3v(b.Minus(d));
|
||||
glEnd();
|
||||
glxLockColorTo(RGB(255, 0, 0));
|
||||
glBegin(GL_POINTS);
|
||||
glxVertex3v(a.Plus(d));
|
||||
glxVertex3v(b.Minus(d));
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void glxDrawEdges(SEdgeList *el, bool endpointsToo)
|
||||
{
|
||||
SEdge *se;
|
||||
glBegin(GL_LINES);
|
||||
for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
|
||||
glxVertex3v(se->a);
|
||||
glxVertex3v(se->b);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
if(endpointsToo) {
|
||||
glPointSize(12);
|
||||
glBegin(GL_POINTS);
|
||||
for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
|
||||
glxVertex3v(se->a);
|
||||
glxVertex3v(se->b);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void glxDebugMesh(SMesh *m)
|
||||
{
|
||||
int i;
|
||||
glLineWidth(1);
|
||||
glPointSize(7);
|
||||
glxDepthRangeOffset(1);
|
||||
glxUnlockColor();
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
glxColorRGBa(RGB(0, 255, 0), 1.0);
|
||||
glBegin(GL_TRIANGLES);
|
||||
for(i = 0; i < m->l.n; i++) {
|
||||
STriangle *t = &(m->l.elem[i]);
|
||||
if(t->tag) continue;
|
||||
|
||||
glxVertex3v(t->a);
|
||||
glxVertex3v(t->b);
|
||||
glxVertex3v(t->c);
|
||||
}
|
||||
glEnd();
|
||||
glxDepthRangeOffset(0);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
}
|
||||
|
||||
void glxMarkPolygonNormal(SPolygon *p)
|
||||
{
|
||||
Vector tail = Vector::From(0, 0, 0);
|
||||
int i, j, cnt = 0;
|
||||
// Choose some reasonable center point.
|
||||
for(i = 0; i < p->l.n; i++) {
|
||||
SContour *sc = &(p->l.elem[i]);
|
||||
for(j = 0; j < (sc->l.n-1); j++) {
|
||||
SPoint *sp = &(sc->l.elem[j]);
|
||||
tail = tail.Plus(sp->p);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if(cnt == 0) return;
|
||||
tail = tail.ScaledBy(1.0/cnt);
|
||||
|
||||
Vector gn = SS.GW.projRight.Cross(SS.GW.projUp);
|
||||
Vector tip = tail.Plus((p->normal).WithMagnitude(40/SS.GW.scale));
|
||||
Vector arrow = (p->normal).WithMagnitude(15/SS.GW.scale);
|
||||
|
||||
glColor3d(1, 1, 0);
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(tail);
|
||||
glxVertex3v(tip);
|
||||
glxVertex3v(tip);
|
||||
glxVertex3v(tip.Minus(arrow.RotatedAbout(gn, 0.6)));
|
||||
glxVertex3v(tip);
|
||||
glxVertex3v(tip.Minus(arrow.RotatedAbout(gn, -0.6)));
|
||||
glEnd();
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
void glxDepthRangeOffset(int units)
|
||||
{
|
||||
if(!DepthOffsetLocked) {
|
||||
// The size of this step depends on the resolution of the Z buffer; for
|
||||
// a 16-bit buffer, this should be fine.
|
||||
double d = units/60000.0;
|
||||
glDepthRange(0.1-d, 1-d);
|
||||
}
|
||||
}
|
||||
|
||||
void glxDepthRangeLockToFront(bool yes)
|
||||
{
|
||||
if(yes) {
|
||||
DepthOffsetLocked = true;
|
||||
glDepthRange(0, 0);
|
||||
} else {
|
||||
DepthOffsetLocked = false;
|
||||
glxDepthRangeOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
void glxCreateBitmapFont(void)
|
||||
{
|
||||
// Place the font in our texture in a two-dimensional grid; 1d would
|
||||
// be simpler, but long skinny textures (256*16 = 4096 pixels wide)
|
||||
// won't work.
|
||||
static BYTE MappedTexture[4*16*64*16];
|
||||
int a, i;
|
||||
for(a = 0; a < 256; a++) {
|
||||
int row = a / 4, col = a % 4;
|
||||
|
||||
for(i = 0; i < 16; i++) {
|
||||
memcpy(MappedTexture + row*4*16*16 + col*16 + i*4*16,
|
||||
FontTexture + a*16*16 + i*16,
|
||||
16);
|
||||
}
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, TEXTURE_BITMAP_FONT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
|
||||
16*4, 64*16,
|
||||
0,
|
||||
GL_ALPHA, GL_UNSIGNED_BYTE,
|
||||
MappedTexture);
|
||||
}
|
||||
|
||||
void glxBitmapCharQuad(char c, double x, double y)
|
||||
{
|
||||
BYTE b = (BYTE)c;
|
||||
int w, h;
|
||||
|
||||
if(b & 0x80) {
|
||||
// Special character, like a checkbox or a radio button
|
||||
w = h = 16;
|
||||
x -= 3;
|
||||
} else {
|
||||
// Normal character from our font
|
||||
w = SS.TW.CHAR_WIDTH,
|
||||
h = SS.TW.CHAR_HEIGHT;
|
||||
}
|
||||
|
||||
if(b != ' ' && b != 0) {
|
||||
int row = b / 4, col = b % 4;
|
||||
double s0 = col/4.0,
|
||||
s1 = (col+1)/4.0,
|
||||
t0 = row/64.0,
|
||||
t1 = t0 + (w/16.0)/64;
|
||||
|
||||
glTexCoord2d(s1, t0);
|
||||
glVertex2d(x, y);
|
||||
|
||||
glTexCoord2d(s1, t1);
|
||||
glVertex2d(x + w, y);
|
||||
|
||||
glTexCoord2d(s0, t1);
|
||||
glVertex2d(x + w, y - h);
|
||||
|
||||
glTexCoord2d(s0, t0);
|
||||
glVertex2d(x, y - h);
|
||||
}
|
||||
}
|
||||
|
||||
void glxBitmapText(char *str, Vector p)
|
||||
{
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBegin(GL_QUADS);
|
||||
while(*str) {
|
||||
glxBitmapCharQuad(*str, p.x, p.y);
|
||||
|
||||
str++;
|
||||
p.x += SS.TW.CHAR_WIDTH;
|
||||
}
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
void glxDrawPixelsWithTexture(BYTE *data, int w, int h)
|
||||
{
|
||||
#define MAX_DIM 32
|
||||
static BYTE Texture[MAX_DIM*MAX_DIM*3];
|
||||
int i, j;
|
||||
if(w > MAX_DIM || h > MAX_DIM) oops();
|
||||
|
||||
for(i = 0; i < w; i++) {
|
||||
for(j = 0; j < h; j++) {
|
||||
Texture[(j*MAX_DIM + i)*3 + 0] = data[(j*w + i)*3 + 0];
|
||||
Texture[(j*MAX_DIM + i)*3 + 1] = data[(j*w + i)*3 + 1];
|
||||
Texture[(j*MAX_DIM + i)*3 + 2] = data[(j*w + i)*3 + 2];
|
||||
}
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, TEXTURE_DRAW_PIXELS);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, MAX_DIM, MAX_DIM, 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, Texture);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2d(0, 0);
|
||||
glVertex2d(0, h);
|
||||
|
||||
glTexCoord2d(((double)w)/MAX_DIM, 0);
|
||||
glVertex2d(w, h);
|
||||
|
||||
glTexCoord2d(((double)w)/MAX_DIM, ((double)h)/MAX_DIM);
|
||||
glVertex2d(w, 0);
|
||||
|
||||
glTexCoord2d(0, ((double)h)/MAX_DIM);
|
||||
glVertex2d(0, 0);
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
935
graphicswin.cpp
|
@ -1,935 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Top-level implementation of the program's main window, in which a graphical
|
||||
// representation of the model is drawn and edited by the user.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
#define mView (&GraphicsWindow::MenuView)
|
||||
#define mEdit (&GraphicsWindow::MenuEdit)
|
||||
#define mClip (&GraphicsWindow::MenuClipboard)
|
||||
#define mReq (&GraphicsWindow::MenuRequest)
|
||||
#define mCon (&Constraint::MenuConstrain)
|
||||
#define mFile (&SolveSpace::MenuFile)
|
||||
#define mGrp (&Group::MenuGroup)
|
||||
#define mAna (&SolveSpace::MenuAnalyze)
|
||||
#define mHelp (&SolveSpace::MenuHelp)
|
||||
#define S 0x100
|
||||
#define C 0x200
|
||||
#define F(k) (0xf0+(k))
|
||||
const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||
{ 0, "&File", 0, NULL },
|
||||
{ 1, "&New\tCtrl+N", MNU_NEW, 'N'|C, mFile },
|
||||
{ 1, "&Open...\tCtrl+O", MNU_OPEN, 'O'|C, mFile },
|
||||
{10, "Open &Recent", MNU_OPEN_RECENT, 0, mFile },
|
||||
{ 1, "&Save\tCtrl+S", MNU_SAVE, 'S'|C, mFile },
|
||||
{ 1, "Save &As...", MNU_SAVE_AS, 0, mFile },
|
||||
{ 1, NULL, 0, 0, NULL },
|
||||
{ 1, "Export &Image...", MNU_EXPORT_PNG, 0, mFile },
|
||||
{ 1, "Export 2d &View...", MNU_EXPORT_VIEW, 0, mFile },
|
||||
{ 1, "Export 2d &Section...", MNU_EXPORT_SECTION, 0, mFile },
|
||||
{ 1, "Export 3d &Wireframe...", MNU_EXPORT_WIREFRAME, 0, mFile },
|
||||
{ 1, "Export Triangle &Mesh...", MNU_EXPORT_MESH, 0, mFile },
|
||||
{ 1, "Export &Surfaces...", MNU_EXPORT_SURFACES,0, mFile },
|
||||
{ 1, NULL, 0, 0, NULL },
|
||||
{ 1, "E&xit", MNU_EXIT, 0, mFile },
|
||||
|
||||
{ 0, "&Edit", 0, NULL },
|
||||
{ 1, "&Undo\tCtrl+Z", MNU_UNDO, 'Z'|C, mEdit },
|
||||
{ 1, "&Redo\tCtrl+Y", MNU_REDO, 'Y'|C, mEdit },
|
||||
{ 1, "Re&generate All\tSpace", MNU_REGEN_ALL, ' ', mEdit },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Snap Selection to &Grid\t.", MNU_SNAP_TO_GRID, '.', mEdit },
|
||||
{ 1, "Rotate Imported &90°\t9", MNU_ROTATE_90, '9', mEdit },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Cu&t\tCtrl+X", MNU_CUT, 'X'|C, mClip },
|
||||
{ 1, "&Copy\tCtrl+C", MNU_COPY, 'C'|C, mClip },
|
||||
{ 1, "&Paste\tCtrl+V", MNU_PASTE, 'V'|C, mClip },
|
||||
{ 1, "Paste &Transformed...\tCtrl+T", MNU_PASTE_TRANSFORM,'T'|C, mClip },
|
||||
{ 1, "&Delete\tDel", MNU_DELETE, 127, mClip },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Select &Edge Chain\tCtrl+E", MNU_SELECT_CHAIN, 'E'|C, mEdit },
|
||||
{ 1, "Select &All\tCtrl+A", MNU_SELECT_ALL, 'A'|C, mEdit },
|
||||
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
|
||||
|
||||
{ 0, "&View", 0, NULL },
|
||||
{ 1, "Zoom &In\t+", MNU_ZOOM_IN, '+', mView },
|
||||
{ 1, "Zoom &Out\t-", MNU_ZOOM_OUT, '-', mView },
|
||||
{ 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Align View to &Workplane\tW", MNU_ONTO_WORKPLANE, 'W', mView },
|
||||
{ 1, "Nearest &Ortho View\tF2", MNU_NEAREST_ORTHO, F(2), mView },
|
||||
{ 1, "Nearest &Isometric View\tF3", MNU_NEAREST_ISO, F(3), mView },
|
||||
{ 1, "&Center View At Point\tF4", MNU_CENTER_VIEW, F(4), mView },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Show Snap &Grid\t>", MNU_SHOW_GRID, '.'|S, mView },
|
||||
{ 1, "Use &Perspective Projection\t`", MNU_PERSPECTIVE_PROJ,'`', mView },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Show Text &Window\tTab", MNU_SHOW_TEXT_WND, '\t', mView },
|
||||
{ 1, "Show &Toolbar", MNU_SHOW_TOOLBAR, 0, mView },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Dimensions in &Inches", MNU_UNITS_INCHES, 0, mView },
|
||||
{ 1, "Dimensions in &Millimeters", MNU_UNITS_MM, 0, mView },
|
||||
|
||||
{ 0, "&New Group", 0, 0, NULL },
|
||||
{ 1, "Sketch In &3d\tShift+3", MNU_GROUP_3D, '3'|S, mGrp },
|
||||
{ 1, "Sketch In New &Workplane\tShift+W", MNU_GROUP_WRKPL, 'W'|S, mGrp },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Step &Translating\tShift+T", MNU_GROUP_TRANS, 'T'|S, mGrp },
|
||||
{ 1, "Step &Rotating\tShift+R", MNU_GROUP_ROT, 'R'|S, mGrp },
|
||||
{ 1, NULL, 0, 0, NULL },
|
||||
{ 1, "E&xtrude\tShift+X", MNU_GROUP_EXTRUDE, 'X'|S, mGrp },
|
||||
{ 1, "&Lathe\tShift+L", MNU_GROUP_LATHE, 'L'|S, mGrp },
|
||||
{ 1, NULL, 0, 0, NULL },
|
||||
{ 1, "Import / Assemble...\tShift+I", MNU_GROUP_IMPORT, 'I'|S, mGrp },
|
||||
{11, "Import Recent", MNU_GROUP_RECENT, 0, mGrp },
|
||||
|
||||
{ 0, "&Sketch", 0, NULL },
|
||||
{ 1, "In &Workplane\t2", MNU_SEL_WORKPLANE, '2', mReq },
|
||||
{ 1, "Anywhere In &3d\t3", MNU_FREE_IN_3D, '3', mReq },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Datum &Point\tP", MNU_DATUM_POINT, 'P', mReq },
|
||||
{ 1, "&Workplane", MNU_WORKPLANE, 0, mReq },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq },
|
||||
{ 1, "&Rectangle\tR", MNU_RECTANGLE, 'R', mReq },
|
||||
{ 1, "&Circle\tC", MNU_CIRCLE, 'C', mReq },
|
||||
{ 1, "&Arc of a Circle\tA", MNU_ARC, 'A', mReq },
|
||||
{ 1, "&Bezier Cubic Spline\tB", MNU_CUBIC, 'B', mReq },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "&Text in TrueType Font\tT", MNU_TTF_TEXT, 'T', mReq },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "To&ggle Construction\tG", MNU_CONSTRUCTION, 'G', mReq },
|
||||
{ 1, "Tangent &Arc at Point\tShift+A", MNU_TANGENT_ARC, 'A'|S, mReq },
|
||||
{ 1, "Split Curves at &Intersection\tI", MNU_SPLIT_CURVES, 'I', mReq },
|
||||
|
||||
{ 0, "&Constrain", 0, NULL },
|
||||
{ 1, "&Distance / Diameter\tD", MNU_DISTANCE_DIA, 'D', mCon },
|
||||
{ 1, "A&ngle\tN", MNU_ANGLE, 'N', mCon },
|
||||
{ 1, "Other S&upplementary Angle\tU", MNU_OTHER_ANGLE, 'U', mCon },
|
||||
{ 1, "Toggle R&eference Dim\tE", MNU_REFERENCE, 'E', mCon },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "&Horizontal\tH", MNU_HORIZONTAL, 'H', mCon },
|
||||
{ 1, "&Vertical\tV", MNU_VERTICAL, 'V', mCon },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "&On Point / Curve / Plane\tO", MNU_ON_ENTITY, 'O', mCon },
|
||||
{ 1, "E&qual Length / Radius / Angle\tQ", MNU_EQUAL, 'Q', mCon },
|
||||
{ 1, "Length Ra&tio\tZ", MNU_RATIO, 'Z', mCon },
|
||||
{ 1, "At &Midpoint\tM", MNU_AT_MIDPOINT, 'M', mCon },
|
||||
{ 1, "S&ymmetric\tY", MNU_SYMMETRIC, 'Y', mCon },
|
||||
{ 1, "Para&llel / Tangent\tL", MNU_PARALLEL, 'L', mCon },
|
||||
{ 1, "&Perpendicular\t[", MNU_PERPENDICULAR, '[', mCon },
|
||||
{ 1, "Same Orient&ation\tX", MNU_ORIENTED_SAME, 'X', mCon },
|
||||
{ 1, "Lock Point Where &Dragged\t]", MNU_WHERE_DRAGGED, ']', mCon },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Comment\t;", MNU_COMMENT, ';', mCon },
|
||||
|
||||
{ 0, "&Analyze", 0, NULL },
|
||||
{ 1, "Measure &Volume\tCtrl+Shift+V", MNU_VOLUME, 'V'|S|C,mAna },
|
||||
{ 1, "Measure &Area\tCtrl+Shift+A", MNU_AREA, 'A'|S|C,mAna },
|
||||
{ 1, "Show &Interfering Parts\tCtrl+Shift+I", MNU_INTERFERENCE, 'I'|S|C,mAna },
|
||||
{ 1, "Show &Naked Edges\tCtrl+Shift+N", MNU_NAKED_EDGES, 'N'|S|C,mAna },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Show Degrees of &Freedom\tCtrl+Shift+F", MNU_SHOW_DOF, 'F'|S|C,mAna },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "&Trace Point\tCtrl+Shift+T", MNU_TRACE_PT, 'T'|S|C,mAna },
|
||||
{ 1, "&Stop Tracing...\tCtrl+Shift+S", MNU_STOP_TRACING, 'S'|S|C,mAna },
|
||||
{ 1, "Step &Dimension...\tCtrl+Shift+D", MNU_STEP_DIM, 'D'|S|C,mAna },
|
||||
|
||||
{ 0, "&Help", 0, 0, NULL },
|
||||
{ 1, "&Website / Manual", MNU_WEBSITE, 0, mHelp },
|
||||
{ 1, "&About", MNU_ABOUT, 0, mHelp },
|
||||
{ -1 },
|
||||
};
|
||||
|
||||
void GraphicsWindow::Init(void) {
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
scale = 5;
|
||||
offset = Vector::From(0, 0, 0);
|
||||
projRight = Vector::From(1, 0, 0);
|
||||
projUp = Vector::From(0, 1, 0);
|
||||
|
||||
// Make sure those are valid; could get a mouse move without a mouse
|
||||
// down if someone depresses the button, then drags into our window.
|
||||
orig.projRight = projRight;
|
||||
orig.projUp = projUp;
|
||||
|
||||
// And with the last group active
|
||||
activeGroup = SK.group.elem[SK.group.n-1].h;
|
||||
SK.GetGroup(activeGroup)->Activate();
|
||||
|
||||
showWorkplanes = false;
|
||||
showNormals = true;
|
||||
showPoints = true;
|
||||
showConstraints = true;
|
||||
showHdnLines = false;
|
||||
showShaded = true;
|
||||
showEdges = true;
|
||||
showMesh = false;
|
||||
|
||||
showTextWindow = true;
|
||||
ShowTextWindow(showTextWindow);
|
||||
|
||||
showSnapGrid = false;
|
||||
context.active = false;
|
||||
|
||||
// Do this last, so that all the menus get updated correctly.
|
||||
EnsureValidActives();
|
||||
}
|
||||
|
||||
void GraphicsWindow::AnimateOntoWorkplane(void) {
|
||||
if(!LockedInWorkplane()) return;
|
||||
|
||||
Entity *w = SK.GetEntity(ActiveWorkplane());
|
||||
Quaternion quatf = w->Normal()->NormalGetNum();
|
||||
Vector offsetf = (SK.GetEntity(w->point[0])->PointGetNum()).ScaledBy(-1);
|
||||
|
||||
AnimateOnto(quatf, offsetf);
|
||||
}
|
||||
|
||||
void GraphicsWindow::AnimateOnto(Quaternion quatf, Vector offsetf) {
|
||||
// Get our initial orientation and translation.
|
||||
Quaternion quat0 = Quaternion::From(projRight, projUp);
|
||||
Vector offset0 = offset;
|
||||
|
||||
// Make sure we take the shorter of the two possible paths.
|
||||
double mp = (quatf.Minus(quat0)).Magnitude();
|
||||
double mm = (quatf.Plus(quat0)).Magnitude();
|
||||
if(mp > mm) {
|
||||
quatf = quatf.ScaledBy(-1);
|
||||
mp = mm;
|
||||
}
|
||||
double mo = (offset0.Minus(offsetf)).Magnitude()*scale;
|
||||
|
||||
// Animate transition, unless it's a tiny move.
|
||||
SDWORD dt = (mp < 0.01 && mo < 10) ? (-20) :
|
||||
(SDWORD)(100 + 1000*mp + 0.4*mo);
|
||||
// Don't ever animate for longer than 2000 ms; we can get absurdly
|
||||
// long translations (as measured in pixels) if the user zooms out, moves,
|
||||
// and then zooms in again.
|
||||
if(dt > 2000) dt = 2000;
|
||||
SDWORD tn, t0 = GetMilliseconds();
|
||||
double s = 0;
|
||||
Quaternion dq = quatf.Times(quat0.Inverse());
|
||||
do {
|
||||
offset = (offset0.ScaledBy(1 - s)).Plus(offsetf.ScaledBy(s));
|
||||
Quaternion quat = (dq.ToThe(s)).Times(quat0);
|
||||
quat = quat.WithMagnitude(1);
|
||||
|
||||
projRight = quat.RotationU();
|
||||
projUp = quat.RotationV();
|
||||
PaintGraphics();
|
||||
|
||||
tn = GetMilliseconds();
|
||||
s = (tn - t0)/((double)dt);
|
||||
} while((tn - t0) < dt);
|
||||
|
||||
projRight = quatf.RotationU();
|
||||
projUp = quatf.RotationV();
|
||||
offset = offsetf;
|
||||
InvalidateGraphics();
|
||||
// If the view screen is open, then we need to refresh it.
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
|
||||
void GraphicsWindow::HandlePointForZoomToFit(Vector p,
|
||||
Point2d *pmax, Point2d *pmin, double *wmin, bool div)
|
||||
{
|
||||
double w;
|
||||
Vector pp = ProjectPoint4(p, &w);
|
||||
// If div is true, then we calculate a perspective projection of the point.
|
||||
// If not, then we do a parallel projection regardless of the current
|
||||
// scale factor.
|
||||
if(div) {
|
||||
pp = pp.ScaledBy(1.0/w);
|
||||
}
|
||||
|
||||
pmax->x = max(pmax->x, pp.x);
|
||||
pmax->y = max(pmax->y, pp.y);
|
||||
pmin->x = min(pmin->x, pp.x);
|
||||
pmin->y = min(pmin->y, pp.y);
|
||||
*wmin = min(*wmin, w);
|
||||
}
|
||||
void GraphicsWindow::LoopOverPoints(Point2d *pmax, Point2d *pmin, double *wmin,
|
||||
bool div, bool includingInvisibles)
|
||||
{
|
||||
HandlePointForZoomToFit(Vector::From(0, 0, 0), pmax, pmin, wmin, div);
|
||||
|
||||
int i, j;
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
if(!(e->IsVisible() || includingInvisibles)) continue;
|
||||
if(e->IsPoint()) {
|
||||
HandlePointForZoomToFit(e->PointGetNum(), pmax, pmin, wmin, div);
|
||||
} else if(e->type == Entity::CIRCLE) {
|
||||
// Lots of entities can extend outside the bbox of their points,
|
||||
// but circles are particularly bad. We want to get things halfway
|
||||
// reasonable without the mesh, because a zoom to fit is used to
|
||||
// set the zoom level to set the chord tol.
|
||||
double r = e->CircleGetRadiusNum();
|
||||
Vector c = SK.GetEntity(e->point[0])->PointGetNum();
|
||||
Quaternion q = SK.GetEntity(e->normal)->NormalGetNum();
|
||||
for(j = 0; j < 4; j++) {
|
||||
Vector p = (j == 0) ? (c.Plus(q.RotationU().ScaledBy( r))) :
|
||||
(j == 1) ? (c.Plus(q.RotationU().ScaledBy(-r))) :
|
||||
(j == 2) ? (c.Plus(q.RotationV().ScaledBy( r))) :
|
||||
(c.Plus(q.RotationV().ScaledBy(-r)));
|
||||
HandlePointForZoomToFit(p, pmax, pmin, wmin, div);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Group *g = SK.GetGroup(activeGroup);
|
||||
g->GenerateDisplayItems();
|
||||
for(i = 0; i < g->displayMesh.l.n; i++) {
|
||||
STriangle *tr = &(g->displayMesh.l.elem[i]);
|
||||
HandlePointForZoomToFit(tr->a, pmax, pmin, wmin, div);
|
||||
HandlePointForZoomToFit(tr->b, pmax, pmin, wmin, div);
|
||||
HandlePointForZoomToFit(tr->c, pmax, pmin, wmin, div);
|
||||
}
|
||||
for(i = 0; i < g->polyLoops.l.n; i++) {
|
||||
SContour *sc = &(g->polyLoops.l.elem[i]);
|
||||
for(j = 0; j < sc->l.n; j++) {
|
||||
HandlePointForZoomToFit(sc->l.elem[j].p, pmax, pmin, wmin, div);
|
||||
}
|
||||
}
|
||||
}
|
||||
void GraphicsWindow::ZoomToFit(bool includingInvisibles) {
|
||||
// On the first run, ignore perspective.
|
||||
Point2d pmax = { -1e12, -1e12 }, pmin = { 1e12, 1e12 };
|
||||
double wmin = 1;
|
||||
LoopOverPoints(&pmax, &pmin, &wmin, false, includingInvisibles);
|
||||
|
||||
double xm = (pmax.x + pmin.x)/2, ym = (pmax.y + pmin.y)/2;
|
||||
double dx = pmax.x - pmin.x, dy = pmax.y - pmin.y;
|
||||
|
||||
offset = offset.Plus(projRight.ScaledBy(-xm)).Plus(
|
||||
projUp. ScaledBy(-ym));
|
||||
|
||||
// And based on this, we calculate the scale and offset
|
||||
if(dx == 0 && dy == 0) {
|
||||
scale = 5;
|
||||
} else {
|
||||
double scalex = 1e12, scaley = 1e12;
|
||||
if(dx != 0) scalex = 0.9*width /dx;
|
||||
if(dy != 0) scaley = 0.9*height/dy;
|
||||
scale = min(scalex, scaley);
|
||||
|
||||
scale = min(300, scale);
|
||||
scale = max(0.003, scale);
|
||||
}
|
||||
|
||||
// Then do another run, considering the perspective.
|
||||
pmax.x = -1e12; pmax.y = -1e12;
|
||||
pmin.x = 1e12; pmin.y = 1e12;
|
||||
wmin = 1;
|
||||
LoopOverPoints(&pmax, &pmin, &wmin, true, includingInvisibles);
|
||||
|
||||
// Adjust the scale so that no points are behind the camera
|
||||
if(wmin < 0.1) {
|
||||
double k = SS.CameraTangent();
|
||||
// w = 1+k*scale*z
|
||||
double zmin = (wmin - 1)/(k*scale);
|
||||
// 0.1 = 1 + k*scale*zmin
|
||||
// (0.1 - 1)/(k*zmin) = scale
|
||||
scale = min(scale, (0.1 - 1)/(k*zmin));
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::MenuView(int id) {
|
||||
switch(id) {
|
||||
case MNU_ZOOM_IN:
|
||||
SS.GW.scale *= 1.2;
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
|
||||
case MNU_ZOOM_OUT:
|
||||
SS.GW.scale /= 1.2;
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
|
||||
case MNU_ZOOM_TO_FIT:
|
||||
SS.GW.ZoomToFit(false);
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
|
||||
case MNU_SHOW_GRID:
|
||||
SS.GW.showSnapGrid = !SS.GW.showSnapGrid;
|
||||
if(SS.GW.showSnapGrid && !SS.GW.LockedInWorkplane()) {
|
||||
Message("No workplane is active, so the grid will not "
|
||||
"appear.");
|
||||
}
|
||||
SS.GW.EnsureValidActives();
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
case MNU_PERSPECTIVE_PROJ:
|
||||
SS.usePerspectiveProj = !SS.usePerspectiveProj;
|
||||
if(SS.cameraTangent < 1e-6) {
|
||||
Error("The perspective factor is set to zero, so the view will "
|
||||
"always be a parallel projection.\n\n"
|
||||
"For a perspective projection, modify the perspective "
|
||||
"factor in the configuration screen. A value around 0.3 "
|
||||
"is typical.");
|
||||
}
|
||||
SS.GW.EnsureValidActives();
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
case MNU_ONTO_WORKPLANE:
|
||||
if(!SS.GW.LockedInWorkplane()) {
|
||||
Error("No workplane is active.");
|
||||
break;
|
||||
}
|
||||
SS.GW.AnimateOntoWorkplane();
|
||||
SS.GW.ClearSuper();
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
|
||||
case MNU_NEAREST_ORTHO:
|
||||
case MNU_NEAREST_ISO: {
|
||||
static const Vector ortho[3] = {
|
||||
Vector::From(1, 0, 0),
|
||||
Vector::From(0, 1, 0),
|
||||
Vector::From(0, 0, 1)
|
||||
};
|
||||
double sqrt2 = sqrt(2.0), sqrt6 = sqrt(6.0);
|
||||
Quaternion quat0 = Quaternion::From(SS.GW.projRight, SS.GW.projUp);
|
||||
Quaternion quatf = quat0;
|
||||
double dmin = 1e10;
|
||||
|
||||
// There are 24 possible views; 3*2*2*2
|
||||
int i, j, negi, negj;
|
||||
for(i = 0; i < 3; i++) {
|
||||
for(j = 0; j < 3; j++) {
|
||||
if(i == j) continue;
|
||||
for(negi = 0; negi < 2; negi++) {
|
||||
for(negj = 0; negj < 2; negj++) {
|
||||
Vector ou = ortho[i], ov = ortho[j];
|
||||
if(negi) ou = ou.ScaledBy(-1);
|
||||
if(negj) ov = ov.ScaledBy(-1);
|
||||
Vector on = ou.Cross(ov);
|
||||
|
||||
Vector u, v;
|
||||
if(id == MNU_NEAREST_ORTHO) {
|
||||
u = ou;
|
||||
v = ov;
|
||||
} else {
|
||||
u =
|
||||
ou.ScaledBy(1/sqrt2).Plus(
|
||||
on.ScaledBy(-1/sqrt2));
|
||||
v =
|
||||
ou.ScaledBy(-1/sqrt6).Plus(
|
||||
ov.ScaledBy(2/sqrt6).Plus(
|
||||
on.ScaledBy(-1/sqrt6)));
|
||||
}
|
||||
|
||||
Quaternion quatt = Quaternion::From(u, v);
|
||||
double d = min(
|
||||
(quatt.Minus(quat0)).Magnitude(),
|
||||
(quatt.Plus(quat0)).Magnitude());
|
||||
if(d < dmin) {
|
||||
dmin = d;
|
||||
quatf = quatt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SS.GW.AnimateOnto(quatf, SS.GW.offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_CENTER_VIEW:
|
||||
SS.GW.GroupSelection();
|
||||
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
|
||||
Quaternion quat0;
|
||||
// Offset is the selected point, quaternion is same as before
|
||||
Vector pt = SK.GetEntity(SS.GW.gs.point[0])->PointGetNum();
|
||||
quat0 = Quaternion::From(SS.GW.projRight, SS.GW.projUp);
|
||||
SS.GW.AnimateOnto(quat0, pt.ScaledBy(-1));
|
||||
SS.GW.ClearSelection();
|
||||
} else {
|
||||
Error("Select a point; this point will become the center "
|
||||
"of the view on screen.");
|
||||
}
|
||||
break;
|
||||
|
||||
case MNU_SHOW_TEXT_WND:
|
||||
SS.GW.showTextWindow = !SS.GW.showTextWindow;
|
||||
SS.GW.EnsureValidActives();
|
||||
break;
|
||||
|
||||
case MNU_SHOW_TOOLBAR:
|
||||
SS.showToolbar = !SS.showToolbar;
|
||||
SS.GW.EnsureValidActives();
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
case MNU_UNITS_MM:
|
||||
SS.viewUnits = SolveSpace::UNIT_MM;
|
||||
SS.later.showTW = true;
|
||||
SS.GW.EnsureValidActives();
|
||||
break;
|
||||
|
||||
case MNU_UNITS_INCHES:
|
||||
SS.viewUnits = SolveSpace::UNIT_INCHES;
|
||||
SS.later.showTW = true;
|
||||
SS.GW.EnsureValidActives();
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
void GraphicsWindow::EnsureValidActives(void) {
|
||||
bool change = false;
|
||||
// The active group must exist, and not be the references.
|
||||
Group *g = SK.group.FindByIdNoOops(activeGroup);
|
||||
if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) {
|
||||
int i;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
if(SK.group.elem[i].h.v != Group::HGROUP_REFERENCES.v) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i >= SK.group.n) {
|
||||
// This can happen if the user deletes all the groups in the
|
||||
// sketch. It's difficult to prevent that, because the last
|
||||
// group might have been deleted automatically, because it failed
|
||||
// a dependency. There needs to be something, so create a plane
|
||||
// drawing group and activate that. They should never be able
|
||||
// to delete the references, though.
|
||||
activeGroup = SS.CreateDefaultDrawingGroup();
|
||||
} else {
|
||||
activeGroup = SK.group.elem[i].h;
|
||||
}
|
||||
SK.GetGroup(activeGroup)->Activate();
|
||||
change = true;
|
||||
}
|
||||
|
||||
// The active coordinate system must also exist.
|
||||
if(LockedInWorkplane()) {
|
||||
Entity *e = SK.entity.FindByIdNoOops(ActiveWorkplane());
|
||||
if(e) {
|
||||
hGroup hgw = e->group;
|
||||
if(hgw.v != activeGroup.v && SS.GroupsInOrder(activeGroup, hgw)) {
|
||||
// The active workplane is in a group that comes after the
|
||||
// active group; so any request or constraint will fail.
|
||||
SetWorkplaneFreeIn3d();
|
||||
change = true;
|
||||
}
|
||||
} else {
|
||||
SetWorkplaneFreeIn3d();
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
|
||||
// And update the checked state for various menus
|
||||
bool locked = LockedInWorkplane();
|
||||
CheckMenuById(MNU_FREE_IN_3D, !locked);
|
||||
CheckMenuById(MNU_SEL_WORKPLANE, locked);
|
||||
|
||||
SS.UndoEnableMenus();
|
||||
|
||||
switch(SS.viewUnits) {
|
||||
case SolveSpace::UNIT_MM:
|
||||
case SolveSpace::UNIT_INCHES:
|
||||
break;
|
||||
default:
|
||||
SS.viewUnits = SolveSpace::UNIT_MM;
|
||||
break;
|
||||
}
|
||||
CheckMenuById(MNU_UNITS_MM, SS.viewUnits == SolveSpace::UNIT_MM);
|
||||
CheckMenuById(MNU_UNITS_INCHES, SS.viewUnits == SolveSpace::UNIT_INCHES);
|
||||
|
||||
ShowTextWindow(SS.GW.showTextWindow);
|
||||
CheckMenuById(MNU_SHOW_TEXT_WND, SS.GW.showTextWindow);
|
||||
|
||||
CheckMenuById(MNU_SHOW_TOOLBAR, SS.showToolbar);
|
||||
CheckMenuById(MNU_PERSPECTIVE_PROJ, SS.usePerspectiveProj);
|
||||
CheckMenuById(MNU_SHOW_GRID, SS.GW.showSnapGrid);
|
||||
|
||||
if(change) SS.later.showTW = true;
|
||||
}
|
||||
|
||||
void GraphicsWindow::SetWorkplaneFreeIn3d(void) {
|
||||
SK.GetGroup(activeGroup)->activeWorkplane = Entity::FREE_IN_3D;
|
||||
}
|
||||
hEntity GraphicsWindow::ActiveWorkplane(void) {
|
||||
Group *g = SK.group.FindByIdNoOops(activeGroup);
|
||||
if(g) {
|
||||
return g->activeWorkplane;
|
||||
} else {
|
||||
return Entity::FREE_IN_3D;
|
||||
}
|
||||
}
|
||||
bool GraphicsWindow::LockedInWorkplane(void) {
|
||||
return (SS.GW.ActiveWorkplane().v != Entity::FREE_IN_3D.v);
|
||||
}
|
||||
|
||||
void GraphicsWindow::ForceTextWindowShown(void) {
|
||||
if(!showTextWindow) {
|
||||
showTextWindow = true;
|
||||
CheckMenuById(MNU_SHOW_TEXT_WND, true);
|
||||
ShowTextWindow(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::DeleteTaggedRequests(void) {
|
||||
// Rewrite any point-coincident constraints that were affected by this
|
||||
// deletion.
|
||||
Request *r;
|
||||
for(r = SK.request.First(); r; r = SK.request.NextAfter(r)) {
|
||||
if(!r->tag) continue;
|
||||
FixConstraintsForRequestBeingDeleted(r->h);
|
||||
}
|
||||
// and then delete the tagged requests.
|
||||
SK.request.RemoveTagged();
|
||||
|
||||
// An edit might be in progress for the just-deleted item. So
|
||||
// now it's not.
|
||||
HideGraphicsEditControl();
|
||||
SS.TW.HideEditControl();
|
||||
// And clear out the selection, which could contain that item.
|
||||
ClearSuper();
|
||||
// And regenerate to get rid of what it generates, plus anything
|
||||
// that references it (since the regen code checks for that).
|
||||
SS.GenerateAll(0, INT_MAX);
|
||||
EnsureValidActives();
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
|
||||
Vector GraphicsWindow::SnapToGrid(Vector p) {
|
||||
if(!LockedInWorkplane()) return p;
|
||||
|
||||
EntityBase *wrkpl = SK.GetEntity(ActiveWorkplane()),
|
||||
*norm = wrkpl->Normal();
|
||||
Vector wo = SK.GetEntity(wrkpl->point[0])->PointGetNum(),
|
||||
wu = norm->NormalU(),
|
||||
wv = norm->NormalV(),
|
||||
wn = norm->NormalN();
|
||||
|
||||
Vector pp = (p.Minus(wo)).DotInToCsys(wu, wv, wn);
|
||||
pp.x = floor((pp.x / SS.gridSpacing) + 0.5)*SS.gridSpacing;
|
||||
pp.y = floor((pp.y / SS.gridSpacing) + 0.5)*SS.gridSpacing;
|
||||
pp.z = 0;
|
||||
|
||||
return pp.ScaleOutOfCsys(wu, wv, wn).Plus(wo);
|
||||
}
|
||||
|
||||
void GraphicsWindow::MenuEdit(int id) {
|
||||
switch(id) {
|
||||
case MNU_UNSELECT_ALL:
|
||||
SS.GW.GroupSelection();
|
||||
// If there's nothing selected to de-select, and no operation
|
||||
// to cancel, then perhaps they want to return to the home
|
||||
// screen in the text window.
|
||||
if(SS.GW.gs.n == 0 &&
|
||||
SS.GW.gs.constraints == 0 &&
|
||||
SS.GW.pending.operation == 0)
|
||||
{
|
||||
if(!(TextEditControlIsVisible() ||
|
||||
GraphicsEditControlIsVisible()))
|
||||
{
|
||||
if(SS.TW.shown.screen == TextWindow::SCREEN_STYLE_INFO) {
|
||||
SS.TW.GoToScreen(TextWindow::SCREEN_LIST_OF_STYLES);
|
||||
} else {
|
||||
SS.TW.ClearSuper();
|
||||
}
|
||||
}
|
||||
}
|
||||
SS.GW.ClearSuper();
|
||||
SS.TW.HideEditControl();
|
||||
SS.nakedEdges.Clear();
|
||||
SS.justExportedInfo.draw = false;
|
||||
// This clears the marks drawn to indicate which points are
|
||||
// still free to drag.
|
||||
Param *p;
|
||||
for(p = SK.param.First(); p; p = SK.param.NextAfter(p)) {
|
||||
p->free = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case MNU_SELECT_ALL: {
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group.v != SS.GW.activeGroup.v) continue;
|
||||
if(e->IsFace() || e->IsDistance()) continue;
|
||||
if(!e->IsVisible()) continue;
|
||||
|
||||
SS.GW.MakeSelected(e->h);
|
||||
}
|
||||
InvalidateGraphics();
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_SELECT_CHAIN: {
|
||||
Entity *e;
|
||||
int newlySelected = 0;
|
||||
bool didSomething;
|
||||
do {
|
||||
didSomething = false;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group.v != SS.GW.activeGroup.v) continue;
|
||||
if(!e->HasEndpoints()) continue;
|
||||
if(!e->IsVisible()) continue;
|
||||
|
||||
Vector st = e->EndpointStart(),
|
||||
fi = e->EndpointFinish();
|
||||
|
||||
bool onChain = false, alreadySelected = false;
|
||||
List<Selection> *ls = &(SS.GW.selection);
|
||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||
if(!s->entity.v) continue;
|
||||
if(s->entity.v == e->h.v) {
|
||||
alreadySelected = true;
|
||||
continue;
|
||||
}
|
||||
Entity *se = SK.GetEntity(s->entity);
|
||||
if(!se->HasEndpoints()) continue;
|
||||
|
||||
Vector sst = se->EndpointStart(),
|
||||
sfi = se->EndpointFinish();
|
||||
|
||||
if(sst.Equals(st) || sst.Equals(fi) ||
|
||||
sfi.Equals(st) || sfi.Equals(fi))
|
||||
{
|
||||
onChain = true;
|
||||
}
|
||||
}
|
||||
if(onChain && !alreadySelected) {
|
||||
SS.GW.MakeSelected(e->h);
|
||||
newlySelected++;
|
||||
didSomething = true;
|
||||
}
|
||||
}
|
||||
} while(didSomething);
|
||||
if(newlySelected == 0) {
|
||||
Error("No additional entities share endpoints with the "
|
||||
"selected entities.");
|
||||
}
|
||||
InvalidateGraphics();
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_ROTATE_90: {
|
||||
SS.GW.GroupSelection();
|
||||
Entity *e = NULL;
|
||||
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
|
||||
e = SK.GetEntity(SS.GW.gs.point[0]);
|
||||
} else if(SS.GW.gs.n == 1 && SS.GW.gs.entities == 1) {
|
||||
e = SK.GetEntity(SS.GW.gs.entity[0]);
|
||||
}
|
||||
SS.GW.ClearSelection();
|
||||
|
||||
hGroup hg = e ? e->group : SS.GW.activeGroup;
|
||||
Group *g = SK.GetGroup(hg);
|
||||
if(g->type != Group::IMPORTED) {
|
||||
Error("To use this command, select a point or other "
|
||||
"entity from an imported part, or make an import "
|
||||
"group the active group.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
SS.UndoRemember();
|
||||
// Rotate by ninety degrees about the coordinate axis closest
|
||||
// to the screen normal.
|
||||
Vector norm = SS.GW.projRight.Cross(SS.GW.projUp);
|
||||
norm = norm.ClosestOrtho();
|
||||
norm = norm.WithMagnitude(1);
|
||||
Quaternion qaa = Quaternion::From(norm, PI/2);
|
||||
|
||||
g->TransformImportedBy(Vector::From(0, 0, 0), qaa);
|
||||
|
||||
// and regenerate as necessary.
|
||||
SS.MarkGroupDirty(hg);
|
||||
SS.GenerateAll();
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_SNAP_TO_GRID: {
|
||||
if(!SS.GW.LockedInWorkplane()) {
|
||||
Error("No workplane is active. Select a workplane to define "
|
||||
"the plane for the snap grid.");
|
||||
break;
|
||||
}
|
||||
SS.GW.GroupSelection();
|
||||
if(SS.GW.gs.points == 0 && SS.GW.gs.comments == 0) {
|
||||
Error("Can't snap these items to grid; select points or "
|
||||
"text comments. To snap a line, select its endpoints.");
|
||||
break;
|
||||
}
|
||||
SS.UndoRemember();
|
||||
|
||||
List<Selection> *ls = &(SS.GW.selection);
|
||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||
if(s->entity.v) {
|
||||
hEntity hp = s->entity;
|
||||
Entity *ep = SK.GetEntity(hp);
|
||||
if(!ep->IsPoint()) continue;
|
||||
|
||||
Vector p = ep->PointGetNum();
|
||||
ep->PointForceTo(SS.GW.SnapToGrid(p));
|
||||
SS.GW.pending.points.Add(&hp);
|
||||
SS.MarkGroupDirty(ep->group);
|
||||
} else if(s->constraint.v) {
|
||||
Constraint *c = SK.GetConstraint(s->constraint);
|
||||
if(c->type != Constraint::COMMENT) continue;
|
||||
|
||||
c->disp.offset = SS.GW.SnapToGrid(c->disp.offset);
|
||||
}
|
||||
}
|
||||
// Regenerate, with these points marked as dragged so that they
|
||||
// get placed as close as possible to our snap grid.
|
||||
SS.GenerateAll();
|
||||
SS.GW.ClearPending();
|
||||
|
||||
SS.GW.ClearSelection();
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_UNDO:
|
||||
SS.UndoUndo();
|
||||
break;
|
||||
|
||||
case MNU_REDO:
|
||||
SS.UndoRedo();
|
||||
break;
|
||||
|
||||
case MNU_REGEN_ALL:
|
||||
SS.ReloadAllImported();
|
||||
SS.GenerateAll(0, INT_MAX);
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::MenuRequest(int id) {
|
||||
char *s;
|
||||
switch(id) {
|
||||
case MNU_SEL_WORKPLANE: {
|
||||
SS.GW.GroupSelection();
|
||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
||||
|
||||
if(SS.GW.gs.n == 1 && SS.GW.gs.workplanes == 1) {
|
||||
// A user-selected workplane
|
||||
g->activeWorkplane = SS.GW.gs.entity[0];
|
||||
} else if(g->type == Group::DRAWING_WORKPLANE) {
|
||||
// The group's default workplane
|
||||
g->activeWorkplane = g->h.entity(0);
|
||||
Message("No workplane selected. Activating default workplane "
|
||||
"for this group.");
|
||||
}
|
||||
|
||||
if(!SS.GW.LockedInWorkplane()) {
|
||||
Error("No workplane is selected, and the active group does "
|
||||
"not have a default workplane. Try selecting a "
|
||||
"workplane, or activating a sketch-in-new-workplane "
|
||||
"group.");
|
||||
break;
|
||||
}
|
||||
// Align the view with the selected workplane
|
||||
SS.GW.AnimateOntoWorkplane();
|
||||
SS.GW.ClearSuper();
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
}
|
||||
case MNU_FREE_IN_3D:
|
||||
SS.GW.SetWorkplaneFreeIn3d();
|
||||
SS.GW.EnsureValidActives();
|
||||
SS.later.showTW = true;
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
|
||||
case MNU_TANGENT_ARC:
|
||||
SS.GW.GroupSelection();
|
||||
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
|
||||
SS.GW.MakeTangentArc();
|
||||
} else if(SS.GW.gs.n != 0) {
|
||||
Error("Bad selection for tangent arc at point. Select a "
|
||||
"single point, or select nothing to set up arc "
|
||||
"parameters.");
|
||||
} else {
|
||||
SS.TW.GoToScreen(TextWindow::SCREEN_TANGENT_ARC);
|
||||
SS.GW.ForceTextWindowShown();
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case MNU_ARC: s = "click point on arc (draws anti-clockwise)"; goto c;
|
||||
case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
|
||||
case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c;
|
||||
case MNU_CUBIC: s = "click first point of cubic segment"; goto c;
|
||||
case MNU_CIRCLE: s = "click center of circle"; goto c;
|
||||
case MNU_WORKPLANE: s = "click origin of workplane"; goto c;
|
||||
case MNU_RECTANGLE: s = "click one corner of rectangle"; goto c;
|
||||
case MNU_TTF_TEXT: s = "click top left of text"; goto c;
|
||||
c:
|
||||
SS.GW.pending.operation = id;
|
||||
SS.GW.pending.description = s;
|
||||
SS.later.showTW = true;
|
||||
break;
|
||||
|
||||
case MNU_CONSTRUCTION: {
|
||||
SS.UndoRemember();
|
||||
SS.GW.GroupSelection();
|
||||
if(SS.GW.gs.entities == 0) {
|
||||
Error("No entities are selected. Select entities before "
|
||||
"trying to toggle their construction state.");
|
||||
}
|
||||
int i;
|
||||
for(i = 0; i < SS.GW.gs.entities; i++) {
|
||||
hEntity he = SS.GW.gs.entity[i];
|
||||
if(!he.isFromRequest()) continue;
|
||||
Request *r = SK.GetRequest(he.request());
|
||||
r->construction = !(r->construction);
|
||||
SS.MarkGroupDirty(r->group);
|
||||
}
|
||||
SS.GW.ClearSelection();
|
||||
SS.GenerateAll();
|
||||
break;
|
||||
}
|
||||
|
||||
case MNU_SPLIT_CURVES:
|
||||
SS.GW.SplitLinesOrCurves();
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsWindow::ClearSuper(void) {
|
||||
HideGraphicsEditControl();
|
||||
ClearPending();
|
||||
ClearSelection();
|
||||
hover.Clear();
|
||||
EnsureValidActives();
|
||||
}
|
||||
|
||||
void GraphicsWindow::ToggleBool(bool *v) {
|
||||
*v = !*v;
|
||||
|
||||
// The faces are shown as special stippling on the shaded triangle mesh,
|
||||
// so not meaningful to show them and hide the shaded.
|
||||
if(!showShaded) showFaces = false;
|
||||
|
||||
// We might need to regenerate the mesh and edge list, since the edges
|
||||
// wouldn't have been generated if they were previously hidden.
|
||||
if(showEdges) (SK.GetGroup(activeGroup))->displayDirty = true;
|
||||
|
||||
SS.GenerateAll();
|
||||
InvalidateGraphics();
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
|
760
group.cpp
|
@ -1,760 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Implementation of the Group class, which represents a set of entities and
|
||||
// constraints that are solved together, in some cases followed by another
|
||||
// operation, like to extrude surfaces from the entities or to step and
|
||||
// repeat them parametrically.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
const hParam Param::NO_PARAM = { 0 };
|
||||
#define NO_PARAM (Param::NO_PARAM)
|
||||
|
||||
const hGroup Group::HGROUP_REFERENCES = { 1 };
|
||||
|
||||
#define gs (SS.GW.gs)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// The group structure includes pointers to other dynamically-allocated
|
||||
// memory. This clears and frees them all.
|
||||
//-----------------------------------------------------------------------------
|
||||
void Group::Clear(void) {
|
||||
polyLoops.Clear();
|
||||
bezierLoops.Clear();
|
||||
bezierOpens.Clear();
|
||||
thisMesh.Clear();
|
||||
runningMesh.Clear();
|
||||
thisShell.Clear();
|
||||
runningShell.Clear();
|
||||
displayMesh.Clear();
|
||||
displayEdges.Clear();
|
||||
impMesh.Clear();
|
||||
impShell.Clear();
|
||||
impEntity.Clear();
|
||||
// remap is the only one that doesn't get recreated when we regen
|
||||
remap.Clear();
|
||||
}
|
||||
|
||||
void Group::AddParam(IdList<Param,hParam> *param, hParam hp, double v) {
|
||||
Param pa;
|
||||
memset(&pa, 0, sizeof(pa));
|
||||
pa.h = hp;
|
||||
pa.val = v;
|
||||
|
||||
param->Add(&pa);
|
||||
}
|
||||
|
||||
bool Group::IsVisible(void) {
|
||||
if(!visible) return false;
|
||||
if(SS.GroupsInOrder(SS.GW.activeGroup, h)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Group::MenuGroup(int id) {
|
||||
Group g;
|
||||
ZERO(&g);
|
||||
g.visible = true;
|
||||
g.color = RGB(100, 100, 100);
|
||||
g.scale = 1;
|
||||
|
||||
if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
|
||||
strcpy(g.impFile, RecentFile[id-RECENT_IMPORT]);
|
||||
id = GraphicsWindow::MNU_GROUP_IMPORT;
|
||||
}
|
||||
|
||||
SS.GW.GroupSelection();
|
||||
|
||||
switch(id) {
|
||||
case GraphicsWindow::MNU_GROUP_3D:
|
||||
g.type = DRAWING_3D;
|
||||
g.name.strcpy("sketch-in-3d");
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_GROUP_WRKPL:
|
||||
g.type = DRAWING_WORKPLANE;
|
||||
g.name.strcpy("sketch-in-plane");
|
||||
if(gs.points == 1 && gs.n == 1) {
|
||||
g.subtype = WORKPLANE_BY_POINT_ORTHO;
|
||||
|
||||
Vector u = SS.GW.projRight, v = SS.GW.projUp;
|
||||
u = u.ClosestOrtho();
|
||||
v = v.Minus(u.ScaledBy(v.Dot(u)));
|
||||
v = v.ClosestOrtho();
|
||||
|
||||
g.predef.q = Quaternion::From(u, v);
|
||||
g.predef.origin = gs.point[0];
|
||||
} else if(gs.points == 1 && gs.lineSegments == 2 && gs.n == 3) {
|
||||
g.subtype = WORKPLANE_BY_LINE_SEGMENTS;
|
||||
|
||||
g.predef.origin = gs.point[0];
|
||||
g.predef.entityB = gs.entity[0];
|
||||
g.predef.entityC = gs.entity[1];
|
||||
|
||||
Vector ut = SK.GetEntity(g.predef.entityB)->VectorGetNum();
|
||||
Vector vt = SK.GetEntity(g.predef.entityC)->VectorGetNum();
|
||||
ut = ut.WithMagnitude(1);
|
||||
vt = vt.WithMagnitude(1);
|
||||
|
||||
if(fabs(SS.GW.projUp.Dot(vt)) < fabs(SS.GW.projUp.Dot(ut))) {
|
||||
SWAP(Vector, ut, vt);
|
||||
g.predef.swapUV = true;
|
||||
}
|
||||
if(SS.GW.projRight.Dot(ut) < 0) g.predef.negateU = true;
|
||||
if(SS.GW.projUp. Dot(vt) < 0) g.predef.negateV = true;
|
||||
} else {
|
||||
Error("Bad selection for new sketch in workplane. This "
|
||||
"group can be created with:\n\n"
|
||||
" * a point (orthogonal to coordinate axes, "
|
||||
"through the point)\n"
|
||||
" * a point and two line segments (parallel to the "
|
||||
"lines, through the point)\n");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_GROUP_EXTRUDE:
|
||||
if(!SS.GW.LockedInWorkplane()) {
|
||||
Error("Select a workplane (Sketch -> In Workplane) before "
|
||||
"extruding. The sketch will be extruded normal to the "
|
||||
"workplane.");
|
||||
return;
|
||||
}
|
||||
g.type = EXTRUDE;
|
||||
g.opA = SS.GW.activeGroup;
|
||||
g.predef.entityB = SS.GW.ActiveWorkplane();
|
||||
g.subtype = ONE_SIDED;
|
||||
g.name.strcpy("extrude");
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_GROUP_LATHE:
|
||||
if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
|
||||
g.predef.origin = gs.point[0];
|
||||
g.predef.entityB = gs.vector[0];
|
||||
} else if(gs.lineSegments == 1 && gs.n == 1) {
|
||||
g.predef.origin = SK.GetEntity(gs.entity[0])->point[0];
|
||||
g.predef.entityB = gs.entity[0];
|
||||
// since a line segment is a vector
|
||||
} else {
|
||||
Error("Bad selection for new lathe group. This group can "
|
||||
"be created with:\n\n"
|
||||
" * a point and a line segment or normal "
|
||||
"(revolved about an axis parallel to line / "
|
||||
"normal, through point)\n"
|
||||
" * a line segment (revolved about line segment)\n");
|
||||
return;
|
||||
}
|
||||
g.type = LATHE;
|
||||
g.opA = SS.GW.activeGroup;
|
||||
g.name.strcpy("lathe");
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_GROUP_ROT: {
|
||||
if(gs.points == 1 && gs.n == 1 && SS.GW.LockedInWorkplane()) {
|
||||
g.predef.origin = gs.point[0];
|
||||
Entity *w = SK.GetEntity(SS.GW.ActiveWorkplane());
|
||||
g.predef.entityB = w->Normal()->h;
|
||||
g.activeWorkplane = w->h;
|
||||
} else if(gs.points == 1 && gs.vectors == 1 && gs.n == 2) {
|
||||
g.predef.origin = gs.point[0];
|
||||
g.predef.entityB = gs.vector[0];
|
||||
} else {
|
||||
Error("Bad selection for new rotation. This group can "
|
||||
"be created with:\n\n"
|
||||
" * a point, while locked in workplane (rotate "
|
||||
"in plane, about that point)\n"
|
||||
" * a point and a line or a normal (rotate about "
|
||||
"an axis through the point, and parallel to "
|
||||
"line / normal)\n");
|
||||
return;
|
||||
}
|
||||
g.type = ROTATE;
|
||||
g.opA = SS.GW.activeGroup;
|
||||
g.valA = 3;
|
||||
g.subtype = ONE_SIDED;
|
||||
g.name.strcpy("rotate");
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_GROUP_TRANS:
|
||||
g.type = TRANSLATE;
|
||||
g.opA = SS.GW.activeGroup;
|
||||
g.valA = 3;
|
||||
g.subtype = ONE_SIDED;
|
||||
g.predef.entityB = SS.GW.ActiveWorkplane();
|
||||
g.activeWorkplane = SS.GW.ActiveWorkplane();
|
||||
g.name.strcpy("translate");
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_GROUP_IMPORT: {
|
||||
g.type = IMPORTED;
|
||||
g.opA = SS.GW.activeGroup;
|
||||
if(strlen(g.impFile) == 0) {
|
||||
if(!GetOpenFile(g.impFile, SLVS_EXT, SLVS_PATTERN)) return;
|
||||
}
|
||||
|
||||
// Assign the default name of the group based on the name of
|
||||
// the imported file.
|
||||
char groupName[MAX_PATH];
|
||||
strcpy(groupName, g.impFile);
|
||||
char *dot = strrchr(groupName, '.');
|
||||
if(dot) *dot = '\0';
|
||||
|
||||
char *s, *start = groupName;
|
||||
for(s = groupName; *s; s++) {
|
||||
if(*s == '/' || *s == '\\') {
|
||||
start = s + 1;
|
||||
} else if(isalnum(*s)) {
|
||||
// do nothing, valid character
|
||||
} else {
|
||||
// convert invalid characters (like spaces) to dashes
|
||||
*s = '-';
|
||||
}
|
||||
}
|
||||
if(strlen(start) > 0) {
|
||||
g.name.strcpy(start);
|
||||
} else {
|
||||
g.name.strcpy("import");
|
||||
}
|
||||
|
||||
g.meshCombine = COMBINE_AS_ASSEMBLE;
|
||||
break;
|
||||
}
|
||||
|
||||
default: oops();
|
||||
}
|
||||
SS.GW.ClearSelection();
|
||||
SS.UndoRemember();
|
||||
|
||||
SK.group.AddAndAssignId(&g);
|
||||
Group *gg = SK.GetGroup(g.h);
|
||||
|
||||
if(gg->type == IMPORTED) {
|
||||
SS.ReloadAllImported();
|
||||
}
|
||||
gg->clean = false;
|
||||
SS.GW.activeGroup = gg->h;
|
||||
SS.GenerateAll();
|
||||
if(gg->type == DRAWING_WORKPLANE) {
|
||||
// Can't set the active workplane for this one until after we've
|
||||
// regenerated, because the workplane doesn't exist until then.
|
||||
gg->activeWorkplane = gg->h.entity(0);
|
||||
}
|
||||
gg->Activate();
|
||||
SS.GW.AnimateOntoWorkplane();
|
||||
TextWindow::ScreenSelectGroup(0, gg->h.v);
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
|
||||
void Group::TransformImportedBy(Vector t, Quaternion q) {
|
||||
if(type != IMPORTED) oops();
|
||||
|
||||
hParam tx, ty, tz, qw, qx, qy, qz;
|
||||
tx = h.param(0);
|
||||
ty = h.param(1);
|
||||
tz = h.param(2);
|
||||
qw = h.param(3);
|
||||
qx = h.param(4);
|
||||
qy = h.param(5);
|
||||
qz = h.param(6);
|
||||
|
||||
Quaternion qg = Quaternion::From(qw, qx, qy, qz);
|
||||
qg = q.Times(qg);
|
||||
|
||||
Vector tg = Vector::From(tx, ty, tz);
|
||||
tg = tg.Plus(t);
|
||||
|
||||
SK.GetParam(tx)->val = tg.x;
|
||||
SK.GetParam(ty)->val = tg.y;
|
||||
SK.GetParam(tz)->val = tg.z;
|
||||
|
||||
SK.GetParam(qw)->val = qg.w;
|
||||
SK.GetParam(qx)->val = qg.vx;
|
||||
SK.GetParam(qy)->val = qg.vy;
|
||||
SK.GetParam(qz)->val = qg.vz;
|
||||
}
|
||||
|
||||
char *Group::DescriptionString(void) {
|
||||
static char ret[100];
|
||||
if(name.str[0]) {
|
||||
sprintf(ret, "g%03x-%s", h.v, name.str);
|
||||
} else {
|
||||
sprintf(ret, "g%03x-(unnamed)", h.v);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Group::Activate(void) {
|
||||
if(type == EXTRUDE || type == IMPORTED) {
|
||||
SS.GW.showFaces = true;
|
||||
} else {
|
||||
SS.GW.showFaces = false;
|
||||
}
|
||||
SS.MarkGroupDirty(h); // for good measure; shouldn't be needed
|
||||
SS.later.generateAll = true;
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
|
||||
void Group::Generate(IdList<Entity,hEntity> *entity,
|
||||
IdList<Param,hParam> *param)
|
||||
{
|
||||
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
|
||||
Vector gp = SS.GW.projRight.Plus(SS.GW.projUp);
|
||||
Vector gc = (SS.GW.offset).ScaledBy(-1);
|
||||
gn = gn.WithMagnitude(200/SS.GW.scale);
|
||||
gp = gp.WithMagnitude(200/SS.GW.scale);
|
||||
int a, i;
|
||||
switch(type) {
|
||||
case DRAWING_3D:
|
||||
break;
|
||||
|
||||
case DRAWING_WORKPLANE: {
|
||||
Quaternion q;
|
||||
if(subtype == WORKPLANE_BY_LINE_SEGMENTS) {
|
||||
Vector u = SK.GetEntity(predef.entityB)->VectorGetNum();
|
||||
Vector v = SK.GetEntity(predef.entityC)->VectorGetNum();
|
||||
u = u.WithMagnitude(1);
|
||||
Vector n = u.Cross(v);
|
||||
v = (n.Cross(u)).WithMagnitude(1);
|
||||
|
||||
if(predef.swapUV) SWAP(Vector, u, v);
|
||||
if(predef.negateU) u = u.ScaledBy(-1);
|
||||
if(predef.negateV) v = v.ScaledBy(-1);
|
||||
q = Quaternion::From(u, v);
|
||||
} else if(subtype == WORKPLANE_BY_POINT_ORTHO) {
|
||||
// Already given, numerically.
|
||||
q = predef.q;
|
||||
} else oops();
|
||||
|
||||
Entity normal;
|
||||
memset(&normal, 0, sizeof(normal));
|
||||
normal.type = Entity::NORMAL_N_COPY;
|
||||
normal.numNormal = q;
|
||||
normal.point[0] = h.entity(2);
|
||||
normal.group = h;
|
||||
normal.h = h.entity(1);
|
||||
entity->Add(&normal);
|
||||
|
||||
Entity point;
|
||||
memset(&point, 0, sizeof(point));
|
||||
point.type = Entity::POINT_N_COPY;
|
||||
point.numPoint = SK.GetEntity(predef.origin)->PointGetNum();
|
||||
point.group = h;
|
||||
point.h = h.entity(2);
|
||||
entity->Add(&point);
|
||||
|
||||
Entity wp;
|
||||
memset(&wp, 0, sizeof(wp));
|
||||
wp.type = Entity::WORKPLANE;
|
||||
wp.normal = normal.h;
|
||||
wp.point[0] = point.h;
|
||||
wp.group = h;
|
||||
wp.h = h.entity(0);
|
||||
entity->Add(&wp);
|
||||
break;
|
||||
}
|
||||
|
||||
case EXTRUDE: {
|
||||
AddParam(param, h.param(0), gn.x);
|
||||
AddParam(param, h.param(1), gn.y);
|
||||
AddParam(param, h.param(2), gn.z);
|
||||
int ai, af;
|
||||
if(subtype == ONE_SIDED) {
|
||||
ai = 0; af = 2;
|
||||
} else if(subtype == TWO_SIDED) {
|
||||
ai = -1; af = 1;
|
||||
} else oops();
|
||||
|
||||
// Get some arbitrary point in the sketch, that will be used
|
||||
// as a reference when defining top and bottom faces.
|
||||
hEntity pt = { 0 };
|
||||
for(i = 0; i < entity->n; i++) {
|
||||
Entity *e = &(entity->elem[i]);
|
||||
if(e->group.v != opA.v) continue;
|
||||
|
||||
if(e->IsPoint()) pt = e->h;
|
||||
|
||||
e->CalculateNumerical(false);
|
||||
hEntity he = e->h; e = NULL;
|
||||
// As soon as I call CopyEntity, e may become invalid! That
|
||||
// adds entities, which may cause a realloc.
|
||||
CopyEntity(entity, SK.GetEntity(he), ai, REMAP_BOTTOM,
|
||||
h.param(0), h.param(1), h.param(2),
|
||||
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
|
||||
true, false);
|
||||
CopyEntity(entity, SK.GetEntity(he), af, REMAP_TOP,
|
||||
h.param(0), h.param(1), h.param(2),
|
||||
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
|
||||
true, false);
|
||||
MakeExtrusionLines(entity, he);
|
||||
}
|
||||
// Remapped versions of that arbitrary point will be used to
|
||||
// provide points on the plane faces.
|
||||
MakeExtrusionTopBottomFaces(entity, pt);
|
||||
break;
|
||||
}
|
||||
|
||||
case LATHE: {
|
||||
break;
|
||||
}
|
||||
|
||||
case TRANSLATE: {
|
||||
// The translation vector
|
||||
AddParam(param, h.param(0), gp.x);
|
||||
AddParam(param, h.param(1), gp.y);
|
||||
AddParam(param, h.param(2), gp.z);
|
||||
|
||||
int n = (int)valA, a0 = 0;
|
||||
if(subtype == ONE_SIDED && skipFirst) {
|
||||
a0++; n++;
|
||||
}
|
||||
|
||||
for(a = a0; a < n; a++) {
|
||||
for(i = 0; i < entity->n; i++) {
|
||||
Entity *e = &(entity->elem[i]);
|
||||
if(e->group.v != opA.v) continue;
|
||||
|
||||
e->CalculateNumerical(false);
|
||||
CopyEntity(entity, e,
|
||||
a*2 - (subtype == ONE_SIDED ? 0 : (n-1)),
|
||||
(a == (n - 1)) ? REMAP_LAST : a,
|
||||
h.param(0), h.param(1), h.param(2),
|
||||
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
|
||||
true, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ROTATE: {
|
||||
// The center of rotation
|
||||
AddParam(param, h.param(0), gc.x);
|
||||
AddParam(param, h.param(1), gc.y);
|
||||
AddParam(param, h.param(2), gc.z);
|
||||
// The rotation quaternion
|
||||
AddParam(param, h.param(3), 30*PI/180);
|
||||
AddParam(param, h.param(4), gn.x);
|
||||
AddParam(param, h.param(5), gn.y);
|
||||
AddParam(param, h.param(6), gn.z);
|
||||
|
||||
int n = (int)valA, a0 = 0;
|
||||
if(subtype == ONE_SIDED && skipFirst) {
|
||||
a0++; n++;
|
||||
}
|
||||
|
||||
for(a = a0; a < n; a++) {
|
||||
for(i = 0; i < entity->n; i++) {
|
||||
Entity *e = &(entity->elem[i]);
|
||||
if(e->group.v != opA.v) continue;
|
||||
|
||||
e->CalculateNumerical(false);
|
||||
CopyEntity(entity, e,
|
||||
a*2 - (subtype == ONE_SIDED ? 0 : (n-1)),
|
||||
(a == (n - 1)) ? REMAP_LAST : a,
|
||||
h.param(0), h.param(1), h.param(2),
|
||||
h.param(3), h.param(4), h.param(5), h.param(6),
|
||||
false, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMPORTED:
|
||||
// The translation vector
|
||||
AddParam(param, h.param(0), gp.x);
|
||||
AddParam(param, h.param(1), gp.y);
|
||||
AddParam(param, h.param(2), gp.z);
|
||||
// The rotation quaternion
|
||||
AddParam(param, h.param(3), 1);
|
||||
AddParam(param, h.param(4), 0);
|
||||
AddParam(param, h.param(5), 0);
|
||||
AddParam(param, h.param(6), 0);
|
||||
|
||||
for(i = 0; i < impEntity.n; i++) {
|
||||
Entity *ie = &(impEntity.elem[i]);
|
||||
CopyEntity(entity, ie, 0, 0,
|
||||
h.param(0), h.param(1), h.param(2),
|
||||
h.param(3), h.param(4), h.param(5), h.param(6),
|
||||
false, false);
|
||||
}
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
void Group::AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index) {
|
||||
Equation eq;
|
||||
eq.e = expr;
|
||||
eq.h = h.equation(index);
|
||||
l->Add(&eq);
|
||||
}
|
||||
|
||||
void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
|
||||
Equation eq;
|
||||
ZERO(&eq);
|
||||
if(type == IMPORTED) {
|
||||
// Normalize the quaternion
|
||||
ExprQuaternion q = {
|
||||
Expr::From(h.param(3)),
|
||||
Expr::From(h.param(4)),
|
||||
Expr::From(h.param(5)),
|
||||
Expr::From(h.param(6)) };
|
||||
AddEq(l, (q.Magnitude())->Minus(Expr::From(1)), 0);
|
||||
} else if(type == ROTATE) {
|
||||
// The axis and center of rotation are specified numerically
|
||||
#define EC(x) (Expr::From(x))
|
||||
#define EP(x) (Expr::From(h.param(x)))
|
||||
ExprVector orig = SK.GetEntity(predef.origin)->PointGetExprs();
|
||||
AddEq(l, (orig.x)->Minus(EP(0)), 0);
|
||||
AddEq(l, (orig.y)->Minus(EP(1)), 1);
|
||||
AddEq(l, (orig.z)->Minus(EP(2)), 2);
|
||||
// param 3 is the angle, which is free
|
||||
Vector axis = SK.GetEntity(predef.entityB)->VectorGetNum();
|
||||
axis = axis.WithMagnitude(1);
|
||||
AddEq(l, (EC(axis.x))->Minus(EP(4)), 3);
|
||||
AddEq(l, (EC(axis.y))->Minus(EP(5)), 4);
|
||||
AddEq(l, (EC(axis.z))->Minus(EP(6)), 5);
|
||||
} else if(type == EXTRUDE) {
|
||||
if(predef.entityB.v != Entity::FREE_IN_3D.v) {
|
||||
// The extrusion path is locked along a line, normal to the
|
||||
// specified workplane.
|
||||
Entity *w = SK.GetEntity(predef.entityB);
|
||||
ExprVector u = w->Normal()->NormalExprsU();
|
||||
ExprVector v = w->Normal()->NormalExprsV();
|
||||
ExprVector extruden = {
|
||||
Expr::From(h.param(0)),
|
||||
Expr::From(h.param(1)),
|
||||
Expr::From(h.param(2)) };
|
||||
|
||||
AddEq(l, u.Dot(extruden), 0);
|
||||
AddEq(l, v.Dot(extruden), 1);
|
||||
}
|
||||
} else if(type == TRANSLATE) {
|
||||
if(predef.entityB.v != Entity::FREE_IN_3D.v) {
|
||||
Entity *w = SK.GetEntity(predef.entityB);
|
||||
ExprVector n = w->Normal()->NormalExprsN();
|
||||
ExprVector trans;
|
||||
trans = ExprVector::From(h.param(0), h.param(1), h.param(2));
|
||||
|
||||
// The translation vector is parallel to the workplane
|
||||
AddEq(l, trans.Dot(n), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hEntity Group::Remap(hEntity in, int copyNumber) {
|
||||
// A hash table is used to accelerate the search
|
||||
int hash = ((unsigned)(in.v*61 + copyNumber)) % REMAP_PRIME;
|
||||
int i = remapCache[hash];
|
||||
if(i >= 0 && i < remap.n) {
|
||||
EntityMap *em = &(remap.elem[i]);
|
||||
if(em->input.v == in.v && em->copyNumber == copyNumber) {
|
||||
return h.entity(em->h.v);
|
||||
}
|
||||
}
|
||||
// but if we don't find it in the hash table, then linear search
|
||||
for(i = 0; i < remap.n; i++) {
|
||||
EntityMap *em = &(remap.elem[i]);
|
||||
if(em->input.v == in.v && em->copyNumber == copyNumber) {
|
||||
// We already have a mapping for this entity.
|
||||
remapCache[hash] = i;
|
||||
return h.entity(em->h.v);
|
||||
}
|
||||
}
|
||||
// And if we still don't find it, then create a new entry.
|
||||
EntityMap em;
|
||||
em.input = in;
|
||||
em.copyNumber = copyNumber;
|
||||
remap.AddAndAssignId(&em);
|
||||
return h.entity(em.h.v);
|
||||
}
|
||||
|
||||
void Group::MakeExtrusionLines(IdList<Entity,hEntity> *el, hEntity in) {
|
||||
Entity *ep = SK.GetEntity(in);
|
||||
|
||||
Entity en;
|
||||
ZERO(&en);
|
||||
if(ep->IsPoint()) {
|
||||
// A point gets extruded to form a line segment
|
||||
en.point[0] = Remap(ep->h, REMAP_TOP);
|
||||
en.point[1] = Remap(ep->h, REMAP_BOTTOM);
|
||||
en.group = h;
|
||||
en.construction = ep->construction;
|
||||
en.style = ep->style;
|
||||
en.h = Remap(ep->h, REMAP_PT_TO_LINE);
|
||||
en.type = Entity::LINE_SEGMENT;
|
||||
el->Add(&en);
|
||||
} else if(ep->type == Entity::LINE_SEGMENT) {
|
||||
// A line gets extruded to form a plane face; an endpoint of the
|
||||
// original line is a point in the plane, and the line is in the plane.
|
||||
Vector a = SK.GetEntity(ep->point[0])->PointGetNum();
|
||||
Vector b = SK.GetEntity(ep->point[1])->PointGetNum();
|
||||
Vector ab = b.Minus(a);
|
||||
|
||||
en.param[0] = h.param(0);
|
||||
en.param[1] = h.param(1);
|
||||
en.param[2] = h.param(2);
|
||||
en.numPoint = a;
|
||||
en.numNormal = Quaternion::From(0, ab.x, ab.y, ab.z);
|
||||
|
||||
en.group = h;
|
||||
en.construction = ep->construction;
|
||||
en.style = ep->style;
|
||||
en.h = Remap(ep->h, REMAP_LINE_TO_FACE);
|
||||
en.type = Entity::FACE_XPROD;
|
||||
el->Add(&en);
|
||||
}
|
||||
}
|
||||
|
||||
void Group::MakeExtrusionTopBottomFaces(IdList<Entity,hEntity> *el, hEntity pt)
|
||||
{
|
||||
if(pt.v == 0) return;
|
||||
Group *src = SK.GetGroup(opA);
|
||||
Vector n = src->polyLoops.normal;
|
||||
|
||||
Entity en;
|
||||
ZERO(&en);
|
||||
en.type = Entity::FACE_NORMAL_PT;
|
||||
en.group = h;
|
||||
|
||||
en.numNormal = Quaternion::From(0, n.x, n.y, n.z);
|
||||
en.point[0] = Remap(pt, REMAP_TOP);
|
||||
en.h = Remap(Entity::NO_ENTITY, REMAP_TOP);
|
||||
el->Add(&en);
|
||||
|
||||
en.point[0] = Remap(pt, REMAP_BOTTOM);
|
||||
en.h = Remap(Entity::NO_ENTITY, REMAP_BOTTOM);
|
||||
el->Add(&en);
|
||||
}
|
||||
|
||||
void Group::CopyEntity(IdList<Entity,hEntity> *el,
|
||||
Entity *ep, int timesApplied, int remap,
|
||||
hParam dx, hParam dy, hParam dz,
|
||||
hParam qw, hParam qvx, hParam qvy, hParam qvz,
|
||||
bool asTrans, bool asAxisAngle)
|
||||
{
|
||||
Entity en;
|
||||
ZERO(&en);
|
||||
en.type = ep->type;
|
||||
en.extraPoints = ep->extraPoints;
|
||||
en.h = Remap(ep->h, remap);
|
||||
en.timesApplied = timesApplied;
|
||||
en.group = h;
|
||||
en.construction = ep->construction;
|
||||
en.style = ep->style;
|
||||
en.str.strcpy(ep->str.str);
|
||||
en.font.strcpy(ep->font.str);
|
||||
|
||||
switch(ep->type) {
|
||||
case Entity::WORKPLANE:
|
||||
// Don't copy these.
|
||||
return;
|
||||
|
||||
case Entity::POINT_N_COPY:
|
||||
case Entity::POINT_N_TRANS:
|
||||
case Entity::POINT_N_ROT_TRANS:
|
||||
case Entity::POINT_N_ROT_AA:
|
||||
case Entity::POINT_IN_3D:
|
||||
case Entity::POINT_IN_2D:
|
||||
if(asTrans) {
|
||||
en.type = Entity::POINT_N_TRANS;
|
||||
en.param[0] = dx;
|
||||
en.param[1] = dy;
|
||||
en.param[2] = dz;
|
||||
} else {
|
||||
if(asAxisAngle) {
|
||||
en.type = Entity::POINT_N_ROT_AA;
|
||||
} else {
|
||||
en.type = Entity::POINT_N_ROT_TRANS;
|
||||
}
|
||||
en.param[0] = dx;
|
||||
en.param[1] = dy;
|
||||
en.param[2] = dz;
|
||||
en.param[3] = qw;
|
||||
en.param[4] = qvx;
|
||||
en.param[5] = qvy;
|
||||
en.param[6] = qvz;
|
||||
}
|
||||
en.numPoint = (ep->actPoint).ScaledBy(scale);
|
||||
break;
|
||||
|
||||
case Entity::NORMAL_N_COPY:
|
||||
case Entity::NORMAL_N_ROT:
|
||||
case Entity::NORMAL_N_ROT_AA:
|
||||
case Entity::NORMAL_IN_3D:
|
||||
case Entity::NORMAL_IN_2D:
|
||||
if(asTrans) {
|
||||
en.type = Entity::NORMAL_N_COPY;
|
||||
} else {
|
||||
if(asAxisAngle) {
|
||||
en.type = Entity::NORMAL_N_ROT_AA;
|
||||
} else {
|
||||
en.type = Entity::NORMAL_N_ROT;
|
||||
}
|
||||
en.param[0] = qw;
|
||||
en.param[1] = qvx;
|
||||
en.param[2] = qvy;
|
||||
en.param[3] = qvz;
|
||||
}
|
||||
en.numNormal = ep->actNormal;
|
||||
if(scale < 0) en.numNormal = en.numNormal.Mirror();
|
||||
|
||||
en.point[0] = Remap(ep->point[0], remap);
|
||||
break;
|
||||
|
||||
case Entity::DISTANCE_N_COPY:
|
||||
case Entity::DISTANCE:
|
||||
en.type = Entity::DISTANCE_N_COPY;
|
||||
en.numDistance = ep->actDistance*fabs(scale);
|
||||
break;
|
||||
|
||||
case Entity::FACE_NORMAL_PT:
|
||||
case Entity::FACE_XPROD:
|
||||
case Entity::FACE_N_ROT_TRANS:
|
||||
case Entity::FACE_N_TRANS:
|
||||
case Entity::FACE_N_ROT_AA:
|
||||
if(asTrans) {
|
||||
en.type = Entity::FACE_N_TRANS;
|
||||
en.param[0] = dx;
|
||||
en.param[1] = dy;
|
||||
en.param[2] = dz;
|
||||
} else {
|
||||
if(asAxisAngle) {
|
||||
en.type = Entity::FACE_N_ROT_AA;
|
||||
} else {
|
||||
en.type = Entity::FACE_N_ROT_TRANS;
|
||||
}
|
||||
en.param[0] = dx;
|
||||
en.param[1] = dy;
|
||||
en.param[2] = dz;
|
||||
en.param[3] = qw;
|
||||
en.param[4] = qvx;
|
||||
en.param[5] = qvy;
|
||||
en.param[6] = qvz;
|
||||
}
|
||||
en.numPoint = (ep->actPoint).ScaledBy(scale);
|
||||
en.numNormal = (ep->actNormal).ScaledBy(scale);
|
||||
break;
|
||||
|
||||
default: {
|
||||
int i, points;
|
||||
bool hasNormal, hasDistance;
|
||||
EntReqTable::GetEntityInfo(ep->type, ep->extraPoints,
|
||||
NULL, &points, &hasNormal, &hasDistance);
|
||||
for(i = 0; i < points; i++) {
|
||||
en.point[i] = Remap(ep->point[i], remap);
|
||||
}
|
||||
if(hasNormal) en.normal = Remap(ep->normal, remap);
|
||||
if(hasDistance) en.distance = Remap(ep->distance, remap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the entity came from an imported file where it was invisible then
|
||||
// ep->actiVisble will be false, and we should hide it. Or if the entity
|
||||
// came from a copy (e.g. step and repeat) of a force-hidden imported
|
||||
// entity, then we also want to hide it.
|
||||
en.forceHidden = (!ep->actVisible) || ep->forceHidden;
|
||||
|
||||
el->Add(&en);
|
||||
}
|
||||
|
569
groupmesh.cpp
|
@ -1,569 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Routines to generate our watertight brep shells from the operations
|
||||
// and entities specified by the user in each group; templated to work either
|
||||
// on an SShell of ratpoly surfaces or on an SMesh of triangles.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
#define gs (SS.GW.gs)
|
||||
|
||||
void Group::AssembleLoops(bool *allClosed,
|
||||
bool *allCoplanar,
|
||||
bool *allNonZeroLen)
|
||||
{
|
||||
SBezierList sbl;
|
||||
ZERO(&sbl);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < SK.entity.n; i++) {
|
||||
Entity *e = &(SK.entity.elem[i]);
|
||||
if(e->group.v != h.v) continue;
|
||||
if(e->construction) continue;
|
||||
if(e->forceHidden) continue;
|
||||
|
||||
e->GenerateBezierCurves(&sbl);
|
||||
}
|
||||
|
||||
SBezier *sb;
|
||||
*allNonZeroLen = true;
|
||||
for(sb = sbl.l.First(); sb; sb = sbl.l.NextAfter(sb)) {
|
||||
for(i = 1; i <= sb->deg; i++) {
|
||||
if(!(sb->ctrl[i]).Equals(sb->ctrl[0])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i > sb->deg) {
|
||||
// This is a zero-length edge.
|
||||
*allNonZeroLen = false;
|
||||
polyError.errorPointAt = sb->ctrl[0];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to assemble all these Beziers into loops. The closed loops go into
|
||||
// bezierLoops, with the outer loops grouped with their holes. The
|
||||
// leftovers, if any, go in bezierOpens.
|
||||
bezierLoops.FindOuterFacesFrom(&sbl, &polyLoops, NULL,
|
||||
SS.ChordTolMm(),
|
||||
allClosed, &(polyError.notClosedAt),
|
||||
allCoplanar, &(polyError.errorPointAt),
|
||||
&bezierOpens);
|
||||
sbl.Clear();
|
||||
}
|
||||
|
||||
void Group::GenerateLoops(void) {
|
||||
polyLoops.Clear();
|
||||
bezierLoops.Clear();
|
||||
bezierOpens.Clear();
|
||||
|
||||
if(type == DRAWING_3D || type == DRAWING_WORKPLANE ||
|
||||
type == ROTATE || type == TRANSLATE || type == IMPORTED)
|
||||
{
|
||||
bool allClosed, allCoplanar, allNonZeroLen;
|
||||
AssembleLoops(&allClosed, &allCoplanar, &allNonZeroLen);
|
||||
if(!allCoplanar) {
|
||||
polyError.how = POLY_NOT_COPLANAR;
|
||||
} else if(!allClosed) {
|
||||
polyError.how = POLY_NOT_CLOSED;
|
||||
} else if(!allNonZeroLen) {
|
||||
polyError.how = POLY_ZERO_LEN_EDGE;
|
||||
} else {
|
||||
polyError.how = POLY_GOOD;
|
||||
// The self-intersecting check is kind of slow, so don't run it
|
||||
// unless requested.
|
||||
if(SS.checkClosedContour) {
|
||||
if(polyLoops.SelfIntersecting(&(polyError.errorPointAt))) {
|
||||
polyError.how = POLY_SELF_INTERSECTING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SShell::RemapFaces(Group *g, int remap) {
|
||||
SSurface *ss;
|
||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)){
|
||||
hEntity face = { ss->face };
|
||||
if(face.v == Entity::NO_ENTITY.v) continue;
|
||||
|
||||
face = g->Remap(face, remap);
|
||||
ss->face = face.v;
|
||||
}
|
||||
}
|
||||
|
||||
void SMesh::RemapFaces(Group *g, int remap) {
|
||||
STriangle *tr;
|
||||
for(tr = l.First(); tr; tr = l.NextAfter(tr)) {
|
||||
hEntity face = { tr->meta.face };
|
||||
if(face.v == Entity::NO_ENTITY.v) continue;
|
||||
|
||||
face = g->Remap(face, remap);
|
||||
tr->meta.face = face.v;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Group::GenerateForStepAndRepeat(T *steps, T *outs) {
|
||||
T workA, workB;
|
||||
ZERO(&workA);
|
||||
ZERO(&workB);
|
||||
T *soFar = &workA, *scratch = &workB;
|
||||
|
||||
int n = (int)valA, a0 = 0;
|
||||
if(subtype == ONE_SIDED && skipFirst) {
|
||||
a0++; n++;
|
||||
}
|
||||
int a;
|
||||
for(a = a0; a < n; a++) {
|
||||
int ap = a*2 - (subtype == ONE_SIDED ? 0 : (n-1));
|
||||
int remap = (a == (n - 1)) ? REMAP_LAST : a;
|
||||
|
||||
T transd;
|
||||
ZERO(&transd);
|
||||
if(type == TRANSLATE) {
|
||||
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
|
||||
trans = trans.ScaledBy(ap);
|
||||
transd.MakeFromTransformationOf(steps,
|
||||
trans, Quaternion::IDENTITY, 1.0);
|
||||
} else {
|
||||
Vector trans = Vector::From(h.param(0), h.param(1), h.param(2));
|
||||
double theta = ap * SK.GetParam(h.param(3))->val;
|
||||
double c = cos(theta), s = sin(theta);
|
||||
Vector axis = Vector::From(h.param(4), h.param(5), h.param(6));
|
||||
Quaternion q = Quaternion::From(c, s*axis.x, s*axis.y, s*axis.z);
|
||||
// Rotation is centered at t; so A(x - t) + t = Ax + (t - At)
|
||||
transd.MakeFromTransformationOf(steps,
|
||||
trans.Minus(q.Rotate(trans)), q, 1.0);
|
||||
}
|
||||
|
||||
// We need to rewrite any plane face entities to the transformed ones.
|
||||
transd.RemapFaces(this, remap);
|
||||
|
||||
// And tack this transformed copy on to the return.
|
||||
if(soFar->IsEmpty()) {
|
||||
scratch->MakeFromCopyOf(&transd);
|
||||
} else {
|
||||
scratch->MakeFromUnionOf(soFar, &transd);
|
||||
}
|
||||
|
||||
SWAP(T *, scratch, soFar);
|
||||
scratch->Clear();
|
||||
transd.Clear();
|
||||
}
|
||||
|
||||
outs->Clear();
|
||||
*outs = *soFar;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Group::GenerateForBoolean(T *prevs, T *thiss, T *outs, int how) {
|
||||
// If this group contributes no new mesh, then our running mesh is the
|
||||
// same as last time, no combining required. Likewise if we have a mesh
|
||||
// but it's suppressed.
|
||||
if(thiss->IsEmpty() || suppress) {
|
||||
outs->MakeFromCopyOf(prevs);
|
||||
return;
|
||||
}
|
||||
|
||||
// So our group's shell appears in thisShell. Combine this with the
|
||||
// previous group's shell, using the requested operation.
|
||||
if(how == COMBINE_AS_UNION) {
|
||||
outs->MakeFromUnionOf(prevs, thiss);
|
||||
} else if(how == COMBINE_AS_DIFFERENCE) {
|
||||
outs->MakeFromDifferenceOf(prevs, thiss);
|
||||
} else {
|
||||
outs->MakeFromAssemblyOf(prevs, thiss);
|
||||
}
|
||||
}
|
||||
|
||||
void Group::GenerateShellAndMesh(void) {
|
||||
bool prevBooleanFailed = booleanFailed;
|
||||
booleanFailed = false;
|
||||
|
||||
Group *srcg = this;
|
||||
|
||||
thisShell.Clear();
|
||||
thisMesh.Clear();
|
||||
runningShell.Clear();
|
||||
runningMesh.Clear();
|
||||
|
||||
// Don't attempt a lathe or extrusion unless the source section is good:
|
||||
// planar and not self-intersecting.
|
||||
bool haveSrc = true;
|
||||
if(type == EXTRUDE || type == LATHE) {
|
||||
Group *src = SK.GetGroup(opA);
|
||||
if(src->polyError.how != POLY_GOOD) {
|
||||
haveSrc = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(type == TRANSLATE || type == ROTATE) {
|
||||
// A step and repeat gets merged against the group's prevous group,
|
||||
// not our own previous group.
|
||||
srcg = SK.GetGroup(opA);
|
||||
|
||||
GenerateForStepAndRepeat<SShell>(&(srcg->thisShell), &thisShell);
|
||||
GenerateForStepAndRepeat<SMesh> (&(srcg->thisMesh), &thisMesh);
|
||||
} else if(type == EXTRUDE && haveSrc) {
|
||||
Group *src = SK.GetGroup(opA);
|
||||
Vector translate = Vector::From(h.param(0), h.param(1), h.param(2));
|
||||
|
||||
Vector tbot, ttop;
|
||||
if(subtype == ONE_SIDED) {
|
||||
tbot = Vector::From(0, 0, 0); ttop = translate.ScaledBy(2);
|
||||
} else {
|
||||
tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1);
|
||||
}
|
||||
|
||||
SBezierLoopSetSet *sblss = &(src->bezierLoops);
|
||||
SBezierLoopSet *sbls;
|
||||
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
|
||||
int is = thisShell.surface.n;
|
||||
// Extrude this outer contour (plus its inner contours, if present)
|
||||
thisShell.MakeFromExtrusionOf(sbls, tbot, ttop, color);
|
||||
|
||||
// And for any plane faces, annotate the model with the entity for
|
||||
// that face, so that the user can select them with the mouse.
|
||||
Vector onOrig = sbls->point;
|
||||
int i;
|
||||
for(i = is; i < thisShell.surface.n; i++) {
|
||||
SSurface *ss = &(thisShell.surface.elem[i]);
|
||||
hEntity face = Entity::NO_ENTITY;
|
||||
|
||||
Vector p = ss->PointAt(0, 0),
|
||||
n = ss->NormalAt(0, 0).WithMagnitude(1);
|
||||
double d = n.Dot(p);
|
||||
|
||||
if(i == is || i == (is + 1)) {
|
||||
// These are the top and bottom of the shell.
|
||||
if(fabs((onOrig.Plus(ttop)).Dot(n) - d) < LENGTH_EPS) {
|
||||
face = Remap(Entity::NO_ENTITY, REMAP_TOP);
|
||||
ss->face = face.v;
|
||||
}
|
||||
if(fabs((onOrig.Plus(tbot)).Dot(n) - d) < LENGTH_EPS) {
|
||||
face = Remap(Entity::NO_ENTITY, REMAP_BOTTOM);
|
||||
ss->face = face.v;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// So these are the sides
|
||||
if(ss->degm != 1 || ss->degn != 1) continue;
|
||||
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group.v != opA.v) continue;
|
||||
if(e->type != Entity::LINE_SEGMENT) continue;
|
||||
|
||||
Vector a = SK.GetEntity(e->point[0])->PointGetNum(),
|
||||
b = SK.GetEntity(e->point[1])->PointGetNum();
|
||||
a = a.Plus(ttop);
|
||||
b = b.Plus(ttop);
|
||||
// Could get taken backwards, so check all cases.
|
||||
if((a.Equals(ss->ctrl[0][0]) && b.Equals(ss->ctrl[1][0])) ||
|
||||
(b.Equals(ss->ctrl[0][0]) && a.Equals(ss->ctrl[1][0])) ||
|
||||
(a.Equals(ss->ctrl[0][1]) && b.Equals(ss->ctrl[1][1])) ||
|
||||
(b.Equals(ss->ctrl[0][1]) && a.Equals(ss->ctrl[1][1])))
|
||||
{
|
||||
face = Remap(e->h, REMAP_LINE_TO_FACE);
|
||||
ss->face = face.v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(type == LATHE && haveSrc) {
|
||||
Group *src = SK.GetGroup(opA);
|
||||
|
||||
Vector pt = SK.GetEntity(predef.origin)->PointGetNum(),
|
||||
axis = SK.GetEntity(predef.entityB)->VectorGetNum();
|
||||
axis = axis.WithMagnitude(1);
|
||||
|
||||
SBezierLoopSetSet *sblss = &(src->bezierLoops);
|
||||
SBezierLoopSet *sbls;
|
||||
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
|
||||
thisShell.MakeFromRevolutionOf(sbls, pt, axis, color);
|
||||
}
|
||||
} else if(type == IMPORTED) {
|
||||
// The imported shell or mesh are copied over, with the appropriate
|
||||
// transformation applied. We also must remap the face entities.
|
||||
Vector offset = {
|
||||
SK.GetParam(h.param(0))->val,
|
||||
SK.GetParam(h.param(1))->val,
|
||||
SK.GetParam(h.param(2))->val };
|
||||
Quaternion q = {
|
||||
SK.GetParam(h.param(3))->val,
|
||||
SK.GetParam(h.param(4))->val,
|
||||
SK.GetParam(h.param(5))->val,
|
||||
SK.GetParam(h.param(6))->val };
|
||||
|
||||
thisMesh.MakeFromTransformationOf(&impMesh, offset, q, scale);
|
||||
thisMesh.RemapFaces(this, 0);
|
||||
|
||||
thisShell.MakeFromTransformationOf(&impShell, offset, q, scale);
|
||||
thisShell.RemapFaces(this, 0);
|
||||
}
|
||||
|
||||
if(srcg->meshCombine != COMBINE_AS_ASSEMBLE) {
|
||||
thisShell.MergeCoincidentSurfaces();
|
||||
}
|
||||
|
||||
// So now we've got the mesh or shell for this group. Combine it with
|
||||
// the previous group's mesh or shell with the requested Boolean, and
|
||||
// we're done.
|
||||
|
||||
Group *prevg = srcg->RunningMeshGroup();
|
||||
|
||||
if(prevg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) {
|
||||
SShell *prevs = &(prevg->runningShell);
|
||||
GenerateForBoolean<SShell>(prevs, &thisShell, &runningShell,
|
||||
srcg->meshCombine);
|
||||
|
||||
if(srcg->meshCombine != COMBINE_AS_ASSEMBLE) {
|
||||
runningShell.MergeCoincidentSurfaces();
|
||||
}
|
||||
|
||||
// If the Boolean failed, then we should note that in the text screen
|
||||
// for this group.
|
||||
booleanFailed = runningShell.booleanFailed;
|
||||
if(booleanFailed != prevBooleanFailed) {
|
||||
SS.later.showTW = true;
|
||||
}
|
||||
} else {
|
||||
SMesh prevm, thism;
|
||||
ZERO(&prevm);
|
||||
ZERO(&thism);
|
||||
|
||||
prevm.MakeFromCopyOf(&(prevg->runningMesh));
|
||||
prevg->runningShell.TriangulateInto(&prevm);
|
||||
|
||||
thism.MakeFromCopyOf(&thisMesh);
|
||||
thisShell.TriangulateInto(&thism);
|
||||
|
||||
SMesh outm;
|
||||
ZERO(&outm);
|
||||
GenerateForBoolean<SMesh>(&prevm, &thism, &outm, srcg->meshCombine);
|
||||
|
||||
// And make sure that the output mesh is vertex-to-vertex.
|
||||
SKdNode *root = SKdNode::From(&outm);
|
||||
root->SnapToMesh(&outm);
|
||||
root->MakeMeshInto(&runningMesh);
|
||||
|
||||
outm.Clear();
|
||||
thism.Clear();
|
||||
prevm.Clear();
|
||||
}
|
||||
|
||||
displayDirty = true;
|
||||
}
|
||||
|
||||
void Group::GenerateDisplayItems(void) {
|
||||
// This is potentially slow (since we've got to triangulate a shell, or
|
||||
// to find the emphasized edges for a mesh), so we will run it only
|
||||
// if its inputs have changed.
|
||||
if(displayDirty) {
|
||||
Group *pg = RunningMeshGroup();
|
||||
if(pg && thisMesh.IsEmpty() && thisShell.IsEmpty()) {
|
||||
// We don't contribute any new solid model in this group, so our
|
||||
// display items are identical to the previous group's; which means
|
||||
// that we can just display those, and stop ourselves from
|
||||
// recalculating for those every time we get a change in this group.
|
||||
//
|
||||
// Note that this can end up recursing multiple times (if multiple
|
||||
// groups that contribute no solid model exist in sequence), but
|
||||
// that's okay.
|
||||
pg->GenerateDisplayItems();
|
||||
|
||||
displayMesh.Clear();
|
||||
displayMesh.MakeFromCopyOf(&(pg->displayMesh));
|
||||
|
||||
displayEdges.Clear();
|
||||
if(SS.GW.showEdges) {
|
||||
SEdge *se;
|
||||
SEdgeList *src = &(pg->displayEdges);
|
||||
for(se = src->l.First(); se; se = src->l.NextAfter(se)) {
|
||||
displayEdges.l.Add(se);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We do contribute new solid model, so we have to triangulate the
|
||||
// shell, and edge-find the mesh.
|
||||
displayMesh.Clear();
|
||||
runningShell.TriangulateInto(&displayMesh);
|
||||
STriangle *t;
|
||||
for(t = runningMesh.l.First(); t; t = runningMesh.l.NextAfter(t)) {
|
||||
STriangle trn = *t;
|
||||
Vector n = trn.Normal();
|
||||
trn.an = n;
|
||||
trn.bn = n;
|
||||
trn.cn = n;
|
||||
displayMesh.AddTriangle(&trn);
|
||||
}
|
||||
|
||||
displayEdges.Clear();
|
||||
|
||||
if(SS.GW.showEdges) {
|
||||
runningShell.MakeEdgesInto(&displayEdges);
|
||||
runningMesh.MakeEmphasizedEdgesInto(&displayEdges);
|
||||
}
|
||||
}
|
||||
|
||||
displayDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
Group *Group::PreviousGroup(void) {
|
||||
int i;
|
||||
for(i = 0; i < SK.group.n; i++) {
|
||||
Group *g = &(SK.group.elem[i]);
|
||||
if(g->h.v == h.v) break;
|
||||
}
|
||||
if(i == 0 || i >= SK.group.n) return NULL;
|
||||
return &(SK.group.elem[i-1]);
|
||||
}
|
||||
|
||||
Group *Group::RunningMeshGroup(void) {
|
||||
if(type == TRANSLATE || type == ROTATE) {
|
||||
return SK.GetGroup(opA)->RunningMeshGroup();
|
||||
} else {
|
||||
return PreviousGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void Group::DrawDisplayItems(int t) {
|
||||
int specColor;
|
||||
if(t == DRAWING_3D || t == DRAWING_WORKPLANE) {
|
||||
// force the color to something dim
|
||||
specColor = Style::Color(Style::DIM_SOLID);
|
||||
} else {
|
||||
specColor = -1; // use the model color
|
||||
}
|
||||
// The back faces are drawn in red; should never seem them, since we
|
||||
// draw closed shells, so that's a debugging aid.
|
||||
GLfloat mpb[] = { 1.0f, 0.1f, 0.1f, 1.0 };
|
||||
glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, mpb);
|
||||
|
||||
// When we fill the mesh, we need to know which triangles are selected
|
||||
// or hovered, in order to draw them differently.
|
||||
DWORD mh = 0, ms1 = 0, ms2 = 0;
|
||||
hEntity he = SS.GW.hover.entity;
|
||||
if(he.v != 0 && SK.GetEntity(he)->IsFace()) {
|
||||
mh = he.v;
|
||||
}
|
||||
SS.GW.GroupSelection();
|
||||
if(gs.faces > 0) ms1 = gs.face[0].v;
|
||||
if(gs.faces > 1) ms2 = gs.face[1].v;
|
||||
|
||||
if(SS.GW.showShaded) {
|
||||
glEnable(GL_LIGHTING);
|
||||
glxFillMesh(specColor, &displayMesh, mh, ms1, ms2);
|
||||
glDisable(GL_LIGHTING);
|
||||
}
|
||||
if(SS.GW.showEdges) {
|
||||
glxDepthRangeOffset(2);
|
||||
glxColorRGB(Style::Color(Style::SOLID_EDGE));
|
||||
glLineWidth(Style::Width(Style::SOLID_EDGE));
|
||||
glxDrawEdges(&displayEdges, false);
|
||||
}
|
||||
|
||||
if(SS.GW.showMesh) glxDebugMesh(&displayMesh);
|
||||
}
|
||||
|
||||
void Group::Draw(void) {
|
||||
// Everything here gets drawn whether or not the group is hidden; we
|
||||
// can control this stuff independently, with show/hide solids, edges,
|
||||
// mesh, etc.
|
||||
|
||||
GenerateDisplayItems();
|
||||
DrawDisplayItems(type);
|
||||
|
||||
if(!SS.checkClosedContour) return;
|
||||
|
||||
// And finally show the polygons too, and any errors if it's not possible
|
||||
// to assemble the lines into closed polygons.
|
||||
if(polyError.how == POLY_NOT_CLOSED) {
|
||||
// Report this error only in sketch-in-workplane groups; otherwise
|
||||
// it's just a nuisance.
|
||||
if(type == DRAWING_WORKPLANE) {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glxColorRGBa(Style::Color(Style::DRAW_ERROR), 0.2);
|
||||
glLineWidth (Style::Width(Style::DRAW_ERROR));
|
||||
glBegin(GL_LINES);
|
||||
glxVertex3v(polyError.notClosedAt.a);
|
||||
glxVertex3v(polyError.notClosedAt.b);
|
||||
glEnd();
|
||||
glxColorRGB(Style::Color(Style::DRAW_ERROR));
|
||||
glxWriteText("not closed contour, or not all same style!",
|
||||
DEFAULT_TEXT_HEIGHT,
|
||||
polyError.notClosedAt.b, SS.GW.projRight, SS.GW.projUp,
|
||||
NULL, NULL);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
} else if(polyError.how == POLY_NOT_COPLANAR ||
|
||||
polyError.how == POLY_SELF_INTERSECTING ||
|
||||
polyError.how == POLY_ZERO_LEN_EDGE)
|
||||
{
|
||||
// These errors occur at points, not lines
|
||||
if(type == DRAWING_WORKPLANE) {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glxColorRGB(Style::Color(Style::DRAW_ERROR));
|
||||
char *msg;
|
||||
if(polyError.how == POLY_NOT_COPLANAR) {
|
||||
msg = "points not all coplanar!";
|
||||
} else if(polyError.how == POLY_SELF_INTERSECTING) {
|
||||
msg = "contour is self-intersecting!";
|
||||
} else {
|
||||
msg = "zero-length edge!";
|
||||
}
|
||||
glxWriteText(msg, DEFAULT_TEXT_HEIGHT,
|
||||
polyError.errorPointAt, SS.GW.projRight, SS.GW.projUp,
|
||||
NULL, NULL);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
} else {
|
||||
// The contours will get filled in DrawFilledPaths.
|
||||
}
|
||||
}
|
||||
|
||||
void Group::FillLoopSetAsPolygon(SBezierLoopSet *sbls) {
|
||||
SPolygon sp;
|
||||
ZERO(&sp);
|
||||
sbls->MakePwlInto(&sp);
|
||||
glxDepthRangeOffset(1);
|
||||
glxFillPolygon(&sp);
|
||||
glxDepthRangeOffset(0);
|
||||
sp.Clear();
|
||||
}
|
||||
|
||||
void Group::DrawFilledPaths(void) {
|
||||
SBezierLoopSet *sbls;
|
||||
SBezierLoopSetSet *sblss = &bezierLoops;
|
||||
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
|
||||
if(sbls->l.n == 0 || sbls->l.elem[0].l.n == 0) continue;
|
||||
// In an assembled loop, all the styles should be the same; so doesn't
|
||||
// matter which one we grab.
|
||||
SBezier *sb = &(sbls->l.elem[0].l.elem[0]);
|
||||
hStyle hs = { sb->auxA };
|
||||
Style *s = Style::Get(hs);
|
||||
if(s->filled) {
|
||||
// This is a filled loop, where the user specified a fill color.
|
||||
glxColorRGBa(s->fillColor, 1);
|
||||
FillLoopSetAsPolygon(sbls);
|
||||
} else {
|
||||
if(h.v == SS.GW.activeGroup.v && SS.checkClosedContour &&
|
||||
polyError.how == POLY_GOOD)
|
||||
{
|
||||
// If this is the active group, and we are supposed to check
|
||||
// for closed contours, and we do indeed have a closed and
|
||||
// non-intersecting contour, then fill it dimly.
|
||||
glxColorRGBa(Style::Color(Style::CONTOUR_FILL), 0.5);
|
||||
glxDepthRangeOffset(1);
|
||||
FillLoopSetAsPolygon(sbls);
|
||||
glxDepthRangeOffset(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
icons/angle.png
Before Width: | Height: | Size: 27 KiB |
BIN
icons/arc.png
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/bezier.png
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 243 B |
Before Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 231 B |
BIN
icons/circle.png
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/edges.png
Before Width: | Height: | Size: 27 KiB |
BIN
icons/equal.png
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/faces.png
Before Width: | Height: | Size: 609 B |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/horiz.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/in3d.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/length.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/line.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/mesh.png
Before Width: | Height: | Size: 28 KiB |
BIN
icons/normal.png
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/point.png
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/ref.png
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/shaded.png
Before Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 27 KiB |
BIN
icons/text.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/trim.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/vert.png
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 395 B |
|
@ -10,20 +10,26 @@
|
|||
#ifndef __SLVS_H
|
||||
#define __SLVS_H
|
||||
|
||||
#ifdef EXPORT_DLL
|
||||
#define DLL __declspec( dllexport )
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(WIN32)
|
||||
#ifdef slvs_EXPORTS
|
||||
#define DLL __declspec( dllexport )
|
||||
#else
|
||||
#define DLL __declspec( dllimport )
|
||||
#define DLL __declspec( dllimport )
|
||||
#endif
|
||||
#else
|
||||
#define DLL
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef DWORD Slvs_hParam;
|
||||
typedef DWORD Slvs_hEntity;
|
||||
typedef DWORD Slvs_hConstraint;
|
||||
typedef DWORD Slvs_hGroup;
|
||||
typedef uint32_t Slvs_hParam;
|
||||
typedef uint32_t Slvs_hEntity;
|
||||
typedef uint32_t Slvs_hConstraint;
|
||||
typedef uint32_t Slvs_hGroup;
|
||||
|
||||
// To obtain the 3d (not projected into a workplane) of a constraint or
|
||||
// an entity, specify this instead of the workplane.
|
||||
|
@ -118,7 +124,7 @@ typedef struct {
|
|||
Slvs_hEntity entityB;
|
||||
Slvs_hEntity entityC;
|
||||
Slvs_hEntity entityD;
|
||||
|
||||
|
||||
int other;
|
||||
int other2;
|
||||
} Slvs_Constraint;
|
||||
|
@ -158,10 +164,10 @@ typedef struct {
|
|||
int calculateFaileds;
|
||||
|
||||
//// OUTPUT VARIABLES
|
||||
//
|
||||
//
|
||||
// If the solver fails, then it can report which constraints are causing
|
||||
// the problem. The caller should allocate the array failed[], and pass
|
||||
// its size in faileds.
|
||||
// its size in faileds.
|
||||
//
|
||||
// The solver will set faileds equal to the number of problematic
|
||||
// constraints, and write their Slvs_hConstraints into failed[]. To
|
||||
|
@ -176,9 +182,10 @@ typedef struct {
|
|||
|
||||
// The solver indicates whether the solution succeeded.
|
||||
#define SLVS_RESULT_OKAY 0
|
||||
#define SLVS_RESULT_INCONSISTENT 1
|
||||
#define SLVS_RESULT_DIDNT_CONVERGE 2
|
||||
#define SLVS_RESULT_TOO_MANY_UNKNOWNS 3
|
||||
#define SLVS_RESULT_REDUNDANT_OKAY 1
|
||||
#define SLVS_RESULT_REDUNDANT_DIDNT_CONVERGE 2
|
||||
#define SLVS_RESULT_DIDNT_CONVERGE 3
|
||||
#define SLVS_RESULT_TOO_MANY_UNKNOWNS 4
|
||||
int result;
|
||||
} Slvs_System;
|
||||
|
974
mesh.cpp
|
@ -1,974 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Operations on triangle meshes, like our mesh Booleans using the BSP, and
|
||||
// the stuff to check for watertightness.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
void SMesh::Clear(void) {
|
||||
l.Clear();
|
||||
}
|
||||
|
||||
void SMesh::AddTriangle(STriMeta meta, Vector n, Vector a, Vector b, Vector c) {
|
||||
Vector ab = b.Minus(a), bc = c.Minus(b);
|
||||
Vector np = ab.Cross(bc);
|
||||
if(np.Magnitude() < 1e-10) {
|
||||
// ugh; gl sometimes tesselates to collinear triangles
|
||||
return;
|
||||
}
|
||||
if(np.Dot(n) > 0) {
|
||||
AddTriangle(meta, a, b, c);
|
||||
} else {
|
||||
AddTriangle(meta, c, b, a);
|
||||
}
|
||||
}
|
||||
void SMesh::AddTriangle(STriMeta meta, Vector a, Vector b, Vector c) {
|
||||
STriangle t; ZERO(&t);
|
||||
t.meta = meta;
|
||||
t.a = a;
|
||||
t.b = b;
|
||||
t.c = c;
|
||||
AddTriangle(&t);
|
||||
}
|
||||
void SMesh::AddTriangle(STriangle *st) {
|
||||
l.Add(st);
|
||||
}
|
||||
|
||||
void SMesh::DoBounding(Vector v, Vector *vmax, Vector *vmin) {
|
||||
vmax->x = max(vmax->x, v.x);
|
||||
vmax->y = max(vmax->y, v.y);
|
||||
vmax->z = max(vmax->z, v.z);
|
||||
|
||||
vmin->x = min(vmin->x, v.x);
|
||||
vmin->y = min(vmin->y, v.y);
|
||||
vmin->z = min(vmin->z, v.z);
|
||||
}
|
||||
void SMesh::GetBounding(Vector *vmax, Vector *vmin) {
|
||||
int i;
|
||||
*vmin = Vector::From( 1e12, 1e12, 1e12);
|
||||
*vmax = Vector::From(-1e12, -1e12, -1e12);
|
||||
for(i = 0; i < l.n; i++) {
|
||||
STriangle *st = &(l.elem[i]);
|
||||
DoBounding(st->a, vmax, vmin);
|
||||
DoBounding(st->b, vmax, vmin);
|
||||
DoBounding(st->c, vmax, vmin);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Report the edges of the boundary of the region(s) of our mesh that lie
|
||||
// within the plane n dot p = d.
|
||||
//----------------------------------------------------------------------------
|
||||
void SMesh::MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d) {
|
||||
SMesh m;
|
||||
ZERO(&m);
|
||||
m.MakeFromCopyOf(this);
|
||||
|
||||
// Delete all triangles in the mesh that do not lie in our export plane.
|
||||
m.l.ClearTags();
|
||||
int i;
|
||||
for(i = 0; i < m.l.n; i++) {
|
||||
STriangle *tr = &(m.l.elem[i]);
|
||||
|
||||
if((fabs(n.Dot(tr->a) - d) >= LENGTH_EPS) ||
|
||||
(fabs(n.Dot(tr->b) - d) >= LENGTH_EPS) ||
|
||||
(fabs(n.Dot(tr->c) - d) >= LENGTH_EPS))
|
||||
{
|
||||
tr->tag = 1;
|
||||
}
|
||||
}
|
||||
m.l.RemoveTagged();
|
||||
|
||||
// Select the naked edges in our resulting open mesh.
|
||||
SKdNode *root = SKdNode::From(&m);
|
||||
root->SnapToMesh(&m);
|
||||
root->MakeCertainEdgesInto(sel, SKdNode::NAKED_OR_SELF_INTER_EDGES,
|
||||
false, NULL, NULL);
|
||||
|
||||
m.Clear();
|
||||
}
|
||||
|
||||
void SMesh::MakeEmphasizedEdgesInto(SEdgeList *sel) {
|
||||
SKdNode *root = SKdNode::From(this);
|
||||
root->MakeCertainEdgesInto(sel, SKdNode::EMPHASIZED_EDGES,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// When we are called, all of the triangles from l.elem[start] to the end must
|
||||
// be coplanar. So we try to find a set of fewer triangles that covers the
|
||||
// exact same area, in order to reduce the number of triangles in the mesh.
|
||||
// We use this after a triangle has been split against the BSP.
|
||||
//
|
||||
// This is really ugly code; basically it just pastes things together to
|
||||
// form convex polygons, merging collinear edges when possible, then
|
||||
// triangulates the convex poly.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SMesh::Simplify(int start) {
|
||||
int maxTriangles = (l.n - start) + 10;
|
||||
|
||||
STriMeta meta = l.elem[start].meta;
|
||||
|
||||
STriangle *tout = (STriangle *)AllocTemporary(maxTriangles*sizeof(*tout));
|
||||
int toutc = 0;
|
||||
|
||||
Vector n, *conv = (Vector *)AllocTemporary(maxTriangles*3*sizeof(*conv));
|
||||
int convc = 0;
|
||||
|
||||
int start0 = start;
|
||||
|
||||
int i, j;
|
||||
for(i = start; i < l.n; i++) {
|
||||
STriangle *tr = &(l.elem[i]);
|
||||
if(tr->MinAltitude() < LENGTH_EPS) {
|
||||
tr->tag = 1;
|
||||
} else {
|
||||
tr->tag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
bool didAdd;
|
||||
convc = 0;
|
||||
for(i = start; i < l.n; i++) {
|
||||
STriangle *tr = &(l.elem[i]);
|
||||
if(tr->tag) continue;
|
||||
|
||||
tr->tag = 1;
|
||||
n = (tr->Normal()).WithMagnitude(1);
|
||||
conv[convc++] = tr->a;
|
||||
conv[convc++] = tr->b;
|
||||
conv[convc++] = tr->c;
|
||||
|
||||
start = i+1;
|
||||
break;
|
||||
}
|
||||
if(i >= l.n) break;
|
||||
|
||||
do {
|
||||
didAdd = false;
|
||||
|
||||
for(j = 0; j < convc; j++) {
|
||||
Vector a = conv[WRAP((j-1), convc)],
|
||||
b = conv[j],
|
||||
d = conv[WRAP((j+1), convc)],
|
||||
e = conv[WRAP((j+2), convc)];
|
||||
|
||||
Vector c;
|
||||
for(i = start; i < l.n; i++) {
|
||||
STriangle *tr = &(l.elem[i]);
|
||||
if(tr->tag) continue;
|
||||
|
||||
if((tr->a).Equals(d) && (tr->b).Equals(b)) {
|
||||
c = tr->c;
|
||||
} else if((tr->b).Equals(d) && (tr->c).Equals(b)) {
|
||||
c = tr->a;
|
||||
} else if((tr->c).Equals(d) && (tr->a).Equals(b)) {
|
||||
c = tr->b;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
// The vertex at C must be convex; but the others must
|
||||
// be tested
|
||||
Vector ab = b.Minus(a);
|
||||
Vector bc = c.Minus(b);
|
||||
Vector cd = d.Minus(c);
|
||||
Vector de = e.Minus(d);
|
||||
|
||||
double bDot = (ab.Cross(bc)).Dot(n);
|
||||
double dDot = (cd.Cross(de)).Dot(n);
|
||||
|
||||
bDot /= min(ab.Magnitude(), bc.Magnitude());
|
||||
dDot /= min(cd.Magnitude(), de.Magnitude());
|
||||
|
||||
if(fabs(bDot) < LENGTH_EPS && fabs(dDot) < LENGTH_EPS) {
|
||||
conv[WRAP((j+1), convc)] = c;
|
||||
// and remove the vertex at j, which is a dup
|
||||
memmove(conv+j, conv+j+1,
|
||||
(convc - j - 1)*sizeof(conv[0]));
|
||||
convc--;
|
||||
} else if(fabs(bDot) < LENGTH_EPS && dDot > 0) {
|
||||
conv[j] = c;
|
||||
} else if(fabs(dDot) < LENGTH_EPS && bDot > 0) {
|
||||
conv[WRAP((j+1), convc)] = c;
|
||||
} else if(bDot > 0 && dDot > 0) {
|
||||
// conv[j] is unchanged, conv[j+1] goes to [j+2]
|
||||
memmove(conv+j+2, conv+j+1,
|
||||
(convc - j - 1)*sizeof(conv[0]));
|
||||
conv[j+1] = c;
|
||||
convc++;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
didAdd = true;
|
||||
tr->tag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(didAdd);
|
||||
|
||||
// I need to debug why this is required; sometimes the above code
|
||||
// still generates a convex polygon
|
||||
for(i = 0; i < convc; i++) {
|
||||
Vector a = conv[WRAP((i-1), convc)],
|
||||
b = conv[i],
|
||||
c = conv[WRAP((i+1), convc)];
|
||||
Vector ab = b.Minus(a);
|
||||
Vector bc = c.Minus(b);
|
||||
double bDot = (ab.Cross(bc)).Dot(n);
|
||||
bDot /= min(ab.Magnitude(), bc.Magnitude());
|
||||
|
||||
if(bDot < 0) return; // XXX, shouldn't happen
|
||||
}
|
||||
|
||||
for(i = 0; i < convc - 2; i++) {
|
||||
STriangle tr = STriangle::From(meta, conv[0], conv[i+1], conv[i+2]);
|
||||
if(tr.MinAltitude() > LENGTH_EPS) {
|
||||
tout[toutc++] = tr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l.n = start0;
|
||||
for(i = 0; i < toutc; i++) {
|
||||
AddTriangle(&(tout[i]));
|
||||
}
|
||||
FreeTemporary(tout);
|
||||
FreeTemporary(conv);
|
||||
}
|
||||
|
||||
void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) {
|
||||
int i;
|
||||
|
||||
for(i = 0; i < srcm->l.n; i++) {
|
||||
STriangle *st = &(srcm->l.elem[i]);
|
||||
int pn = l.n;
|
||||
atLeastOneDiscarded = false;
|
||||
bsp3->Insert(st, this);
|
||||
if(!atLeastOneDiscarded && (l.n != (pn+1))) {
|
||||
l.n = pn;
|
||||
if(flipNormal) {
|
||||
AddTriangle(st->meta, st->c, st->b, st->a);
|
||||
} else {
|
||||
AddTriangle(st->meta, st->a, st->b, st->c);
|
||||
}
|
||||
}
|
||||
if(l.n - pn > 1) {
|
||||
Simplify(pn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SMesh::MakeFromUnionOf(SMesh *a, SMesh *b) {
|
||||
SBsp3 *bspa = SBsp3::FromMesh(a);
|
||||
SBsp3 *bspb = SBsp3::FromMesh(b);
|
||||
|
||||
flipNormal = false;
|
||||
keepCoplanar = false;
|
||||
AddAgainstBsp(b, bspa);
|
||||
|
||||
flipNormal = false;
|
||||
keepCoplanar = true;
|
||||
AddAgainstBsp(a, bspb);
|
||||
}
|
||||
|
||||
void SMesh::MakeFromDifferenceOf(SMesh *a, SMesh *b) {
|
||||
SBsp3 *bspa = SBsp3::FromMesh(a);
|
||||
SBsp3 *bspb = SBsp3::FromMesh(b);
|
||||
|
||||
flipNormal = true;
|
||||
keepCoplanar = true;
|
||||
AddAgainstBsp(b, bspa);
|
||||
|
||||
flipNormal = false;
|
||||
keepCoplanar = false;
|
||||
AddAgainstBsp(a, bspb);
|
||||
}
|
||||
|
||||
void SMesh::MakeFromCopyOf(SMesh *a) {
|
||||
int i;
|
||||
for(i = 0; i < a->l.n; i++) {
|
||||
AddTriangle(&(a->l.elem[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void SMesh::MakeFromAssemblyOf(SMesh *a, SMesh *b) {
|
||||
MakeFromCopyOf(a);
|
||||
MakeFromCopyOf(b);
|
||||
}
|
||||
|
||||
void SMesh::MakeFromTransformationOf(SMesh *a,
|
||||
Vector trans, Quaternion q, double scale)
|
||||
{
|
||||
STriangle *tr;
|
||||
for(tr = a->l.First(); tr; tr = a->l.NextAfter(tr)) {
|
||||
STriangle tt = *tr;
|
||||
tt.a = (tt.a).ScaledBy(scale);
|
||||
tt.b = (tt.b).ScaledBy(scale);
|
||||
tt.c = (tt.c).ScaledBy(scale);
|
||||
if(scale < 0) {
|
||||
// The mirroring would otherwise turn a closed mesh inside out.
|
||||
SWAP(Vector, tt.a, tt.b);
|
||||
}
|
||||
tt.a = (q.Rotate(tt.a)).Plus(trans);
|
||||
tt.b = (q.Rotate(tt.b)).Plus(trans);
|
||||
tt.c = (q.Rotate(tt.c)).Plus(trans);
|
||||
AddTriangle(&tt);
|
||||
}
|
||||
}
|
||||
|
||||
bool SMesh::IsEmpty(void) {
|
||||
return (l.n == 0);
|
||||
}
|
||||
|
||||
DWORD SMesh::FirstIntersectionWith(Point2d mp) {
|
||||
Vector p0 = Vector::From(mp.x, mp.y, 0);
|
||||
Vector gn = Vector::From(0, 0, 1);
|
||||
|
||||
double maxT = -1e12;
|
||||
DWORD face = 0;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
STriangle tr = l.elem[i];
|
||||
tr.a = SS.GW.ProjectPoint3(tr.a);
|
||||
tr.b = SS.GW.ProjectPoint3(tr.b);
|
||||
tr.c = SS.GW.ProjectPoint3(tr.c);
|
||||
|
||||
Vector n = tr.Normal();
|
||||
|
||||
if(n.Dot(gn) < LENGTH_EPS) continue; // back-facing or on edge
|
||||
|
||||
if(tr.ContainsPointProjd(gn, p0)) {
|
||||
// Let our line have the form r(t) = p0 + gn*t
|
||||
double t = -(n.Dot((tr.a).Minus(p0)))/(n.Dot(gn));
|
||||
if(t > maxT) {
|
||||
maxT = t;
|
||||
face = tr.meta.face;
|
||||
}
|
||||
}
|
||||
}
|
||||
return face;
|
||||
}
|
||||
|
||||
STriangleLl *STriangleLl::Alloc(void)
|
||||
{ return (STriangleLl *)AllocTemporary(sizeof(STriangleLl)); }
|
||||
SKdNode *SKdNode::Alloc(void)
|
||||
{ return (SKdNode *)AllocTemporary(sizeof(SKdNode)); }
|
||||
|
||||
SKdNode *SKdNode::From(SMesh *m) {
|
||||
int i;
|
||||
STriangle *tra = (STriangle *)AllocTemporary((m->l.n) * sizeof(*tra));
|
||||
|
||||
for(i = 0; i < m->l.n; i++) {
|
||||
tra[i] = m->l.elem[i];
|
||||
}
|
||||
|
||||
srand(0);
|
||||
int n = m->l.n;
|
||||
while(n > 1) {
|
||||
int k = rand() % n;
|
||||
n--;
|
||||
SWAP(STriangle, tra[k], tra[n]);
|
||||
}
|
||||
|
||||
STriangleLl *tll = NULL;
|
||||
for(i = 0; i < m->l.n; i++) {
|
||||
STriangleLl *tn = STriangleLl::Alloc();
|
||||
tn->tri = &(tra[i]);
|
||||
tn->next = tll;
|
||||
tll = tn;
|
||||
}
|
||||
|
||||
return SKdNode::From(tll);
|
||||
}
|
||||
|
||||
SKdNode *SKdNode::From(STriangleLl *tll) {
|
||||
int which = 0;
|
||||
SKdNode *ret = Alloc();
|
||||
|
||||
if(!tll) {
|
||||
goto leaf;
|
||||
}
|
||||
|
||||
int i;
|
||||
int gtc[3] = { 0, 0, 0 }, ltc[3] = { 0, 0, 0 }, allc = 0;
|
||||
double badness[3] = { 0, 0, 0 };
|
||||
double split[3] = { 0, 0, 0 };
|
||||
for(i = 0; i < 3; i++) {
|
||||
int tcnt = 0;
|
||||
STriangleLl *ll;
|
||||
for(ll = tll; ll; ll = ll->next) {
|
||||
STriangle *tr = ll->tri;
|
||||
split[i] += (ll->tri->a).Element(i);
|
||||
split[i] += (ll->tri->b).Element(i);
|
||||
split[i] += (ll->tri->c).Element(i);
|
||||
tcnt++;
|
||||
}
|
||||
split[i] /= (tcnt*3);
|
||||
|
||||
for(ll = tll; ll; ll = ll->next) {
|
||||
STriangle *tr = ll->tri;
|
||||
|
||||
double a = (tr->a).Element(i),
|
||||
b = (tr->b).Element(i),
|
||||
c = (tr->c).Element(i);
|
||||
|
||||
if(a < split[i] + KDTREE_EPS ||
|
||||
b < split[i] + KDTREE_EPS ||
|
||||
c < split[i] + KDTREE_EPS)
|
||||
{
|
||||
ltc[i]++;
|
||||
}
|
||||
if(a > split[i] - KDTREE_EPS ||
|
||||
b > split[i] - KDTREE_EPS ||
|
||||
c > split[i] - KDTREE_EPS)
|
||||
{
|
||||
gtc[i]++;
|
||||
}
|
||||
if(i == 0) allc++;
|
||||
}
|
||||
badness[i] = pow((double)ltc[i], 4) + pow((double)gtc[i], 4);
|
||||
}
|
||||
if(badness[0] < badness[1] && badness[0] < badness[2]) {
|
||||
which = 0;
|
||||
} else if(badness[1] < badness[2]) {
|
||||
which = 1;
|
||||
} else {
|
||||
which = 2;
|
||||
}
|
||||
|
||||
if(allc < 3 || allc == gtc[which] || allc == ltc[which]) {
|
||||
goto leaf;
|
||||
}
|
||||
|
||||
STriangleLl *ll;
|
||||
STriangleLl *lgt = NULL, *llt = NULL;
|
||||
for(ll = tll; ll; ll = ll->next) {
|
||||
STriangle *tr = ll->tri;
|
||||
|
||||
double a = (tr->a).Element(which),
|
||||
b = (tr->b).Element(which),
|
||||
c = (tr->c).Element(which);
|
||||
|
||||
if(a < split[which] + KDTREE_EPS ||
|
||||
b < split[which] + KDTREE_EPS ||
|
||||
c < split[which] + KDTREE_EPS)
|
||||
{
|
||||
STriangleLl *n = STriangleLl::Alloc();
|
||||
*n = *ll;
|
||||
n->next = llt;
|
||||
llt = n;
|
||||
}
|
||||
if(a > split[which] - KDTREE_EPS ||
|
||||
b > split[which] - KDTREE_EPS ||
|
||||
c > split[which] - KDTREE_EPS)
|
||||
{
|
||||
STriangleLl *n = STriangleLl::Alloc();
|
||||
*n = *ll;
|
||||
n->next = lgt;
|
||||
lgt = n;
|
||||
}
|
||||
}
|
||||
|
||||
ret->which = which;
|
||||
ret->c = split[which];
|
||||
ret->gt = SKdNode::From(lgt);
|
||||
ret->lt = SKdNode::From(llt);
|
||||
return ret;
|
||||
|
||||
leaf:
|
||||
ret->tris = tll;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SKdNode::ClearTags(void) {
|
||||
if(gt && lt) {
|
||||
gt->ClearTags();
|
||||
lt->ClearTags();
|
||||
} else {
|
||||
STriangleLl *ll;
|
||||
for(ll = tris; ll; ll = ll->next) {
|
||||
ll->tri->tag = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SKdNode::AddTriangle(STriangle *tr) {
|
||||
if(gt && lt) {
|
||||
double ta = (tr->a).Element(which),
|
||||
tb = (tr->b).Element(which),
|
||||
tc = (tr->c).Element(which);
|
||||
if(ta < c + KDTREE_EPS ||
|
||||
tb < c + KDTREE_EPS ||
|
||||
tc < c + KDTREE_EPS)
|
||||
{
|
||||
lt->AddTriangle(tr);
|
||||
}
|
||||
if(ta > c - KDTREE_EPS ||
|
||||
tb > c - KDTREE_EPS ||
|
||||
tc > c - KDTREE_EPS)
|
||||
{
|
||||
gt->AddTriangle(tr);
|
||||
}
|
||||
} else {
|
||||
STriangleLl *tn = STriangleLl::Alloc();
|
||||
tn->tri = tr;
|
||||
tn->next = tris;
|
||||
tris = tn;
|
||||
}
|
||||
}
|
||||
|
||||
void SKdNode::MakeMeshInto(SMesh *m) {
|
||||
if(gt) gt->MakeMeshInto(m);
|
||||
if(lt) lt->MakeMeshInto(m);
|
||||
|
||||
STriangleLl *ll;
|
||||
for(ll = tris; ll; ll = ll->next) {
|
||||
if(ll->tri->tag) continue;
|
||||
|
||||
m->AddTriangle(ll->tri);
|
||||
ll->tri->tag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// If any triangles in the mesh have an edge that goes through v (but not
|
||||
// a vertex at v), then split those triangles so that they now have a vertex
|
||||
// there. The existing triangle is modified, and the new triangle appears
|
||||
// in extras.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::SnapToVertex(Vector v, SMesh *extras) {
|
||||
if(gt && lt) {
|
||||
double vc = v.Element(which);
|
||||
if(vc < c + KDTREE_EPS) {
|
||||
lt->SnapToVertex(v, extras);
|
||||
}
|
||||
if(vc > c - KDTREE_EPS) {
|
||||
gt->SnapToVertex(v, extras);
|
||||
}
|
||||
// Nothing bad happens if the triangle to be split appears in both
|
||||
// branches; the first call will split the triangle, so that the
|
||||
// second call will do nothing, because the modified triangle will
|
||||
// already contain v
|
||||
} else {
|
||||
STriangleLl *ll;
|
||||
for(ll = tris; ll; ll = ll->next) {
|
||||
STriangle *tr = ll->tri;
|
||||
|
||||
// Do a cheap bbox test first
|
||||
int k;
|
||||
bool mightHit = true;
|
||||
|
||||
for(k = 0; k < 3; k++) {
|
||||
if((tr->a).Element(k) < v.Element(k) - KDTREE_EPS &&
|
||||
(tr->b).Element(k) < v.Element(k) - KDTREE_EPS &&
|
||||
(tr->c).Element(k) < v.Element(k) - KDTREE_EPS)
|
||||
{
|
||||
mightHit = false;
|
||||
break;
|
||||
}
|
||||
if((tr->a).Element(k) > v.Element(k) + KDTREE_EPS &&
|
||||
(tr->b).Element(k) > v.Element(k) + KDTREE_EPS &&
|
||||
(tr->c).Element(k) > v.Element(k) + KDTREE_EPS)
|
||||
{
|
||||
mightHit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!mightHit) continue;
|
||||
|
||||
if(tr->a.Equals(v)) { tr->a = v; continue; }
|
||||
if(tr->b.Equals(v)) { tr->b = v; continue; }
|
||||
if(tr->c.Equals(v)) { tr->c = v; continue; }
|
||||
|
||||
if(v.OnLineSegment(tr->a, tr->b)) {
|
||||
STriangle nt = STriangle::From(tr->meta, tr->a, v, tr->c);
|
||||
extras->AddTriangle(&nt);
|
||||
tr->a = v;
|
||||
continue;
|
||||
}
|
||||
if(v.OnLineSegment(tr->b, tr->c)) {
|
||||
STriangle nt = STriangle::From(tr->meta, tr->b, v, tr->a);
|
||||
extras->AddTriangle(&nt);
|
||||
tr->b = v;
|
||||
continue;
|
||||
}
|
||||
if(v.OnLineSegment(tr->c, tr->a)) {
|
||||
STriangle nt = STriangle::From(tr->meta, tr->c, v, tr->b);
|
||||
extras->AddTriangle(&nt);
|
||||
tr->c = v;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Snap to each vertex of each triangle of the given mesh. If the given mesh
|
||||
// is identical to the mesh used to make this kd tree, then the result should
|
||||
// be a vertex-to-vertex mesh.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::SnapToMesh(SMesh *m) {
|
||||
int i, j, k;
|
||||
for(i = 0; i < m->l.n; i++) {
|
||||
STriangle *tr = &(m->l.elem[i]);
|
||||
for(j = 0; j < 3; j++) {
|
||||
Vector v = ((j == 0) ? tr->a :
|
||||
((j == 1) ? tr->b :
|
||||
tr->c));
|
||||
|
||||
SMesh extra;
|
||||
ZERO(&extra);
|
||||
SnapToVertex(v, &extra);
|
||||
|
||||
for(k = 0; k < extra.l.n; k++) {
|
||||
STriangle *tra = (STriangle *)AllocTemporary(sizeof(*tra));
|
||||
*tra = extra.l.elem[k];
|
||||
AddTriangle(tra);
|
||||
}
|
||||
extra.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// For all the edges in sel, split them against the given triangle, and test
|
||||
// them for occlusion. Keep only the visible segments. sel is both our input
|
||||
// and our output.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) {
|
||||
SEdgeList seln;
|
||||
ZERO(&seln);
|
||||
|
||||
Vector tn = tr->Normal().WithMagnitude(1);
|
||||
double td = tn.Dot(tr->a);
|
||||
|
||||
// Consider front-facing triangles only.
|
||||
if(tn.z > LENGTH_EPS) {
|
||||
// If the edge crosses our triangle's plane, then split into above
|
||||
// and below parts. Note that we must preserve auxA, which contains
|
||||
// the style associated with this line.
|
||||
SEdge *se;
|
||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||
double da = (se->a).Dot(tn) - td,
|
||||
db = (se->b).Dot(tn) - td;
|
||||
if((da < -LENGTH_EPS && db > LENGTH_EPS) ||
|
||||
(db < -LENGTH_EPS && da > LENGTH_EPS))
|
||||
{
|
||||
Vector m = Vector::AtIntersectionOfPlaneAndLine(
|
||||
tn, td,
|
||||
se->a, se->b, NULL);
|
||||
seln.AddEdge(m, se->b, se->auxA);
|
||||
se->b = m;
|
||||
}
|
||||
}
|
||||
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
|
||||
sel->AddEdge(se->a, se->b, se->auxA);
|
||||
}
|
||||
seln.Clear();
|
||||
|
||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||
Vector pt = ((se->a).Plus(se->b)).ScaledBy(0.5);
|
||||
double dt = pt.Dot(tn) - td;
|
||||
if(pt.Dot(tn) - td > -LENGTH_EPS) {
|
||||
// Edge is in front of or on our plane (remember, tn.z > 0)
|
||||
// so it is exempt from further splitting
|
||||
se->auxB = 1;
|
||||
} else {
|
||||
// Edge is behind our plane, needs further splitting
|
||||
se->auxB = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Considering only the (x, y) coordinates, split the edge against our
|
||||
// triangle.
|
||||
Point2d a = (tr->a).ProjectXy(),
|
||||
b = (tr->b).ProjectXy(),
|
||||
c = (tr->c).ProjectXy();
|
||||
|
||||
Point2d n[3] = { (b.Minus(a)).Normal().WithMagnitude(1),
|
||||
(c.Minus(b)).Normal().WithMagnitude(1),
|
||||
(a.Minus(c)).Normal().WithMagnitude(1) };
|
||||
|
||||
double d[3] = { n[0].Dot(b),
|
||||
n[1].Dot(c),
|
||||
n[2].Dot(a) };
|
||||
|
||||
// Split all of the edges where they intersect the triangle edges
|
||||
int i;
|
||||
for(i = 0; i < 3; i++) {
|
||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||
if(se->auxB) continue;
|
||||
|
||||
Point2d ap = (se->a).ProjectXy(),
|
||||
bp = (se->b).ProjectXy();
|
||||
double da = n[i].Dot(ap) - d[i],
|
||||
db = n[i].Dot(bp) - d[i];
|
||||
if((da < -LENGTH_EPS && db > LENGTH_EPS) ||
|
||||
(db < -LENGTH_EPS && da > LENGTH_EPS))
|
||||
{
|
||||
double dab = (db - da);
|
||||
Vector spl = ((se->a).ScaledBy( db/dab)).Plus(
|
||||
(se->b).ScaledBy(-da/dab));
|
||||
seln.AddEdge(spl, se->b, se->auxA);
|
||||
se->b = spl;
|
||||
}
|
||||
}
|
||||
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
|
||||
// The split pieces are all behind the triangle, since only
|
||||
// edges behind the triangle got split. So their auxB is 0.
|
||||
sel->AddEdge(se->a, se->b, se->auxA, 0);
|
||||
}
|
||||
seln.Clear();
|
||||
}
|
||||
|
||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||
if(se->auxB) {
|
||||
// Lies above or on the triangle plane, so triangle doesn't
|
||||
// occlude it.
|
||||
se->tag = 0;
|
||||
} else {
|
||||
// Test the segment to see if it lies outside the triangle
|
||||
// (i.e., outside wrt at least one edge), and keep it only
|
||||
// then.
|
||||
Point2d pt = ((se->a).Plus(se->b).ScaledBy(0.5)).ProjectXy();
|
||||
se->tag = 1;
|
||||
for(i = 0; i < 3; i++) {
|
||||
// If the test point lies on the boundary of our triangle,
|
||||
// then we still discard the edge.
|
||||
if(n[i].Dot(pt) - d[i] > LENGTH_EPS) se->tag = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
sel->l.RemoveTagged();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Given an edge orig, occlusion test it against our mesh. We output an edge
|
||||
// list in sel, containing the visible portions of that edge.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) {
|
||||
if(gt && lt) {
|
||||
double ac = (orig.a).Element(which),
|
||||
bc = (orig.b).Element(which);
|
||||
// We can ignore triangles that are separated in x or y, but triangles
|
||||
// that are separated in z may still contribute
|
||||
if(ac < c + KDTREE_EPS ||
|
||||
bc < c + KDTREE_EPS ||
|
||||
which == 2)
|
||||
{
|
||||
lt->OcclusionTestLine(orig, sel, cnt);
|
||||
}
|
||||
if(ac > c - KDTREE_EPS ||
|
||||
bc > c - KDTREE_EPS ||
|
||||
which == 2)
|
||||
{
|
||||
gt->OcclusionTestLine(orig, sel, cnt);
|
||||
}
|
||||
} else {
|
||||
STriangleLl *ll;
|
||||
for(ll = tris; ll; ll = ll->next) {
|
||||
STriangle *tr = ll->tri;
|
||||
|
||||
if(tr->tag == cnt) continue;
|
||||
|
||||
SplitLinesAgainstTriangle(sel, tr);
|
||||
tr->tag = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Search the mesh for a triangle with an edge from b to a (i.e., the mate
|
||||
// for the edge from a to b), and increment *n each time that we find one.
|
||||
// If a triangle is found, then report whether it is front- or back-facing
|
||||
// using *fwd. And regardless of whether a mate is found, report whether
|
||||
// the edge intersects the mesh with *inter; if coplanarIsInter then we
|
||||
// count the edge as intersecting if it's coplanar with a triangle in the
|
||||
// mesh, otherwise not.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt,
|
||||
bool coplanarIsInter, bool *inter, bool *fwd,
|
||||
DWORD *face)
|
||||
{
|
||||
if(gt && lt) {
|
||||
double ac = a.Element(which),
|
||||
bc = b.Element(which);
|
||||
if(ac < c + KDTREE_EPS ||
|
||||
bc < c + KDTREE_EPS)
|
||||
{
|
||||
lt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd, face);
|
||||
}
|
||||
if(ac > c - KDTREE_EPS ||
|
||||
bc > c - KDTREE_EPS)
|
||||
{
|
||||
gt->FindEdgeOn(a, b, n, cnt, coplanarIsInter, inter, fwd, face);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We are a leaf node; so we iterate over all the triangles in our
|
||||
// linked list.
|
||||
STriangleLl *ll;
|
||||
for(ll = tris; ll; ll = ll->next) {
|
||||
STriangle *tr = ll->tri;
|
||||
|
||||
if(tr->tag == cnt) continue;
|
||||
|
||||
// Test if this triangle matches up with the given edge
|
||||
if((a.Equals(tr->b) && b.Equals(tr->a)) ||
|
||||
(a.Equals(tr->c) && b.Equals(tr->b)) ||
|
||||
(a.Equals(tr->a) && b.Equals(tr->c)))
|
||||
{
|
||||
(*n)++;
|
||||
// Record whether this triangle is front- or back-facing.
|
||||
if(tr->Normal().z > LENGTH_EPS) {
|
||||
*fwd = true;
|
||||
} else {
|
||||
*fwd = false;
|
||||
}
|
||||
// And record the triangle's face
|
||||
*face = tr->meta.face;
|
||||
} else if(((a.Equals(tr->a) && b.Equals(tr->b)) ||
|
||||
(a.Equals(tr->b) && b.Equals(tr->c)) ||
|
||||
(a.Equals(tr->c) && b.Equals(tr->a))))
|
||||
{
|
||||
// It's an edge of this triangle, okay.
|
||||
} else {
|
||||
// Check for self-intersection
|
||||
Vector n = (tr->Normal()).WithMagnitude(1);
|
||||
double d = (tr->a).Dot(n);
|
||||
double pa = a.Dot(n) - d, pb = b.Dot(n) - d;
|
||||
// It's an intersection if neither point lies in-plane,
|
||||
// and the edge crosses the plane (should handle in-plane
|
||||
// intersections separately but don't yet).
|
||||
if((pa < -LENGTH_EPS || pa > LENGTH_EPS) &&
|
||||
(pb < -LENGTH_EPS || pb > LENGTH_EPS) &&
|
||||
(pa*pb < 0))
|
||||
{
|
||||
// The edge crosses the plane of the triangle; now see if
|
||||
// it crosses inside the triangle.
|
||||
if(tr->ContainsPointProjd(b.Minus(a), a)) {
|
||||
if(coplanarIsInter) {
|
||||
*inter = true;
|
||||
} else {
|
||||
Vector p = Vector::AtIntersectionOfPlaneAndLine(
|
||||
n, d, a, b, NULL);
|
||||
Vector ta = tr->a,
|
||||
tb = tr->b,
|
||||
tc = tr->c;
|
||||
if((p.DistanceToLine(ta, tb.Minus(ta)) < LENGTH_EPS) ||
|
||||
(p.DistanceToLine(tb, tc.Minus(tb)) < LENGTH_EPS) ||
|
||||
(p.DistanceToLine(tc, ta.Minus(tc)) < LENGTH_EPS))
|
||||
{
|
||||
// Intersection lies on edge. This happens when
|
||||
// our edge is from a triangle coplanar with
|
||||
// another triangle in the mesh. We don't test
|
||||
// the edge against triangles whose plane contains
|
||||
// that edge, but we do end up testing against
|
||||
// the coplanar triangle's neighbours, which we
|
||||
// will intersect on their edges.
|
||||
} else {
|
||||
*inter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we don't count this triangle twice if it appears
|
||||
// in two buckets of the kd tree.
|
||||
tr->tag = cnt;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Pick certain classes of edges out from our mesh. These might be:
|
||||
// * naked edges (i.e., edges with no anti-parallel neighbor) and self-
|
||||
// intersecting edges (i.e., edges that cross another triangle)
|
||||
// * turning edges (i.e., edges where a front-facing triangle joins
|
||||
// a back-facing triangle)
|
||||
// * emphasized edges (i.e., edges where a triangle from one face joins
|
||||
// a triangle from a different face)
|
||||
//-----------------------------------------------------------------------------
|
||||
void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how,
|
||||
bool coplanarIsInter, bool *inter, bool *leaky)
|
||||
{
|
||||
if(inter) *inter = false;
|
||||
if(leaky) *leaky = false;
|
||||
|
||||
SMesh m;
|
||||
ZERO(&m);
|
||||
ClearTags();
|
||||
MakeMeshInto(&m);
|
||||
|
||||
int cnt = 1234;
|
||||
int i, j;
|
||||
for(i = 0; i < m.l.n; i++) {
|
||||
STriangle *tr = &(m.l.elem[i]);
|
||||
|
||||
for(j = 0; j < 3; j++) {
|
||||
Vector a = (j == 0) ? tr->a : ((j == 1) ? tr->b : tr->c);
|
||||
Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a);
|
||||
|
||||
int n = 0;
|
||||
bool thisIntersects = false, fwd;
|
||||
DWORD face;
|
||||
FindEdgeOn(a, b, &n, cnt, coplanarIsInter,
|
||||
&thisIntersects, &fwd, &face);
|
||||
|
||||
switch(how) {
|
||||
case NAKED_OR_SELF_INTER_EDGES:
|
||||
if(n != 1) {
|
||||
sel->AddEdge(a, b);
|
||||
if(leaky) *leaky = true;
|
||||
}
|
||||
if(thisIntersects) {
|
||||
sel->AddEdge(a, b);
|
||||
if(inter) *inter = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SELF_INTER_EDGES:
|
||||
if(thisIntersects) {
|
||||
sel->AddEdge(a, b);
|
||||
if(inter) *inter = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TURNING_EDGES:
|
||||
if((tr->Normal().z < LENGTH_EPS) &&
|
||||
(n == 1) &&
|
||||
fwd)
|
||||
{
|
||||
// This triangle is back-facing (or on edge), and
|
||||
// this edge has exactly one mate, and that mate is
|
||||
// front-facing. So this is a turning edge.
|
||||
sel->AddEdge(a, b, Style::SOLID_EDGE);
|
||||
}
|
||||
break;
|
||||
|
||||
case EMPHASIZED_EDGES:
|
||||
if(tr->meta.face != face && n == 1) {
|
||||
// The two triangles that join at this edge come from
|
||||
// different faces; either really different faces,
|
||||
// or one is from a face and the other is zero (i.e.,
|
||||
// not from a face).
|
||||
sel->AddEdge(a, b);
|
||||
}
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
m.Clear();
|
||||
}
|
||||
|
652
modify.cpp
|
@ -1,652 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// User-initiated (not parametric) operations to modify our sketch, by
|
||||
// changing the requests, like to round a corner or split curves where they
|
||||
// intersect.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Replace a point-coincident constraint on oldpt with that same constraint
|
||||
// on newpt. Useful when splitting or tangent arcing.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::ReplacePointInConstraints(hEntity oldpt, hEntity newpt) {
|
||||
int i;
|
||||
for(i = 0; i < SK.constraint.n; i++) {
|
||||
Constraint *c = &(SK.constraint.elem[i]);
|
||||
|
||||
if(c->type == Constraint::POINTS_COINCIDENT) {
|
||||
if(c->ptA.v == oldpt.v) c->ptA = newpt;
|
||||
if(c->ptB.v == oldpt.v) c->ptB = newpt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Let's say that A is coincident with B, and B is coincident with C. This
|
||||
// implies that A is coincident with C; but if we delete B, then both
|
||||
// constraints must be deleted too (since they reference B), and A is no
|
||||
// longer constrained to C. This routine adds back that constraint.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
|
||||
Request *r = SK.GetRequest(hr);
|
||||
if(r->group.v != SS.GW.activeGroup.v) return;
|
||||
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(!(e->h.isFromRequest())) continue;
|
||||
if(e->h.request().v != hr.v) continue;
|
||||
|
||||
if(e->type != Entity::POINT_IN_2D &&
|
||||
e->type != Entity::POINT_IN_3D)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is a point generated by the request being deleted; so fix
|
||||
// the constraints for that.
|
||||
FixConstraintsForPointBeingDeleted(e->h);
|
||||
}
|
||||
}
|
||||
void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
|
||||
List<hEntity> ld;
|
||||
ZERO(&ld);
|
||||
|
||||
Constraint *c;
|
||||
SK.constraint.ClearTags();
|
||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
||||
if(c->type != Constraint::POINTS_COINCIDENT) continue;
|
||||
if(c->group.v != SS.GW.activeGroup.v) continue;
|
||||
|
||||
if(c->ptA.v == hpt.v) {
|
||||
ld.Add(&(c->ptB));
|
||||
c->tag = 1;
|
||||
}
|
||||
if(c->ptB.v == hpt.v) {
|
||||
ld.Add(&(c->ptA));
|
||||
c->tag = 1;
|
||||
}
|
||||
}
|
||||
// These would get removed anyways when we regenerated, but do it now;
|
||||
// that way subsequent calls of this function (if multiple coincident
|
||||
// points are getting deleted) will work correctly.
|
||||
SK.constraint.RemoveTagged();
|
||||
|
||||
// If more than one point was constrained coincident with hpt, then
|
||||
// those two points were implicitly coincident with each other. By
|
||||
// deleting hpt (and all constraints that mention it), we will delete
|
||||
// that relationship. So put it back here now.
|
||||
int i;
|
||||
for(i = 1; i < ld.n; i++) {
|
||||
Constraint::ConstrainCoincident(ld.elem[i-1], ld.elem[i]);
|
||||
}
|
||||
ld.Clear();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A curve by its parametric equation, helper functions for computing tangent
|
||||
// arcs by a numerical method.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::ParametricCurve::MakeFromEntity(hEntity he, bool reverse) {
|
||||
ZERO(this);
|
||||
Entity *e = SK.GetEntity(he);
|
||||
if(e->type == Entity::LINE_SEGMENT) {
|
||||
isLine = true;
|
||||
p0 = e->EndpointStart(),
|
||||
p1 = e->EndpointFinish();
|
||||
if(reverse) {
|
||||
SWAP(Vector, p0, p1);
|
||||
}
|
||||
} else if(e->type == Entity::ARC_OF_CIRCLE) {
|
||||
isLine = false;
|
||||
p0 = SK.GetEntity(e->point[0])->PointGetNum();
|
||||
Vector pe = SK.GetEntity(e->point[1])->PointGetNum();
|
||||
r = (pe.Minus(p0)).Magnitude();
|
||||
e->ArcGetAngles(&theta0, &theta1, &dtheta);
|
||||
if(reverse) {
|
||||
SWAP(double, theta0, theta1);
|
||||
dtheta = -dtheta;
|
||||
}
|
||||
EntityBase *wrkpln = SK.GetEntity(e->workplane)->Normal();
|
||||
u = wrkpln->NormalU();
|
||||
v = wrkpln->NormalV();
|
||||
} else {
|
||||
oops();
|
||||
}
|
||||
}
|
||||
double GraphicsWindow::ParametricCurve::LengthForAuto(void) {
|
||||
if(isLine) {
|
||||
// Allow a third of the line to disappear with auto radius
|
||||
return (p1.Minus(p0)).Magnitude() / 3;
|
||||
} else {
|
||||
// But only a twentieth of the arc; shorter means fewer numerical
|
||||
// problems since the curve is more linear over shorter sections.
|
||||
return (fabs(dtheta)*r)/20;
|
||||
}
|
||||
}
|
||||
Vector GraphicsWindow::ParametricCurve::PointAt(double t) {
|
||||
if(isLine) {
|
||||
return p0.Plus((p1.Minus(p0)).ScaledBy(t));
|
||||
} else {
|
||||
double theta = theta0 + dtheta*t;
|
||||
return p0.Plus(u.ScaledBy(r*cos(theta)).Plus(v.ScaledBy(r*sin(theta))));
|
||||
}
|
||||
}
|
||||
Vector GraphicsWindow::ParametricCurve::TangentAt(double t) {
|
||||
if(isLine) {
|
||||
return p1.Minus(p0);
|
||||
} else {
|
||||
double theta = theta0 + dtheta*t;
|
||||
Vector t = u.ScaledBy(-r*sin(theta)).Plus(v.ScaledBy(r*cos(theta)));
|
||||
t = t.ScaledBy(dtheta);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
hRequest GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t,
|
||||
bool extraConstraints, hEntity orig, hEntity arc, bool arcFinish)
|
||||
{
|
||||
hRequest hr;
|
||||
Entity *e;
|
||||
if(isLine) {
|
||||
hr = SS.GW.AddRequest(Request::LINE_SEGMENT, false),
|
||||
e = SK.GetEntity(hr.entity(0));
|
||||
SK.GetEntity(e->point[0])->PointForceTo(PointAt(t));
|
||||
SK.GetEntity(e->point[1])->PointForceTo(PointAt(1));
|
||||
ConstrainPointIfCoincident(e->point[0]);
|
||||
ConstrainPointIfCoincident(e->point[1]);
|
||||
if(extraConstraints) {
|
||||
Constraint::Constrain(Constraint::PT_ON_LINE,
|
||||
hr.entity(1), Entity::NO_ENTITY, orig);
|
||||
}
|
||||
Constraint::Constrain(Constraint::ARC_LINE_TANGENT,
|
||||
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
||||
arc, e->h, arcFinish, false);
|
||||
} else {
|
||||
hr = SS.GW.AddRequest(Request::ARC_OF_CIRCLE, false),
|
||||
e = SK.GetEntity(hr.entity(0));
|
||||
SK.GetEntity(e->point[0])->PointForceTo(p0);
|
||||
if(dtheta > 0) {
|
||||
SK.GetEntity(e->point[1])->PointForceTo(PointAt(t));
|
||||
SK.GetEntity(e->point[2])->PointForceTo(PointAt(1));
|
||||
} else {
|
||||
SK.GetEntity(e->point[2])->PointForceTo(PointAt(t));
|
||||
SK.GetEntity(e->point[1])->PointForceTo(PointAt(1));
|
||||
}
|
||||
ConstrainPointIfCoincident(e->point[0]);
|
||||
ConstrainPointIfCoincident(e->point[1]);
|
||||
ConstrainPointIfCoincident(e->point[2]);
|
||||
// The tangency constraint alone is enough to fully constrain it,
|
||||
// so there's no need for more.
|
||||
Constraint::Constrain(Constraint::CURVE_CURVE_TANGENT,
|
||||
Entity::NO_ENTITY, Entity::NO_ENTITY,
|
||||
arc, e->h, arcFinish, (dtheta < 0));
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// If a point in the same group as hpt, and numerically coincident with hpt,
|
||||
// happens to exist, then constrain that point coincident to hpt.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::ParametricCurve::ConstrainPointIfCoincident(hEntity hpt) {
|
||||
Entity *e, *pt;
|
||||
pt = SK.GetEntity(hpt);
|
||||
Vector ev, ptv;
|
||||
ptv = pt->PointGetNum();
|
||||
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->h.v == pt->h.v) continue;
|
||||
if(!e->IsPoint()) continue;
|
||||
if(e->group.v != pt->group.v) continue;
|
||||
if(e->workplane.v != pt->workplane.v) continue;
|
||||
|
||||
ev = e->PointGetNum();
|
||||
if(!ev.Equals(ptv)) continue;
|
||||
|
||||
Constraint::ConstrainCoincident(hpt, e->h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A single point must be selected when this function is called. We find two
|
||||
// non-construction line segments that join at this point, and create a
|
||||
// tangent arc joining them.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::MakeTangentArc(void) {
|
||||
if(!LockedInWorkplane()) {
|
||||
Error("Must be sketching in workplane to create tangent "
|
||||
"arc.");
|
||||
return;
|
||||
}
|
||||
|
||||
// The point corresponding to the vertex to be rounded.
|
||||
Vector pshared = SK.GetEntity(gs.point[0])->PointGetNum();
|
||||
ClearSelection();
|
||||
|
||||
// First, find two requests (that are not construction, and that are
|
||||
// in our group and workplane) that generate entities that have an
|
||||
// endpoint at our vertex to be rounded.
|
||||
int i, c = 0;
|
||||
Entity *ent[2];
|
||||
Request *req[2];
|
||||
hRequest hreq[2];
|
||||
hEntity hent[2];
|
||||
bool pointf[2];
|
||||
for(i = 0; i < SK.request.n; i++) {
|
||||
Request *r = &(SK.request.elem[i]);
|
||||
if(r->group.v != activeGroup.v) continue;
|
||||
if(r->workplane.v != ActiveWorkplane().v) continue;
|
||||
if(r->construction) continue;
|
||||
if(r->type != Request::LINE_SEGMENT &&
|
||||
r->type != Request::ARC_OF_CIRCLE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Entity *e = SK.GetEntity(r->h.entity(0));
|
||||
Vector ps = e->EndpointStart(),
|
||||
pf = e->EndpointFinish();
|
||||
|
||||
if(ps.Equals(pshared) || pf.Equals(pshared)) {
|
||||
if(c < 2) {
|
||||
// We record the entity and request and their handles,
|
||||
// and whether the vertex to be rounded is the start or
|
||||
// finish of this entity.
|
||||
ent[c] = e;
|
||||
hent[c] = e->h;
|
||||
req[c] = r;
|
||||
hreq[c] = r->h;
|
||||
pointf[c] = (pf.Equals(pshared));
|
||||
}
|
||||
c++;
|
||||
}
|
||||
}
|
||||
if(c != 2) {
|
||||
Error("To create a tangent arc, select a point where two "
|
||||
"non-construction lines or cicles in this group and "
|
||||
"workplane join.");
|
||||
return;
|
||||
}
|
||||
|
||||
Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
|
||||
Vector wn = wrkpl->Normal()->NormalN();
|
||||
|
||||
// Based on these two entities, we make the objects that we'll use to
|
||||
// numerically find the tangent arc.
|
||||
ParametricCurve pc[2];
|
||||
pc[0].MakeFromEntity(ent[0]->h, pointf[0]);
|
||||
pc[1].MakeFromEntity(ent[1]->h, pointf[1]);
|
||||
|
||||
// And thereafter we mustn't touch the entity or req ptrs,
|
||||
// because the new requests/entities we add might force a
|
||||
// realloc.
|
||||
memset(ent, 0, sizeof(ent));
|
||||
memset(req, 0, sizeof(req));
|
||||
|
||||
Vector pinter;
|
||||
double r, vv;
|
||||
// We now do Newton iterations to find the tangent arc, and its positions
|
||||
// t back along the two curves, starting from shared point of the curves
|
||||
// at t = 0. Lots of iterations helps convergence, and this is still
|
||||
// ~10 ms for everything.
|
||||
int iters = 1000;
|
||||
double t[2] = { 0, 0 }, tp[2];
|
||||
for(i = 0; i < iters + 20; i++) {
|
||||
Vector p0 = pc[0].PointAt(t[0]),
|
||||
p1 = pc[1].PointAt(t[1]),
|
||||
t0 = pc[0].TangentAt(t[0]),
|
||||
t1 = pc[1].TangentAt(t[1]);
|
||||
|
||||
pinter = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
|
||||
p1, p1.Plus(t1),
|
||||
NULL, NULL, NULL);
|
||||
|
||||
// The sign of vv determines whether shortest distance is
|
||||
// clockwise or anti-clockwise.
|
||||
Vector v = (wn.Cross(t0)).WithMagnitude(1);
|
||||
vv = t1.Dot(v);
|
||||
|
||||
double dot = (t0.WithMagnitude(1)).Dot(t1.WithMagnitude(1));
|
||||
double theta = acos(dot);
|
||||
|
||||
if(SS.tangentArcManual) {
|
||||
r = SS.tangentArcRadius;
|
||||
} else {
|
||||
r = 200/scale;
|
||||
// Set the radius so that no more than one third of the
|
||||
// line segment disappears.
|
||||
r = min(r, pc[0].LengthForAuto()*tan(theta/2));
|
||||
r = min(r, pc[1].LengthForAuto()*tan(theta/2));;
|
||||
}
|
||||
// We are source-stepping the radius, to improve convergence. So
|
||||
// ramp that for most of the iterations, and then do a few at
|
||||
// the end with that constant for polishing.
|
||||
if(i < iters) {
|
||||
r *= 0.1 + 0.9*i/((double)iters);
|
||||
}
|
||||
|
||||
// The distance from the intersection of the lines to the endpoint
|
||||
// of the arc, along each line.
|
||||
double el = r/tan(theta/2);
|
||||
|
||||
// Compute the endpoints of the arc, for each curve
|
||||
Vector pa0 = pinter.Plus(t0.WithMagnitude(el)),
|
||||
pa1 = pinter.Plus(t1.WithMagnitude(el));
|
||||
|
||||
tp[0] = t[0];
|
||||
tp[1] = t[1];
|
||||
|
||||
// And convert those points to parameter values along the curve.
|
||||
t[0] += (pa0.Minus(p0)).DivPivoting(t0);
|
||||
t[1] += (pa1.Minus(p1)).DivPivoting(t1);
|
||||
}
|
||||
|
||||
// Stupid check for convergence, and for an out of range result (as
|
||||
// we would get, for example, if the line is too short to fit the
|
||||
// rounding arc).
|
||||
if(fabs(tp[0] - t[0]) > 1e-3 || fabs(tp[1] - t[1]) > 1e-3 ||
|
||||
t[0] < 0.01 || t[1] < 0.01 ||
|
||||
t[0] > 0.99 || t[1] > 0.99 ||
|
||||
isnan(t[0]) || isnan(t[1]))
|
||||
{
|
||||
Error("Couldn't round this corner. Try a smaller radius, or try "
|
||||
"creating the desired geometry by hand with tangency "
|
||||
"constraints.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the location of the center of the arc
|
||||
Vector center = pc[0].PointAt(t[0]),
|
||||
v0inter = pinter.Minus(center);
|
||||
int a, b;
|
||||
if(vv < 0) {
|
||||
a = 1; b = 2;
|
||||
center = center.Minus(v0inter.Cross(wn).WithMagnitude(r));
|
||||
} else {
|
||||
a = 2; b = 1;
|
||||
center = center.Plus(v0inter.Cross(wn).WithMagnitude(r));
|
||||
}
|
||||
|
||||
SS.UndoRemember();
|
||||
|
||||
hRequest harc = AddRequest(Request::ARC_OF_CIRCLE, false);
|
||||
Entity *earc = SK.GetEntity(harc.entity(0));
|
||||
hEntity hearc = earc->h;
|
||||
|
||||
SK.GetEntity(earc->point[0])->PointForceTo(center);
|
||||
SK.GetEntity(earc->point[a])->PointForceTo(pc[0].PointAt(t[0]));
|
||||
SK.GetEntity(earc->point[b])->PointForceTo(pc[1].PointAt(t[1]));
|
||||
|
||||
earc = NULL;
|
||||
|
||||
pc[0].CreateRequestTrimmedTo(t[0], !SS.tangentArcDeleteOld,
|
||||
hent[0], hearc, (b == 1));
|
||||
pc[1].CreateRequestTrimmedTo(t[1], !SS.tangentArcDeleteOld,
|
||||
hent[1], hearc, (a == 1));
|
||||
|
||||
// Now either make the original entities construction, or delete them
|
||||
// entirely, according to user preference.
|
||||
Request *re;
|
||||
SK.request.ClearTags();
|
||||
for(re = SK.request.First(); re; re = SK.request.NextAfter(re)) {
|
||||
if(re->h.v == hreq[0].v || re->h.v == hreq[1].v) {
|
||||
if(SS.tangentArcDeleteOld) {
|
||||
re->tag = 1;
|
||||
} else {
|
||||
re->construction = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(SS.tangentArcDeleteOld) {
|
||||
DeleteTaggedRequests();
|
||||
}
|
||||
|
||||
SS.later.generateAll = true;
|
||||
}
|
||||
|
||||
hEntity GraphicsWindow::SplitLine(hEntity he, Vector pinter) {
|
||||
// Save the original endpoints, since we're about to delete this entity.
|
||||
Entity *e01 = SK.GetEntity(he);
|
||||
hEntity hep0 = e01->point[0], hep1 = e01->point[1];
|
||||
Vector p0 = SK.GetEntity(hep0)->PointGetNum(),
|
||||
p1 = SK.GetEntity(hep1)->PointGetNum();
|
||||
|
||||
// Add the two line segments this one gets split into.
|
||||
hRequest r0i = AddRequest(Request::LINE_SEGMENT, false),
|
||||
ri1 = AddRequest(Request::LINE_SEGMENT, false);
|
||||
// Don't get entities till after adding, realloc issues
|
||||
|
||||
Entity *e0i = SK.GetEntity(r0i.entity(0)),
|
||||
*ei1 = SK.GetEntity(ri1.entity(0));
|
||||
|
||||
SK.GetEntity(e0i->point[0])->PointForceTo(p0);
|
||||
SK.GetEntity(e0i->point[1])->PointForceTo(pinter);
|
||||
SK.GetEntity(ei1->point[0])->PointForceTo(pinter);
|
||||
SK.GetEntity(ei1->point[1])->PointForceTo(p1);
|
||||
|
||||
ReplacePointInConstraints(hep0, e0i->point[0]);
|
||||
ReplacePointInConstraints(hep1, ei1->point[1]);
|
||||
Constraint::ConstrainCoincident(e0i->point[1], ei1->point[0]);
|
||||
return e0i->point[1];
|
||||
}
|
||||
|
||||
hEntity GraphicsWindow::SplitCircle(hEntity he, Vector pinter) {
|
||||
Entity *circle = SK.GetEntity(he);
|
||||
if(circle->type == Entity::CIRCLE) {
|
||||
// Start with an unbroken circle, split it into a 360 degree arc.
|
||||
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
|
||||
|
||||
circle = NULL; // shortly invalid!
|
||||
hRequest hr = AddRequest(Request::ARC_OF_CIRCLE, false);
|
||||
|
||||
Entity *arc = SK.GetEntity(hr.entity(0));
|
||||
|
||||
SK.GetEntity(arc->point[0])->PointForceTo(center);
|
||||
SK.GetEntity(arc->point[1])->PointForceTo(pinter);
|
||||
SK.GetEntity(arc->point[2])->PointForceTo(pinter);
|
||||
|
||||
Constraint::ConstrainCoincident(arc->point[1], arc->point[2]);
|
||||
return arc->point[1];
|
||||
} else {
|
||||
// Start with an arc, break it in to two arcs
|
||||
hEntity hc = circle->point[0],
|
||||
hs = circle->point[1],
|
||||
hf = circle->point[2];
|
||||
Vector center = SK.GetEntity(hc)->PointGetNum(),
|
||||
start = SK.GetEntity(hs)->PointGetNum(),
|
||||
finish = SK.GetEntity(hf)->PointGetNum();
|
||||
|
||||
circle = NULL; // shortly invalid!
|
||||
hRequest hr0 = AddRequest(Request::ARC_OF_CIRCLE, false),
|
||||
hr1 = AddRequest(Request::ARC_OF_CIRCLE, false);
|
||||
|
||||
Entity *arc0 = SK.GetEntity(hr0.entity(0)),
|
||||
*arc1 = SK.GetEntity(hr1.entity(0));
|
||||
|
||||
SK.GetEntity(arc0->point[0])->PointForceTo(center);
|
||||
SK.GetEntity(arc0->point[1])->PointForceTo(start);
|
||||
SK.GetEntity(arc0->point[2])->PointForceTo(pinter);
|
||||
|
||||
SK.GetEntity(arc1->point[0])->PointForceTo(center);
|
||||
SK.GetEntity(arc1->point[1])->PointForceTo(pinter);
|
||||
SK.GetEntity(arc1->point[2])->PointForceTo(finish);
|
||||
|
||||
ReplacePointInConstraints(hs, arc0->point[1]);
|
||||
ReplacePointInConstraints(hf, arc1->point[2]);
|
||||
Constraint::ConstrainCoincident(arc0->point[2], arc1->point[1]);
|
||||
return arc0->point[2];
|
||||
}
|
||||
}
|
||||
|
||||
hEntity GraphicsWindow::SplitCubic(hEntity he, Vector pinter) {
|
||||
// Save the original endpoints, since we're about to delete this entity.
|
||||
Entity *e01 = SK.GetEntity(he);
|
||||
SBezierList sbl;
|
||||
ZERO(&sbl);
|
||||
e01->GenerateBezierCurves(&sbl);
|
||||
|
||||
hEntity hep0 = e01->point[0],
|
||||
hep1 = e01->point[3+e01->extraPoints],
|
||||
hep0n = Entity::NO_ENTITY, // the new start point
|
||||
hep1n = Entity::NO_ENTITY, // the new finish point
|
||||
hepin = Entity::NO_ENTITY; // the intersection point
|
||||
|
||||
// The curve may consist of multiple cubic segments. So find which one
|
||||
// contains the intersection point.
|
||||
double t;
|
||||
int i, j;
|
||||
for(i = 0; i < sbl.l.n; i++) {
|
||||
SBezier *sb = &(sbl.l.elem[i]);
|
||||
if(sb->deg != 3) oops();
|
||||
|
||||
sb->ClosestPointTo(pinter, &t, false);
|
||||
if(pinter.Equals(sb->PointAt(t))) {
|
||||
// Split that segment at the intersection.
|
||||
SBezier b0i, bi1, b01 = *sb;
|
||||
b01.SplitAt(t, &b0i, &bi1);
|
||||
|
||||
// Add the two cubic segments this one gets split into.
|
||||
hRequest r0i = AddRequest(Request::CUBIC, false),
|
||||
ri1 = AddRequest(Request::CUBIC, false);
|
||||
// Don't get entities till after adding, realloc issues
|
||||
|
||||
Entity *e0i = SK.GetEntity(r0i.entity(0)),
|
||||
*ei1 = SK.GetEntity(ri1.entity(0));
|
||||
|
||||
for(j = 0; j <= 3; j++) {
|
||||
SK.GetEntity(e0i->point[j])->PointForceTo(b0i.ctrl[j]);
|
||||
}
|
||||
for(j = 0; j <= 3; j++) {
|
||||
SK.GetEntity(ei1->point[j])->PointForceTo(bi1.ctrl[j]);
|
||||
}
|
||||
|
||||
Constraint::ConstrainCoincident(e0i->point[3], ei1->point[0]);
|
||||
if(i == 0) hep0n = e0i->point[0];
|
||||
hep1n = ei1->point[3];
|
||||
hepin = e0i->point[3];
|
||||
} else {
|
||||
hRequest r = AddRequest(Request::CUBIC, false);
|
||||
Entity *e = SK.GetEntity(r.entity(0));
|
||||
|
||||
for(j = 0; j <= 3; j++) {
|
||||
SK.GetEntity(e->point[j])->PointForceTo(sb->ctrl[j]);
|
||||
}
|
||||
|
||||
if(i == 0) hep0n = e->point[0];
|
||||
hep1n = e->point[3];
|
||||
}
|
||||
}
|
||||
|
||||
sbl.Clear();
|
||||
|
||||
ReplacePointInConstraints(hep0, hep0n);
|
||||
ReplacePointInConstraints(hep1, hep1n);
|
||||
return hepin;
|
||||
}
|
||||
|
||||
hEntity GraphicsWindow::SplitEntity(hEntity he, Vector pinter) {
|
||||
Entity *e = SK.GetEntity(he);
|
||||
int entityType = e->type;
|
||||
|
||||
hEntity ret;
|
||||
if(e->IsCircle()) {
|
||||
ret = SplitCircle(he, pinter);
|
||||
} else if(e->type == Entity::LINE_SEGMENT) {
|
||||
ret = SplitLine(he, pinter);
|
||||
} else if(e->type == Entity::CUBIC || e->type == Entity::CUBIC_PERIODIC) {
|
||||
ret = SplitCubic(he, pinter);
|
||||
} else {
|
||||
Error("Couldn't split this entity; lines, circles, or cubics only.");
|
||||
return Entity::NO_ENTITY;
|
||||
}
|
||||
|
||||
// Finally, delete the request that generated the original entity.
|
||||
int reqType = EntReqTable::GetRequestForEntity(entityType);
|
||||
SK.request.ClearTags();
|
||||
for(int i = 0; i < SK.request.n; i++) {
|
||||
Request *r = &(SK.request.elem[i]);
|
||||
if(r->group.v != activeGroup.v) continue;
|
||||
if(r->type != reqType) continue;
|
||||
|
||||
// If the user wants to keep the old entities around, they can just
|
||||
// mark them construction first.
|
||||
if(he.v == r->h.entity(0).v && !r->construction) {
|
||||
r->tag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DeleteTaggedRequests();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GraphicsWindow::SplitLinesOrCurves(void) {
|
||||
if(!LockedInWorkplane()) {
|
||||
Error("Must be sketching in workplane to split.");
|
||||
return;
|
||||
}
|
||||
|
||||
GroupSelection();
|
||||
if(!(gs.n == 2 &&(gs.lineSegments +
|
||||
gs.circlesOrArcs +
|
||||
gs.cubics +
|
||||
gs.periodicCubics) == 2))
|
||||
{
|
||||
Error("Select two entities that intersect each other (e.g. two lines "
|
||||
"or two circles or a circle and a line).");
|
||||
return;
|
||||
}
|
||||
|
||||
hEntity ha = gs.entity[0],
|
||||
hb = gs.entity[1];
|
||||
Entity *ea = SK.GetEntity(ha),
|
||||
*eb = SK.GetEntity(hb);
|
||||
|
||||
// Compute the possibly-rational Bezier curves for each of these entities
|
||||
SBezierList sbla, sblb;
|
||||
ZERO(&sbla);
|
||||
ZERO(&sblb);
|
||||
ea->GenerateBezierCurves(&sbla);
|
||||
eb->GenerateBezierCurves(&sblb);
|
||||
// and then compute the points where they intersect, based on those curves.
|
||||
SPointList inters;
|
||||
ZERO(&inters);
|
||||
sbla.AllIntersectionsWith(&sblb, &inters);
|
||||
|
||||
if(inters.l.n > 0) {
|
||||
Vector pi;
|
||||
// If there's multiple points, then take the one closest to the
|
||||
// mouse pointer.
|
||||
double dmin = VERY_POSITIVE;
|
||||
SPoint *sp;
|
||||
for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
|
||||
double d = ProjectPoint(sp->p).DistanceTo(currentMousePosition);
|
||||
if(d < dmin) {
|
||||
dmin = d;
|
||||
pi = sp->p;
|
||||
}
|
||||
}
|
||||
|
||||
SS.UndoRemember();
|
||||
hEntity hia = SplitEntity(ha, pi),
|
||||
hib = SplitEntity(hb, pi);
|
||||
// SplitEntity adds the coincident constraints to join the split halves
|
||||
// of each original entity; and then we add the constraint to join
|
||||
// the two entities together at the split point.
|
||||
if(hia.v && hib.v) {
|
||||
Constraint::ConstrainCoincident(hia, hib);
|
||||
}
|
||||
} else {
|
||||
Error("Can't split; no intersection found.");
|
||||
}
|
||||
|
||||
// All done, clean up and regenerate.
|
||||
inters.Clear();
|
||||
sbla.Clear();
|
||||
sblb.Clear();
|
||||
ClearSelection();
|
||||
SS.later.generateAll = true;
|
||||
}
|
||||
|
41
png2c.pl
|
@ -1,41 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use GD;
|
||||
|
||||
my ($out, $proto) = @ARGV;
|
||||
open(OUT, ">$out") or die "$out: $!";
|
||||
open(PROTO, ">$proto") or die "$proto: $!";
|
||||
|
||||
for $file (<icons/*.png>) {
|
||||
next if $file =~ /char-/;
|
||||
|
||||
$file =~ m#.*/(.*)\.png#;
|
||||
$base = "Icon_$1";
|
||||
$base =~ y/-/_/;
|
||||
|
||||
open(PNG, $file) or die "$file: $!\n";
|
||||
$img = newFromPng GD::Image(\*PNG) or die;
|
||||
$img->trueColor(1);
|
||||
|
||||
close PNG;
|
||||
|
||||
($width, $height) = $img->getBounds();
|
||||
die "$file: $width, $height" if ($width != 24) or ($height != 24);
|
||||
|
||||
print PROTO "extern unsigned char $base\[24*24*3\];";
|
||||
print OUT "unsigned char $base\[24*24*3] = {\n";
|
||||
|
||||
for($y = 0; $y < 24; $y++) {
|
||||
for($x = 0; $x < 24; $x++) {
|
||||
$index = $img->getPixel($x, 23-$y);
|
||||
($r, $g, $b) = $img->rgb($index);
|
||||
if($r + $g + $b < 11) {
|
||||
($r, $g, $b) = (30, 30, 30);
|
||||
}
|
||||
printf OUT " 0x%02x, 0x%02x, 0x%02x,\n", $r, $g, $b;
|
||||
}
|
||||
}
|
||||
|
||||
print OUT "};\n\n";
|
||||
|
||||
}
|
28
pngchar2c.pl
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use GD;
|
||||
|
||||
for $file (sort <icons/char-*.png>) {
|
||||
open(PNG, $file) or die "$file: $!\n";
|
||||
$img = newFromPng GD::Image(\*PNG) or die;
|
||||
$img->trueColor(1);
|
||||
close PNG;
|
||||
|
||||
($width, $height) = $img->getBounds();
|
||||
die "$file: $width, $height" if ($width != 16) or ($height != 16);
|
||||
|
||||
for($x = 0; $x < 16; $x++) {
|
||||
for($y = 0; $y < 16; $y++) {
|
||||
$index = $img->getPixel($x, $y);
|
||||
($r, $g, $b) = $img->rgb($index);
|
||||
if($r + $g + $b < 11) {
|
||||
print " 0, ";
|
||||
} else {
|
||||
print "255, ";
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
866
polygon.cpp
|
@ -1,866 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Operations on polygons (planar, of line segment edges).
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
Vector STriangle::Normal(void) {
|
||||
Vector ab = b.Minus(a), bc = c.Minus(b);
|
||||
return ab.Cross(bc);
|
||||
}
|
||||
|
||||
double STriangle::MinAltitude(void) {
|
||||
double altA = a.DistanceToLine(b, c.Minus(b)),
|
||||
altB = b.DistanceToLine(c, a.Minus(c)),
|
||||
altC = c.DistanceToLine(a, b.Minus(a));
|
||||
|
||||
return min(altA, min(altB, altC));
|
||||
}
|
||||
|
||||
bool STriangle::ContainsPoint(Vector p) {
|
||||
Vector n = Normal();
|
||||
if(MinAltitude() < LENGTH_EPS) {
|
||||
// shouldn't happen; zero-area triangle
|
||||
return false;
|
||||
}
|
||||
return ContainsPointProjd(n.WithMagnitude(1), p);
|
||||
}
|
||||
|
||||
bool STriangle::ContainsPointProjd(Vector n, Vector p) {
|
||||
Vector ab = b.Minus(a), bc = c.Minus(b), ca = a.Minus(c);
|
||||
|
||||
Vector no_ab = n.Cross(ab);
|
||||
if(no_ab.Dot(p) < no_ab.Dot(a) - LENGTH_EPS) return false;
|
||||
|
||||
Vector no_bc = n.Cross(bc);
|
||||
if(no_bc.Dot(p) < no_bc.Dot(b) - LENGTH_EPS) return false;
|
||||
|
||||
Vector no_ca = n.Cross(ca);
|
||||
if(no_ca.Dot(p) < no_ca.Dot(c) - LENGTH_EPS) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void STriangle::FlipNormal(void) {
|
||||
SWAP(Vector, a, b);
|
||||
SWAP(Vector, an, bn);
|
||||
}
|
||||
|
||||
STriangle STriangle::From(STriMeta meta, Vector a, Vector b, Vector c) {
|
||||
STriangle tr;
|
||||
ZERO(&tr);
|
||||
tr.meta = meta;
|
||||
tr.a = a;
|
||||
tr.b = b;
|
||||
tr.c = c;
|
||||
return tr;
|
||||
}
|
||||
|
||||
SEdge SEdge::From(Vector a, Vector b) {
|
||||
SEdge se = { 0, 0, 0, a, b };
|
||||
return se;
|
||||
}
|
||||
|
||||
bool SEdge::EdgeCrosses(Vector ea, Vector eb, Vector *ppi, SPointList *spl) {
|
||||
Vector d = eb.Minus(ea);
|
||||
double t_eps = LENGTH_EPS/d.Magnitude();
|
||||
|
||||
double dist_a, dist_b;
|
||||
double t, tthis;
|
||||
bool skew;
|
||||
Vector pi;
|
||||
bool inOrEdge0, inOrEdge1;
|
||||
|
||||
Vector dthis = b.Minus(a);
|
||||
double tthis_eps = LENGTH_EPS/dthis.Magnitude();
|
||||
|
||||
if((ea.Equals(a) && eb.Equals(b)) ||
|
||||
(eb.Equals(a) && ea.Equals(b)))
|
||||
{
|
||||
if(ppi) *ppi = a;
|
||||
if(spl) spl->Add(a);
|
||||
return true;
|
||||
}
|
||||
|
||||
dist_a = a.DistanceToLine(ea, d),
|
||||
dist_b = b.DistanceToLine(ea, d);
|
||||
|
||||
// Can't just test if dist_a equals dist_b; they could be on opposite
|
||||
// sides, since it's unsigned.
|
||||
double m = sqrt(d.Magnitude()*dthis.Magnitude());
|
||||
if(sqrt(fabs(d.Dot(dthis))) > (m - LENGTH_EPS)) {
|
||||
// The edges are parallel.
|
||||
if(fabs(dist_a) > LENGTH_EPS) {
|
||||
// and not coincident, so can't be interesecting
|
||||
return false;
|
||||
}
|
||||
// The edges are coincident. Make sure that neither endpoint lies
|
||||
// on the other
|
||||
bool inters = false;
|
||||
double t;
|
||||
t = a.Minus(ea).DivPivoting(d);
|
||||
if(t > t_eps && t < (1 - t_eps)) inters = true;
|
||||
t = b.Minus(ea).DivPivoting(d);
|
||||
if(t > t_eps && t < (1 - t_eps)) inters = true;
|
||||
t = ea.Minus(a).DivPivoting(dthis);
|
||||
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
|
||||
t = eb.Minus(a).DivPivoting(dthis);
|
||||
if(t > tthis_eps && t < (1 - tthis_eps)) inters = true;
|
||||
|
||||
if(inters) {
|
||||
if(ppi) *ppi = a;
|
||||
if(spl) spl->Add(a);
|
||||
return true;
|
||||
} else {
|
||||
// So coincident but disjoint, okay.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Lines are not parallel, so look for an intersection.
|
||||
pi = Vector::AtIntersectionOfLines(ea, eb, a, b,
|
||||
&skew,
|
||||
&t, &tthis);
|
||||
if(skew) return false;
|
||||
|
||||
inOrEdge0 = (t > -t_eps) && (t < (1 + t_eps));
|
||||
inOrEdge1 = (tthis > -tthis_eps) && (tthis < (1 + tthis_eps));
|
||||
|
||||
if(inOrEdge0 && inOrEdge1) {
|
||||
if(a.Equals(ea) || b.Equals(ea) ||
|
||||
a.Equals(eb) || b.Equals(eb))
|
||||
{
|
||||
// Not an intersection if we share an endpoint with an edge
|
||||
return false;
|
||||
}
|
||||
// But it's an intersection if a vertex of one edge lies on the
|
||||
// inside of the other (or if they cross away from either's
|
||||
// vertex).
|
||||
if(ppi) *ppi = pi;
|
||||
if(spl) spl->Add(pi);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SEdgeList::Clear(void) {
|
||||
l.Clear();
|
||||
}
|
||||
|
||||
void SEdgeList::AddEdge(Vector a, Vector b, int auxA, int auxB) {
|
||||
SEdge e; ZERO(&e);
|
||||
e.a = a;
|
||||
e.b = b;
|
||||
e.auxA = auxA;
|
||||
e.auxB = auxB;
|
||||
l.Add(&e);
|
||||
}
|
||||
|
||||
bool SEdgeList::AssembleContour(Vector first, Vector last, SContour *dest,
|
||||
SEdge *errorAt, bool keepDir)
|
||||
{
|
||||
int i;
|
||||
|
||||
dest->AddPoint(first);
|
||||
dest->AddPoint(last);
|
||||
|
||||
do {
|
||||
for(i = 0; i < l.n; i++) {
|
||||
SEdge *se = &(l.elem[i]);
|
||||
if(se->tag) continue;
|
||||
|
||||
if(se->a.Equals(last)) {
|
||||
dest->AddPoint(se->b);
|
||||
last = se->b;
|
||||
se->tag = 1;
|
||||
break;
|
||||
}
|
||||
// Don't allow backwards edges if keepDir is true.
|
||||
if(!keepDir && se->b.Equals(last)) {
|
||||
dest->AddPoint(se->a);
|
||||
last = se->a;
|
||||
se->tag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i >= l.n) {
|
||||
// Couldn't assemble a closed contour; mark where.
|
||||
if(errorAt) {
|
||||
errorAt->a = first;
|
||||
errorAt->b = last;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} while(!last.Equals(first));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt, bool keepDir) {
|
||||
dest->Clear();
|
||||
|
||||
bool allClosed = true;
|
||||
for(;;) {
|
||||
Vector first, last;
|
||||
int i;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
if(!l.elem[i].tag) {
|
||||
first = l.elem[i].a;
|
||||
last = l.elem[i].b;
|
||||
l.elem[i].tag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i >= l.n) {
|
||||
return allClosed;
|
||||
}
|
||||
|
||||
// Create a new empty contour in our polygon, and finish assembling
|
||||
// into that contour.
|
||||
dest->AddEmptyContour();
|
||||
if(!AssembleContour(first, last, &(dest->l.elem[dest->l.n-1]),
|
||||
errorAt, keepDir))
|
||||
{
|
||||
allClosed = false;
|
||||
}
|
||||
// But continue assembling, even if some of the contours are open
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Test if the specified edge crosses any of the edges in our list. Two edges
|
||||
// are not considered to cross if they share an endpoint (within LENGTH_EPS),
|
||||
// but they are considered to cross if they are coincident and overlapping.
|
||||
// If pi is not NULL, then a crossing is returned in that.
|
||||
//-----------------------------------------------------------------------------
|
||||
int SEdgeList::AnyEdgeCrossings(Vector a, Vector b,
|
||||
Vector *ppi, SPointList *spl)
|
||||
{
|
||||
int cnt = 0;
|
||||
SEdge *se;
|
||||
for(se = l.First(); se; se = l.NextAfter(se)) {
|
||||
if(se->EdgeCrosses(a, b, ppi, spl)) {
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns true if the intersecting edge list contains an edge that shares
|
||||
// an endpoint with one of our edges.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool SEdgeList::ContainsEdgeFrom(SEdgeList *sel) {
|
||||
SEdge *se;
|
||||
for(se = l.First(); se; se = l.NextAfter(se)) {
|
||||
if(sel->ContainsEdge(se)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool SEdgeList::ContainsEdge(SEdge *set) {
|
||||
SEdge *se;
|
||||
for(se = l.First(); se; se = l.NextAfter(se)) {
|
||||
if((se->a).Equals(set->a) && (se->b).Equals(set->b)) return true;
|
||||
if((se->b).Equals(set->a) && (se->a).Equals(set->b)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Remove unnecessary edges: if two are anti-parallel then remove both, and if
|
||||
// two are parallel then remove one.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SEdgeList::CullExtraneousEdges(void) {
|
||||
l.ClearTags();
|
||||
int i, j;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
SEdge *se = &(l.elem[i]);
|
||||
for(j = i+1; j < l.n; j++) {
|
||||
SEdge *set = &(l.elem[j]);
|
||||
if((set->a).Equals(se->a) && (set->b).Equals(se->b)) {
|
||||
// Two parallel edges exist; so keep only the first one.
|
||||
set->tag = 1;
|
||||
}
|
||||
if((set->a).Equals(se->b) && (set->b).Equals(se->a)) {
|
||||
// Two anti-parallel edges exist; so keep neither.
|
||||
se->tag = 1;
|
||||
set->tag = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
l.RemoveTagged();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Make a kd-tree of edges. This is used for O(log(n)) implementations of stuff
|
||||
// that would naively be O(n).
|
||||
//-----------------------------------------------------------------------------
|
||||
SKdNodeEdges *SKdNodeEdges::Alloc(void) {
|
||||
return (SKdNodeEdges *)AllocTemporary(sizeof(SKdNodeEdges));
|
||||
}
|
||||
SEdgeLl *SEdgeLl::Alloc(void) {
|
||||
return (SEdgeLl *)AllocTemporary(sizeof(SEdgeLl));
|
||||
}
|
||||
SKdNodeEdges *SKdNodeEdges::From(SEdgeList *sel) {
|
||||
SEdgeLl *sell = NULL;
|
||||
SEdge *se;
|
||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||
SEdgeLl *n = SEdgeLl::Alloc();
|
||||
n->se = se;
|
||||
n->next = sell;
|
||||
sell = n;
|
||||
}
|
||||
return SKdNodeEdges::From(sell);
|
||||
}
|
||||
SKdNodeEdges *SKdNodeEdges::From(SEdgeLl *sell) {
|
||||
SKdNodeEdges *n = SKdNodeEdges::Alloc();
|
||||
|
||||
// Compute the midpoints (just mean, though median would be better) of
|
||||
// each component.
|
||||
Vector ptAve = Vector::From(0, 0, 0);
|
||||
SEdgeLl *flip;
|
||||
int totaln = 0;
|
||||
for(flip = sell; flip; flip = flip->next) {
|
||||
ptAve = ptAve.Plus(flip->se->a);
|
||||
ptAve = ptAve.Plus(flip->se->b);
|
||||
totaln++;
|
||||
}
|
||||
ptAve = ptAve.ScaledBy(1.0 / (2*totaln));
|
||||
|
||||
// For each component, see how it splits.
|
||||
int ltln[3] = { 0, 0, 0 }, gtln[3] = { 0, 0, 0 };
|
||||
double badness[3];
|
||||
for(flip = sell; flip; flip = flip->next) {
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(flip->se->a.Element(i) < ptAve.Element(i) + KDTREE_EPS ||
|
||||
flip->se->b.Element(i) < ptAve.Element(i) + KDTREE_EPS)
|
||||
{
|
||||
ltln[i]++;
|
||||
}
|
||||
if(flip->se->a.Element(i) > ptAve.Element(i) - KDTREE_EPS ||
|
||||
flip->se->b.Element(i) > ptAve.Element(i) - KDTREE_EPS)
|
||||
{
|
||||
gtln[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < 3; i++) {
|
||||
badness[i] = pow((double)ltln[i], 4) + pow((double)gtln[i], 4);
|
||||
}
|
||||
|
||||
// Choose the least bad coordinate to split along.
|
||||
if(badness[0] < badness[1] && badness[0] < badness[2]) {
|
||||
n->which = 0;
|
||||
} else if(badness[1] < badness[2]) {
|
||||
n->which = 1;
|
||||
} else {
|
||||
n->which = 2;
|
||||
}
|
||||
n->c = ptAve.Element(n->which);
|
||||
|
||||
if(totaln < 3 || totaln == gtln[n->which] || totaln == ltln[n->which]) {
|
||||
n->edges = sell;
|
||||
// and we're a leaf node
|
||||
return n;
|
||||
}
|
||||
|
||||
// Sort the edges according to which side(s) of the split plane they're on.
|
||||
SEdgeLl *gtl = NULL, *ltl = NULL;
|
||||
for(flip = sell; flip; flip = flip->next) {
|
||||
if(flip->se->a.Element(n->which) < n->c + KDTREE_EPS ||
|
||||
flip->se->b.Element(n->which) < n->c + KDTREE_EPS)
|
||||
{
|
||||
SEdgeLl *selln = SEdgeLl::Alloc();
|
||||
selln->se = flip->se;
|
||||
selln->next = ltl;
|
||||
ltl = selln;
|
||||
}
|
||||
if(flip->se->a.Element(n->which) > n->c - KDTREE_EPS ||
|
||||
flip->se->b.Element(n->which) > n->c - KDTREE_EPS)
|
||||
{
|
||||
SEdgeLl *selln = SEdgeLl::Alloc();
|
||||
selln->se = flip->se;
|
||||
selln->next = gtl;
|
||||
gtl = selln;
|
||||
}
|
||||
}
|
||||
|
||||
n->lt = SKdNodeEdges::From(ltl);
|
||||
n->gt = SKdNodeEdges::From(gtl);
|
||||
return n;
|
||||
}
|
||||
|
||||
int SKdNodeEdges::AnyEdgeCrossings(Vector a, Vector b, int cnt,
|
||||
Vector *pi, SPointList *spl)
|
||||
{
|
||||
int inters = 0;
|
||||
if(gt && lt) {
|
||||
if(a.Element(which) < c + KDTREE_EPS ||
|
||||
b.Element(which) < c + KDTREE_EPS)
|
||||
{
|
||||
inters += lt->AnyEdgeCrossings(a, b, cnt, pi, spl);
|
||||
}
|
||||
if(a.Element(which) > c - KDTREE_EPS ||
|
||||
b.Element(which) > c - KDTREE_EPS)
|
||||
{
|
||||
inters += gt->AnyEdgeCrossings(a, b, cnt, pi, spl);
|
||||
}
|
||||
} else {
|
||||
SEdgeLl *sell;
|
||||
for(sell = edges; sell; sell = sell->next) {
|
||||
SEdge *se = sell->se;
|
||||
if(se->tag == cnt) continue;
|
||||
if(se->EdgeCrosses(a, b, pi, spl)) {
|
||||
inters++;
|
||||
}
|
||||
se->tag = cnt;
|
||||
}
|
||||
}
|
||||
return inters;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// We have an edge list that contains only collinear edges, maybe with more
|
||||
// splits than necessary. Merge any collinear segments that join.
|
||||
//-----------------------------------------------------------------------------
|
||||
static Vector LineStart, LineDirection;
|
||||
static int ByTAlongLine(const void *av, const void *bv)
|
||||
{
|
||||
SEdge *a = (SEdge *)av,
|
||||
*b = (SEdge *)bv;
|
||||
|
||||
double ta = (a->a.Minus(LineStart)).DivPivoting(LineDirection),
|
||||
tb = (b->a.Minus(LineStart)).DivPivoting(LineDirection);
|
||||
|
||||
return (ta > tb) ? 1 : -1;
|
||||
}
|
||||
void SEdgeList::MergeCollinearSegments(Vector a, Vector b) {
|
||||
LineStart = a;
|
||||
LineDirection = b.Minus(a);
|
||||
qsort(l.elem, l.n, sizeof(l.elem[0]), ByTAlongLine);
|
||||
|
||||
l.ClearTags();
|
||||
int i;
|
||||
for(i = 1; i < l.n; i++) {
|
||||
SEdge *prev = &(l.elem[i-1]),
|
||||
*now = &(l.elem[i]);
|
||||
|
||||
if((prev->b).Equals(now->a)) {
|
||||
// The previous segment joins up to us; so merge it into us.
|
||||
prev->tag = 1;
|
||||
now->a = prev->a;
|
||||
}
|
||||
}
|
||||
l.RemoveTagged();
|
||||
}
|
||||
|
||||
void SPointList::Clear(void) {
|
||||
l.Clear();
|
||||
}
|
||||
|
||||
bool SPointList::ContainsPoint(Vector pt) {
|
||||
return (IndexForPoint(pt) >= 0);
|
||||
}
|
||||
|
||||
int SPointList::IndexForPoint(Vector pt) {
|
||||
int i;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
SPoint *p = &(l.elem[i]);
|
||||
if(pt.Equals(p->p)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// Not found, so return negative to indicate that.
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SPointList::IncrementTagFor(Vector pt) {
|
||||
SPoint *p;
|
||||
for(p = l.First(); p; p = l.NextAfter(p)) {
|
||||
if(pt.Equals(p->p)) {
|
||||
(p->tag)++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
SPoint pa;
|
||||
pa.p = pt;
|
||||
pa.tag = 1;
|
||||
l.Add(&pa);
|
||||
}
|
||||
|
||||
void SPointList::Add(Vector pt) {
|
||||
SPoint p;
|
||||
ZERO(&p);
|
||||
p.p = pt;
|
||||
l.Add(&p);
|
||||
}
|
||||
|
||||
void SContour::AddPoint(Vector p) {
|
||||
SPoint sp;
|
||||
sp.tag = 0;
|
||||
sp.p = p;
|
||||
|
||||
l.Add(&sp);
|
||||
}
|
||||
|
||||
void SContour::MakeEdgesInto(SEdgeList *el) {
|
||||
int i;
|
||||
for(i = 0; i < (l.n - 1); i++) {
|
||||
el->AddEdge(l.elem[i].p, l.elem[i+1].p);
|
||||
}
|
||||
}
|
||||
|
||||
void SContour::CopyInto(SContour *dest) {
|
||||
SPoint *sp;
|
||||
for(sp = l.First(); sp; sp = l.NextAfter(sp)) {
|
||||
dest->AddPoint(sp->p);
|
||||
}
|
||||
}
|
||||
|
||||
void SContour::FindPointWithMinX(void) {
|
||||
SPoint *sp;
|
||||
xminPt = Vector::From(1e10, 1e10, 1e10);
|
||||
for(sp = l.First(); sp; sp = l.NextAfter(sp)) {
|
||||
if(sp->p.x < xminPt.x) {
|
||||
xminPt = sp->p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector SContour::ComputeNormal(void) {
|
||||
Vector n = Vector::From(0, 0, 0);
|
||||
|
||||
for(int i = 0; i < l.n - 2; i++) {
|
||||
Vector u = (l.elem[i+1].p).Minus(l.elem[i+0].p).WithMagnitude(1);
|
||||
Vector v = (l.elem[i+2].p).Minus(l.elem[i+1].p).WithMagnitude(1);
|
||||
Vector nt = u.Cross(v);
|
||||
if(nt.Magnitude() > n.Magnitude()) {
|
||||
n = nt;
|
||||
}
|
||||
}
|
||||
return n.WithMagnitude(1);
|
||||
}
|
||||
|
||||
Vector SContour::AnyEdgeMidpoint(void) {
|
||||
if(l.n < 2) oops();
|
||||
return ((l.elem[0].p).Plus(l.elem[1].p)).ScaledBy(0.5);
|
||||
}
|
||||
|
||||
bool SContour::IsClockwiseProjdToNormal(Vector n) {
|
||||
// Degenerate things might happen as we draw; doesn't really matter
|
||||
// what we do then.
|
||||
if(n.Magnitude() < 0.01) return true;
|
||||
|
||||
return (SignedAreaProjdToNormal(n) < 0);
|
||||
}
|
||||
|
||||
double SContour::SignedAreaProjdToNormal(Vector n) {
|
||||
// An arbitrary 2d coordinate system that has n as its normal
|
||||
Vector u = n.Normal(0);
|
||||
Vector v = n.Normal(1);
|
||||
|
||||
double area = 0;
|
||||
for(int i = 0; i < (l.n - 1); i++) {
|
||||
double u0 = (l.elem[i ].p).Dot(u);
|
||||
double v0 = (l.elem[i ].p).Dot(v);
|
||||
double u1 = (l.elem[i+1].p).Dot(u);
|
||||
double v1 = (l.elem[i+1].p).Dot(v);
|
||||
|
||||
area += ((v0 + v1)/2)*(u1 - u0);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
bool SContour::ContainsPointProjdToNormal(Vector n, Vector p) {
|
||||
Vector u = n.Normal(0);
|
||||
Vector v = n.Normal(1);
|
||||
|
||||
double up = p.Dot(u);
|
||||
double vp = p.Dot(v);
|
||||
|
||||
bool inside = false;
|
||||
for(int i = 0; i < (l.n - 1); i++) {
|
||||
double ua = (l.elem[i ].p).Dot(u);
|
||||
double va = (l.elem[i ].p).Dot(v);
|
||||
// The curve needs to be exactly closed; approximation is death.
|
||||
double ub = (l.elem[(i+1)%(l.n-1)].p).Dot(u);
|
||||
double vb = (l.elem[(i+1)%(l.n-1)].p).Dot(v);
|
||||
|
||||
if ((((va <= vp) && (vp < vb)) ||
|
||||
((vb <= vp) && (vp < va))) &&
|
||||
(up < (ub - ua) * (vp - va) / (vb - va) + ua))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
|
||||
return inside;
|
||||
}
|
||||
|
||||
void SContour::Reverse(void) {
|
||||
l.Reverse();
|
||||
}
|
||||
|
||||
|
||||
void SPolygon::Clear(void) {
|
||||
int i;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
(l.elem[i]).l.Clear();
|
||||
}
|
||||
l.Clear();
|
||||
}
|
||||
|
||||
void SPolygon::AddEmptyContour(void) {
|
||||
SContour c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
l.Add(&c);
|
||||
}
|
||||
|
||||
void SPolygon::MakeEdgesInto(SEdgeList *el) {
|
||||
int i;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
(l.elem[i]).MakeEdgesInto(el);
|
||||
}
|
||||
}
|
||||
|
||||
Vector SPolygon::ComputeNormal(void) {
|
||||
if(l.n < 1) return Vector::From(0, 0, 0);
|
||||
return (l.elem[0]).ComputeNormal();
|
||||
}
|
||||
|
||||
double SPolygon::SignedArea(void) {
|
||||
SContour *sc;
|
||||
double area = 0;
|
||||
// This returns the true area only if the contours are all oriented
|
||||
// correctly, with the holes backwards from the outer contours.
|
||||
for(sc = l.First(); sc; sc = l.NextAfter(sc)) {
|
||||
area += sc->SignedAreaProjdToNormal(normal);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
bool SPolygon::ContainsPoint(Vector p) {
|
||||
return (WindingNumberForPoint(p) % 2) == 1;
|
||||
}
|
||||
|
||||
int SPolygon::WindingNumberForPoint(Vector p) {
|
||||
int winding = 0;
|
||||
int i;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
SContour *sc = &(l.elem[i]);
|
||||
if(sc->ContainsPointProjdToNormal(normal, p)) {
|
||||
winding++;
|
||||
}
|
||||
}
|
||||
return winding;
|
||||
}
|
||||
|
||||
void SPolygon::FixContourDirections(void) {
|
||||
// At output, the contour's tag will be 1 if we reversed it, else 0.
|
||||
l.ClearTags();
|
||||
|
||||
// Outside curve looks counterclockwise, projected against our normal.
|
||||
int i, j;
|
||||
for(i = 0; i < l.n; i++) {
|
||||
SContour *sc = &(l.elem[i]);
|
||||
if(sc->l.n < 2) continue;
|
||||
// The contours may not intersect, but they may share vertices; so
|
||||
// testing a vertex for point-in-polygon may fail, but the midpoint
|
||||
// of an edge is okay.
|
||||
Vector pt = (((sc->l.elem[0]).p).Plus(sc->l.elem[1].p)).ScaledBy(0.5);
|
||||
|
||||
sc->timesEnclosed = 0;
|
||||
bool outer = true;
|
||||
for(j = 0; j < l.n; j++) {
|
||||
if(i == j) continue;
|
||||
SContour *sct = &(l.elem[j]);
|
||||
if(sct->ContainsPointProjdToNormal(normal, pt)) {
|
||||
outer = !outer;
|
||||
(sc->timesEnclosed)++;
|
||||
}
|
||||
}
|
||||
|
||||
bool clockwise = sc->IsClockwiseProjdToNormal(normal);
|
||||
if(clockwise && outer || (!clockwise && !outer)) {
|
||||
sc->Reverse();
|
||||
sc->tag = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SPolygon::IsEmpty(void) {
|
||||
if(l.n == 0 || l.elem[0].l.n == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector SPolygon::AnyPoint(void) {
|
||||
if(IsEmpty()) oops();
|
||||
return l.elem[0].l.elem[0].p;
|
||||
}
|
||||
|
||||
bool SPolygon::SelfIntersecting(Vector *intersectsAt) {
|
||||
SEdgeList el;
|
||||
ZERO(&el);
|
||||
MakeEdgesInto(&el);
|
||||
SKdNodeEdges *kdtree = SKdNodeEdges::From(&el);
|
||||
|
||||
int cnt = 1;
|
||||
el.l.ClearTags();
|
||||
|
||||
bool ret = false;
|
||||
SEdge *se;
|
||||
for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
|
||||
int inters = kdtree->AnyEdgeCrossings(se->a, se->b, cnt, intersectsAt);
|
||||
if(inters != 1) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
|
||||
el.Clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Low-quality routines to cutter radius compensate a polygon. Assumes the
|
||||
// polygon is in the xy plane, and the contours all go in the right direction
|
||||
// with respect to normal (0, 0, -1).
|
||||
//-----------------------------------------------------------------------------
|
||||
void SPolygon::OffsetInto(SPolygon *dest, double r) {
|
||||
int i;
|
||||
dest->Clear();
|
||||
for(i = 0; i < l.n; i++) {
|
||||
SContour *sc = &(l.elem[i]);
|
||||
dest->AddEmptyContour();
|
||||
sc->OffsetInto(&(dest->l.elem[dest->l.n-1]), r);
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calculate the intersection point of two lines. Returns true for success,
|
||||
// false if they're parallel.
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool IntersectionOfLines(double x0A, double y0A, double dxA, double dyA,
|
||||
double x0B, double y0B, double dxB, double dyB,
|
||||
double *xi, double *yi)
|
||||
{
|
||||
double A[2][2];
|
||||
double b[2];
|
||||
|
||||
// The line is given to us in the form:
|
||||
// (x(t), y(t)) = (x0, y0) + t*(dx, dy)
|
||||
// so first rewrite it as
|
||||
// (x - x0, y - y0) dot (dy, -dx) = 0
|
||||
// x*dy - x0*dy - y*dx + y0*dx = 0
|
||||
// x*dy - y*dx = (x0*dy - y0*dx)
|
||||
|
||||
// So write the matrix, pre-pivoted.
|
||||
if(fabs(dyA) > fabs(dyB)) {
|
||||
A[0][0] = dyA; A[0][1] = -dxA; b[0] = x0A*dyA - y0A*dxA;
|
||||
A[1][0] = dyB; A[1][1] = -dxB; b[1] = x0B*dyB - y0B*dxB;
|
||||
} else {
|
||||
A[1][0] = dyA; A[1][1] = -dxA; b[1] = x0A*dyA - y0A*dxA;
|
||||
A[0][0] = dyB; A[0][1] = -dxB; b[0] = x0B*dyB - y0B*dxB;
|
||||
}
|
||||
|
||||
// Check the determinant; if it's zero then no solution.
|
||||
if(fabs(A[0][0]*A[1][1] - A[0][1]*A[1][0]) < LENGTH_EPS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Solve
|
||||
double v = A[1][0] / A[0][0];
|
||||
A[1][0] -= A[0][0]*v;
|
||||
A[1][1] -= A[0][1]*v;
|
||||
b[1] -= b[0]*v;
|
||||
|
||||
// Back-substitute.
|
||||
*yi = b[1] / A[1][1];
|
||||
*xi = (b[0] - A[0][1]*(*yi)) / A[0][0];
|
||||
|
||||
return true;
|
||||
}
|
||||
void SContour::OffsetInto(SContour *dest, double r) {
|
||||
int i;
|
||||
|
||||
for(i = 0; i < l.n; i++) {
|
||||
Vector a, b, c;
|
||||
Vector dp, dn;
|
||||
double thetan, thetap;
|
||||
|
||||
a = l.elem[WRAP(i-1, (l.n-1))].p;
|
||||
b = l.elem[WRAP(i, (l.n-1))].p;
|
||||
c = l.elem[WRAP(i+1, (l.n-1))].p;
|
||||
|
||||
dp = a.Minus(b);
|
||||
thetap = atan2(dp.y, dp.x);
|
||||
|
||||
dn = b.Minus(c);
|
||||
thetan = atan2(dn.y, dn.x);
|
||||
|
||||
// A short line segment in a badly-generated polygon might look
|
||||
// okay but screw up our sense of direction.
|
||||
if(dp.Magnitude() < LENGTH_EPS || dn.Magnitude() < LENGTH_EPS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(thetan > thetap && (thetan - thetap) > PI) {
|
||||
thetap += 2*PI;
|
||||
}
|
||||
if(thetan < thetap && (thetap - thetan) > PI) {
|
||||
thetan += 2*PI;
|
||||
}
|
||||
|
||||
if(fabs(thetan - thetap) < (1*PI)/180) {
|
||||
Vector p = { b.x - r*sin(thetap), b.y + r*cos(thetap) };
|
||||
dest->AddPoint(p);
|
||||
} else if(thetan < thetap) {
|
||||
// This is an inside corner. We have two edges, Ep and En. Move
|
||||
// out from their intersection by radius, normal to En, and
|
||||
// then draw a line parallel to En. Move out from their
|
||||
// intersection by radius, normal to Ep, and then draw a second
|
||||
// line parallel to Ep. The point that we want to generate is
|
||||
// the intersection of these two lines--it removes as much
|
||||
// material as we can without removing any that we shouldn't.
|
||||
double px0, py0, pdx, pdy;
|
||||
double nx0, ny0, ndx, ndy;
|
||||
double x, y;
|
||||
|
||||
px0 = b.x - r*sin(thetap);
|
||||
py0 = b.y + r*cos(thetap);
|
||||
pdx = cos(thetap);
|
||||
pdy = sin(thetap);
|
||||
|
||||
nx0 = b.x - r*sin(thetan);
|
||||
ny0 = b.y + r*cos(thetan);
|
||||
ndx = cos(thetan);
|
||||
ndy = sin(thetan);
|
||||
|
||||
IntersectionOfLines(px0, py0, pdx, pdy,
|
||||
nx0, ny0, ndx, ndy,
|
||||
&x, &y);
|
||||
|
||||
dest->AddPoint(Vector::From(x, y, 0));
|
||||
} else {
|
||||
if(fabs(thetap - thetan) < (6*PI)/180) {
|
||||
Vector pp = { b.x - r*sin(thetap),
|
||||
b.y + r*cos(thetap), 0 };
|
||||
dest->AddPoint(pp);
|
||||
|
||||
Vector pn = { b.x - r*sin(thetan),
|
||||
b.y + r*cos(thetan), 0 };
|
||||
dest->AddPoint(pn);
|
||||
} else {
|
||||
double theta;
|
||||
for(theta = thetap; theta <= thetan; theta += (6*PI)/180) {
|
||||
Vector p = { b.x - r*sin(theta),
|
||||
b.y + r*cos(theta), 0 };
|
||||
dest->AddPoint(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
199
request.cpp
|
@ -1,199 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Implementation of our Request class; a request is a user-created thing
|
||||
// that will generate an entity (line, curve) when the sketch is generated,
|
||||
// in the same way that other entities are generated automatically, like
|
||||
// by an extrude or a step and repeat.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
const hRequest Request::HREQUEST_REFERENCE_XY = { 1 };
|
||||
const hRequest Request::HREQUEST_REFERENCE_YZ = { 2 };
|
||||
const hRequest Request::HREQUEST_REFERENCE_ZX = { 3 };
|
||||
|
||||
const EntReqTable::TableEntry EntReqTable::Table[] = {
|
||||
// request type entity type pts xtra? norml dist description
|
||||
{ Request::WORKPLANE, Entity::WORKPLANE, 1, false, true, false, "workplane" },
|
||||
{ Request::DATUM_POINT, 0, 1, false, false, false, "datum-point" },
|
||||
{ Request::LINE_SEGMENT, Entity::LINE_SEGMENT, 2, false, false, false, "line-segment" },
|
||||
{ Request::CUBIC, Entity::CUBIC, 4, true, false, false, "cubic-bezier" },
|
||||
{ Request::CUBIC_PERIODIC, Entity::CUBIC_PERIODIC, 3, true, false, false, "periodic-cubic" },
|
||||
{ Request::CIRCLE, Entity::CIRCLE, 1, false, true, true, "circle" },
|
||||
{ Request::ARC_OF_CIRCLE, Entity::ARC_OF_CIRCLE, 3, false, true, false, "arc-of-circle" },
|
||||
{ Request::TTF_TEXT, Entity::TTF_TEXT, 2, false, true, false, "ttf-text" },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
char *EntReqTable::DescriptionForRequest(int req) {
|
||||
for(int i = 0; Table[i].reqType; i++) {
|
||||
if(req == Table[i].reqType) {
|
||||
return Table[i].description;
|
||||
}
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
void EntReqTable::CopyEntityInfo(const TableEntry *te, int extraPoints,
|
||||
int *ent, int *req, int *pts, bool *hasNormal, bool *hasDistance)
|
||||
{
|
||||
int points = te->points;
|
||||
if(te->useExtraPoints) points += extraPoints;
|
||||
|
||||
if(ent) *ent = te->entType;
|
||||
if(req) *req = te->reqType;
|
||||
if(pts) *pts = points;
|
||||
if(hasNormal) *hasNormal = te->hasNormal;
|
||||
if(hasDistance) *hasDistance = te->hasDistance;
|
||||
}
|
||||
|
||||
bool EntReqTable::GetRequestInfo(int req, int extraPoints,
|
||||
int *ent, int *pts, bool *hasNormal, bool *hasDistance)
|
||||
{
|
||||
for(int i = 0; Table[i].reqType; i++) {
|
||||
const TableEntry *te = &(Table[i]);
|
||||
if(req == te->reqType) {
|
||||
CopyEntityInfo(te, extraPoints,
|
||||
ent, NULL, pts, hasNormal, hasDistance);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntReqTable::GetEntityInfo(int ent, int extraPoints,
|
||||
int *req, int *pts, bool *hasNormal, bool *hasDistance)
|
||||
{
|
||||
for(int i = 0; Table[i].reqType; i++) {
|
||||
const TableEntry *te = &(Table[i]);
|
||||
if(ent == te->entType) {
|
||||
CopyEntityInfo(te, extraPoints,
|
||||
NULL, req, pts, hasNormal, hasDistance);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int EntReqTable::GetRequestForEntity(int ent) {
|
||||
int req;
|
||||
GetEntityInfo(ent, 0, &req, NULL, NULL, NULL);
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
void Request::Generate(IdList<Entity,hEntity> *entity,
|
||||
IdList<Param,hParam> *param)
|
||||
{
|
||||
int points = 0;
|
||||
int et = 0;
|
||||
bool hasNormal = false;
|
||||
bool hasDistance = false;
|
||||
int i;
|
||||
|
||||
Entity e;
|
||||
ZERO(&e);
|
||||
EntReqTable::GetRequestInfo(type, extraPoints,
|
||||
&et, &points, &hasNormal, &hasDistance);
|
||||
|
||||
// Generate the entity that's specific to this request.
|
||||
e.type = et;
|
||||
e.extraPoints = extraPoints;
|
||||
e.group = group;
|
||||
e.style = style;
|
||||
e.workplane = workplane;
|
||||
e.construction = construction;
|
||||
e.str.strcpy(str.str);
|
||||
e.font.strcpy(font.str);
|
||||
e.h = h.entity(0);
|
||||
|
||||
// And generate entities for the points
|
||||
for(i = 0; i < points; i++) {
|
||||
Entity p;
|
||||
memset(&p, 0, sizeof(p));
|
||||
p.workplane = workplane;
|
||||
// points start from entity 1, except for datum point case
|
||||
p.h = h.entity(i+(et ? 1 : 0));
|
||||
p.group = group;
|
||||
p.style = style;
|
||||
|
||||
if(workplane.v == Entity::FREE_IN_3D.v) {
|
||||
p.type = Entity::POINT_IN_3D;
|
||||
// params for x y z
|
||||
p.param[0] = AddParam(param, h.param(16 + 3*i + 0));
|
||||
p.param[1] = AddParam(param, h.param(16 + 3*i + 1));
|
||||
p.param[2] = AddParam(param, h.param(16 + 3*i + 2));
|
||||
} else {
|
||||
p.type = Entity::POINT_IN_2D;
|
||||
// params for u v
|
||||
p.param[0] = AddParam(param, h.param(16 + 3*i + 0));
|
||||
p.param[1] = AddParam(param, h.param(16 + 3*i + 1));
|
||||
}
|
||||
entity->Add(&p);
|
||||
e.point[i] = p.h;
|
||||
}
|
||||
if(hasNormal) {
|
||||
Entity n;
|
||||
memset(&n, 0, sizeof(n));
|
||||
n.workplane = workplane;
|
||||
n.h = h.entity(32);
|
||||
n.group = group;
|
||||
n.style = style;
|
||||
if(workplane.v == Entity::FREE_IN_3D.v) {
|
||||
n.type = Entity::NORMAL_IN_3D;
|
||||
n.param[0] = AddParam(param, h.param(32+0));
|
||||
n.param[1] = AddParam(param, h.param(32+1));
|
||||
n.param[2] = AddParam(param, h.param(32+2));
|
||||
n.param[3] = AddParam(param, h.param(32+3));
|
||||
} else {
|
||||
n.type = Entity::NORMAL_IN_2D;
|
||||
// and this is just a copy of the workplane quaternion,
|
||||
// so no params required
|
||||
}
|
||||
if(points < 1) oops();
|
||||
// The point determines where the normal gets displayed on-screen;
|
||||
// it's entirely cosmetic.
|
||||
n.point[0] = e.point[0];
|
||||
entity->Add(&n);
|
||||
e.normal = n.h;
|
||||
}
|
||||
if(hasDistance) {
|
||||
Entity d;
|
||||
memset(&d, 0, sizeof(d));
|
||||
d.workplane = workplane;
|
||||
d.h = h.entity(64);
|
||||
d.group = group;
|
||||
d.style = style;
|
||||
d.type = Entity::DISTANCE;
|
||||
d.param[0] = AddParam(param, h.param(64));
|
||||
entity->Add(&d);
|
||||
e.distance = d.h;
|
||||
}
|
||||
|
||||
if(et) entity->Add(&e);
|
||||
}
|
||||
|
||||
char *Request::DescriptionString(void) {
|
||||
char *s;
|
||||
if(h.v == Request::HREQUEST_REFERENCE_XY.v) {
|
||||
s = "#XY";
|
||||
} else if(h.v == Request::HREQUEST_REFERENCE_YZ.v) {
|
||||
s = "#YZ";
|
||||
} else if(h.v == Request::HREQUEST_REFERENCE_ZX.v) {
|
||||
s = "#ZX";
|
||||
} else {
|
||||
s = EntReqTable::DescriptionForRequest(type);
|
||||
}
|
||||
static char ret[100];
|
||||
sprintf(ret, "r%03x-%s", h.v, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hParam Request::AddParam(IdList<Param,hParam> *param, hParam hp) {
|
||||
Param pa;
|
||||
memset(&pa, 0, sizeof(pa));
|
||||
pa.h = hp;
|
||||
param->Add(&pa);
|
||||
return hp;
|
||||
}
|
||||
|
736
solvespace.cpp
|
@ -1,736 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Entry point in to the program, our registry-stored settings and top-level
|
||||
// housekeeping when we open, save, and create new files.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
|
||||
SolveSpace SS;
|
||||
Sketch SK;
|
||||
|
||||
void SolveSpace::Init(char *cmdLine) {
|
||||
SS.tangentArcRadius = 10.0;
|
||||
|
||||
// Then, load the registry settings.
|
||||
int i;
|
||||
// Default list of colors for the model material
|
||||
modelColor[0] = CnfThawDWORD(RGB(150, 150, 150), "ModelColor_0");
|
||||
modelColor[1] = CnfThawDWORD(RGB(100, 100, 100), "ModelColor_1");
|
||||
modelColor[2] = CnfThawDWORD(RGB( 30, 30, 30), "ModelColor_2");
|
||||
modelColor[3] = CnfThawDWORD(RGB(150, 0, 0), "ModelColor_3");
|
||||
modelColor[4] = CnfThawDWORD(RGB( 0, 100, 0), "ModelColor_4");
|
||||
modelColor[5] = CnfThawDWORD(RGB( 0, 80, 80), "ModelColor_5");
|
||||
modelColor[6] = CnfThawDWORD(RGB( 0, 0, 130), "ModelColor_6");
|
||||
modelColor[7] = CnfThawDWORD(RGB( 80, 0, 80), "ModelColor_7");
|
||||
// Light intensities
|
||||
lightIntensity[0] = CnfThawFloat(1.0f, "LightIntensity_0");
|
||||
lightIntensity[1] = CnfThawFloat(0.5f, "LightIntensity_1");
|
||||
ambientIntensity = 0.3; // no setting for that yet
|
||||
// Light positions
|
||||
lightDir[0].x = CnfThawFloat(-1.0f, "LightDir_0_Right" );
|
||||
lightDir[0].y = CnfThawFloat( 1.0f, "LightDir_0_Up" );
|
||||
lightDir[0].z = CnfThawFloat( 0.0f, "LightDir_0_Forward" );
|
||||
lightDir[1].x = CnfThawFloat( 1.0f, "LightDir_1_Right" );
|
||||
lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up" );
|
||||
lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward" );
|
||||
// Chord tolerance
|
||||
chordTol = CnfThawFloat(2.0f, "ChordTolerance");
|
||||
// Max pwl segments to generate
|
||||
maxSegments = CnfThawDWORD(10, "MaxSegments");
|
||||
// View units
|
||||
viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits");
|
||||
// Number of digits after the decimal point
|
||||
afterDecimalMm = CnfThawDWORD(2, "AfterDecimalMm");
|
||||
afterDecimalInch = CnfThawDWORD(3, "AfterDecimalInch");
|
||||
// Camera tangent (determines perspective)
|
||||
cameraTangent = CnfThawFloat(0.3f/1e3f, "CameraTangent");
|
||||
// Grid spacing
|
||||
gridSpacing = CnfThawFloat(5.0f, "GridSpacing");
|
||||
// Export scale factor
|
||||
exportScale = CnfThawFloat(1.0f, "ExportScale");
|
||||
// Export offset (cutter radius comp)
|
||||
exportOffset = CnfThawFloat(0.0f, "ExportOffset");
|
||||
// Rewrite exported colors close to white into black (assuming white bg)
|
||||
fixExportColors = CnfThawDWORD(1, "FixExportColors");
|
||||
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
|
||||
drawBackFaces = CnfThawDWORD(1, "DrawBackFaces");
|
||||
// Check that contours are closed and not self-intersecting
|
||||
checkClosedContour = CnfThawDWORD(1, "CheckClosedContour");
|
||||
// Export shaded triangles in a 2d view
|
||||
exportShadedTriangles = CnfThawDWORD(1, "ExportShadedTriangles");
|
||||
// Export pwl curves (instead of exact) always
|
||||
exportPwlCurves = CnfThawDWORD(0, "ExportPwlCurves");
|
||||
// Background color on-screen
|
||||
backgroundColor = CnfThawDWORD(RGB(0, 0, 0), "BackgroundColor");
|
||||
// Whether export canvas size is fixed or derived from bbox
|
||||
exportCanvasSizeAuto = CnfThawDWORD(1, "ExportCanvasSizeAuto");
|
||||
// Margins for automatic canvas size
|
||||
exportMargin.left = CnfThawFloat(5.0f, "ExportMargin_Left");
|
||||
exportMargin.right = CnfThawFloat(5.0f, "ExportMargin_Right");
|
||||
exportMargin.bottom = CnfThawFloat(5.0f, "ExportMargin_Bottom");
|
||||
exportMargin.top = CnfThawFloat(5.0f, "ExportMargin_Top");
|
||||
// Dimensions for fixed canvas size
|
||||
exportCanvas.width = CnfThawFloat(100.0f, "ExportCanvas_Width");
|
||||
exportCanvas.height = CnfThawFloat(100.0f, "ExportCanvas_Height");
|
||||
exportCanvas.dx = CnfThawFloat( 5.0f, "ExportCanvas_Dx");
|
||||
exportCanvas.dy = CnfThawFloat( 5.0f, "ExportCanvas_Dy");
|
||||
// Extra parameters when exporting G code
|
||||
gCode.depth = CnfThawFloat(10.0f, "GCode_Depth");
|
||||
gCode.passes = CnfThawDWORD(1, "GCode_Passes");
|
||||
gCode.feed = CnfThawFloat(10.0f, "GCode_Feed");
|
||||
gCode.plungeFeed = CnfThawFloat(10.0f, "GCode_PlungeFeed");
|
||||
// Show toolbar in the graphics window
|
||||
showToolbar = CnfThawDWORD(1, "ShowToolbar");
|
||||
// Recent files menus
|
||||
for(i = 0; i < MAX_RECENT; i++) {
|
||||
char name[100];
|
||||
sprintf(name, "RecentFile_%d", i);
|
||||
strcpy(RecentFile[i], "");
|
||||
CnfThawString(RecentFile[i], MAX_PATH, name);
|
||||
}
|
||||
RefreshRecentMenus();
|
||||
|
||||
// The default styles (colors, line widths, etc.) are also stored in the
|
||||
// configuration file, but we will automatically load those as we need
|
||||
// them.
|
||||
|
||||
// Start with either an empty file, or the file specified on the
|
||||
// command line.
|
||||
NewFile();
|
||||
AfterNewFile();
|
||||
if(strlen(cmdLine) != 0) {
|
||||
if(LoadFromFile(cmdLine)) {
|
||||
strcpy(saveFile, cmdLine);
|
||||
} else {
|
||||
NewFile();
|
||||
}
|
||||
}
|
||||
AfterNewFile();
|
||||
}
|
||||
|
||||
void SolveSpace::Exit(void) {
|
||||
int i;
|
||||
char name[100];
|
||||
// Recent files
|
||||
for(i = 0; i < MAX_RECENT; i++) {
|
||||
sprintf(name, "RecentFile_%d", i);
|
||||
CnfFreezeString(RecentFile[i], name);
|
||||
}
|
||||
// Model colors
|
||||
for(i = 0; i < MODEL_COLORS; i++) {
|
||||
sprintf(name, "ModelColor_%d", i);
|
||||
CnfFreezeDWORD(modelColor[i], name);
|
||||
}
|
||||
// Light intensities
|
||||
CnfFreezeFloat((float)lightIntensity[0], "LightIntensity_0");
|
||||
CnfFreezeFloat((float)lightIntensity[1], "LightIntensity_1");
|
||||
// Light directions
|
||||
CnfFreezeFloat((float)lightDir[0].x, "LightDir_0_Right");
|
||||
CnfFreezeFloat((float)lightDir[0].y, "LightDir_0_Up");
|
||||
CnfFreezeFloat((float)lightDir[0].z, "LightDir_0_Forward");
|
||||
CnfFreezeFloat((float)lightDir[1].x, "LightDir_1_Right");
|
||||
CnfFreezeFloat((float)lightDir[1].y, "LightDir_1_Up");
|
||||
CnfFreezeFloat((float)lightDir[1].z, "LightDir_1_Forward");
|
||||
// Chord tolerance
|
||||
CnfFreezeFloat((float)chordTol, "ChordTolerance");
|
||||
// Max pwl segments to generate
|
||||
CnfFreezeDWORD((DWORD)maxSegments, "MaxSegments");
|
||||
// View units
|
||||
CnfFreezeDWORD((DWORD)viewUnits, "ViewUnits");
|
||||
// Number of digits after the decimal point
|
||||
CnfFreezeDWORD((DWORD)afterDecimalMm, "AfterDecimalMm");
|
||||
CnfFreezeDWORD((DWORD)afterDecimalInch, "AfterDecimalInch");
|
||||
// Camera tangent (determines perspective)
|
||||
CnfFreezeFloat((float)cameraTangent, "CameraTangent");
|
||||
// Grid spacing
|
||||
CnfFreezeFloat(gridSpacing, "GridSpacing");
|
||||
// Export scale (a float, stored as a DWORD)
|
||||
CnfFreezeFloat(exportScale, "ExportScale");
|
||||
// Export offset (cutter radius comp)
|
||||
CnfFreezeFloat(exportOffset, "ExportOffset");
|
||||
// Rewrite exported colors close to white into black (assuming white bg)
|
||||
CnfFreezeDWORD(fixExportColors, "FixExportColors");
|
||||
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
|
||||
CnfFreezeDWORD(drawBackFaces, "DrawBackFaces");
|
||||
// Check that contours are closed and not self-intersecting
|
||||
CnfFreezeDWORD(checkClosedContour, "CheckClosedContour");
|
||||
// Export shaded triangles in a 2d view
|
||||
CnfFreezeDWORD(exportShadedTriangles, "ExportShadedTriangles");
|
||||
// Export pwl curves (instead of exact) always
|
||||
CnfFreezeDWORD(exportPwlCurves, "ExportPwlCurves");
|
||||
// Background color on-screen
|
||||
CnfFreezeDWORD(backgroundColor, "BackgroundColor");
|
||||
// Whether export canvas size is fixed or derived from bbox
|
||||
CnfFreezeDWORD(exportCanvasSizeAuto, "ExportCanvasSizeAuto");
|
||||
// Margins for automatic canvas size
|
||||
CnfFreezeFloat(exportMargin.left, "ExportMargin_Left");
|
||||
CnfFreezeFloat(exportMargin.right, "ExportMargin_Right");
|
||||
CnfFreezeFloat(exportMargin.bottom, "ExportMargin_Bottom");
|
||||
CnfFreezeFloat(exportMargin.top, "ExportMargin_Top");
|
||||
// Dimensions for fixed canvas size
|
||||
CnfFreezeFloat(exportCanvas.width, "ExportCanvas_Width");
|
||||
CnfFreezeFloat(exportCanvas.height, "ExportCanvas_Height");
|
||||
CnfFreezeFloat(exportCanvas.dx, "ExportCanvas_Dx");
|
||||
CnfFreezeFloat(exportCanvas.dy, "ExportCanvas_Dy");
|
||||
// Extra parameters when exporting G code
|
||||
CnfFreezeFloat(gCode.depth, "GCode_Depth");
|
||||
CnfFreezeDWORD(gCode.passes, "GCode_Passes");
|
||||
CnfFreezeFloat(gCode.feed, "GCode_Feed");
|
||||
CnfFreezeFloat(gCode.plungeFeed, "GCode_PlungeFeed");
|
||||
// Show toolbar in the graphics window
|
||||
CnfFreezeDWORD(showToolbar, "ShowToolbar");
|
||||
|
||||
// And the default styles, colors and line widths and such.
|
||||
Style::FreezeDefaultStyles();
|
||||
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
void SolveSpace::DoLater(void) {
|
||||
if(later.generateAll) GenerateAll();
|
||||
if(later.showTW) TW.Show();
|
||||
ZERO(&later);
|
||||
}
|
||||
|
||||
double SolveSpace::MmPerUnit(void) {
|
||||
if(viewUnits == UNIT_INCHES) {
|
||||
return 25.4;
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
char *SolveSpace::UnitName(void) {
|
||||
if(viewUnits == UNIT_INCHES) {
|
||||
return "inch";
|
||||
} else {
|
||||
return "mm";
|
||||
}
|
||||
}
|
||||
char *SolveSpace::MmToString(double v) {
|
||||
static int WhichBuf;
|
||||
static char Bufs[8][128];
|
||||
char fmt[128];
|
||||
|
||||
WhichBuf++;
|
||||
if(WhichBuf >= 8 || WhichBuf < 0) WhichBuf = 0;
|
||||
|
||||
char *s = Bufs[WhichBuf];
|
||||
if(viewUnits == UNIT_INCHES) {
|
||||
sprintf(fmt, "%%.%df", afterDecimalInch);
|
||||
sprintf(s, fmt, v/25.4);
|
||||
} else {
|
||||
sprintf(fmt, "%%.%df", afterDecimalMm);
|
||||
sprintf(s, fmt, v);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
double SolveSpace::ExprToMm(Expr *e) {
|
||||
return (e->Eval()) * MmPerUnit();
|
||||
}
|
||||
double SolveSpace::StringToMm(char *str) {
|
||||
return atof(str) * MmPerUnit();
|
||||
}
|
||||
double SolveSpace::ChordTolMm(void) {
|
||||
return SS.chordTol / SS.GW.scale;
|
||||
}
|
||||
int SolveSpace::UnitDigitsAfterDecimal(void) {
|
||||
return (viewUnits == UNIT_INCHES) ? afterDecimalInch : afterDecimalMm;
|
||||
}
|
||||
void SolveSpace::SetUnitDigitsAfterDecimal(int v) {
|
||||
if(viewUnits == UNIT_INCHES) {
|
||||
afterDecimalInch = v;
|
||||
} else {
|
||||
afterDecimalMm = v;
|
||||
}
|
||||
}
|
||||
|
||||
double SolveSpace::CameraTangent(void) {
|
||||
if(!usePerspectiveProj) {
|
||||
return 0;
|
||||
} else {
|
||||
return cameraTangent;
|
||||
}
|
||||
}
|
||||
|
||||
void SolveSpace::AfterNewFile(void) {
|
||||
// Clear out the traced point, which is no longer valid
|
||||
traced.point = Entity::NO_ENTITY;
|
||||
traced.path.l.Clear();
|
||||
// and the naked edges
|
||||
nakedEdges.Clear();
|
||||
|
||||
// GenerateAll() expects the view to be valid, because it uses that to
|
||||
// fill in default values for extrusion depths etc. (which won't matter
|
||||
// here, but just don't let it work on garbage)
|
||||
SS.GW.offset = Vector::From(0, 0, 0);
|
||||
SS.GW.projRight = Vector::From(1, 0, 0);
|
||||
SS.GW.projUp = Vector::From(0, 1, 0);
|
||||
|
||||
ReloadAllImported();
|
||||
GenerateAll(-1, -1);
|
||||
|
||||
TW.Init();
|
||||
GW.Init();
|
||||
|
||||
unsaved = false;
|
||||
|
||||
int w, h;
|
||||
GetGraphicsWindowSize(&w, &h);
|
||||
GW.width = w;
|
||||
GW.height = h;
|
||||
|
||||
// The triangles haven't been generated yet, but zoom to fit the entities
|
||||
// roughly in the window, since that sets the mesh tolerance. Consider
|
||||
// invisible entities, so we still get something reasonable if the only
|
||||
// thing visible is the not-yet-generated surfaces.
|
||||
GW.ZoomToFit(true);
|
||||
|
||||
GenerateAll(0, INT_MAX);
|
||||
later.showTW = true;
|
||||
// Then zoom to fit again, to fit the triangles
|
||||
GW.ZoomToFit(false);
|
||||
|
||||
// Create all the default styles; they'll get created on the fly anyways,
|
||||
// but can't hurt to do it now.
|
||||
Style::CreateAllDefaultStyles();
|
||||
|
||||
UpdateWindowTitle();
|
||||
}
|
||||
|
||||
void SolveSpace::RemoveFromRecentList(char *file) {
|
||||
int src, dest;
|
||||
dest = 0;
|
||||
for(src = 0; src < MAX_RECENT; src++) {
|
||||
if(strcmp(file, RecentFile[src]) != 0) {
|
||||
if(src != dest) strcpy(RecentFile[dest], RecentFile[src]);
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
while(dest < MAX_RECENT) strcpy(RecentFile[dest++], "");
|
||||
RefreshRecentMenus();
|
||||
}
|
||||
void SolveSpace::AddToRecentList(char *file) {
|
||||
RemoveFromRecentList(file);
|
||||
|
||||
int src;
|
||||
for(src = MAX_RECENT - 2; src >= 0; src--) {
|
||||
strcpy(RecentFile[src+1], RecentFile[src]);
|
||||
}
|
||||
strcpy(RecentFile[0], file);
|
||||
RefreshRecentMenus();
|
||||
}
|
||||
|
||||
bool SolveSpace::GetFilenameAndSave(bool saveAs) {
|
||||
|
||||
char newFile[MAX_PATH];
|
||||
strcpy(newFile, saveFile);
|
||||
if(saveAs || strlen(newFile)==0) {
|
||||
if(!GetSaveFile(newFile, SLVS_EXT, SLVS_PATTERN)) return false;
|
||||
}
|
||||
|
||||
if(SaveToFile(newFile)) {
|
||||
AddToRecentList(newFile);
|
||||
strcpy(saveFile, newFile);
|
||||
unsaved = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SolveSpace::OkayToStartNewFile(void) {
|
||||
if(!unsaved) return true;
|
||||
|
||||
switch(SaveFileYesNoCancel()) {
|
||||
case IDYES:
|
||||
return GetFilenameAndSave(false);
|
||||
|
||||
case IDNO:
|
||||
return true;
|
||||
|
||||
case IDCANCEL:
|
||||
return false;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
void SolveSpace::UpdateWindowTitle(void) {
|
||||
if(strlen(saveFile) == 0) {
|
||||
SetWindowTitle("SolveSpace - (not yet saved)");
|
||||
} else {
|
||||
char buf[MAX_PATH+100];
|
||||
sprintf(buf, "SolveSpace - %s", saveFile);
|
||||
SetWindowTitle(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void SolveSpace::MenuFile(int id) {
|
||||
if(id >= RECENT_OPEN && id < (RECENT_OPEN+MAX_RECENT)) {
|
||||
if(!SS.OkayToStartNewFile()) return;
|
||||
|
||||
char newFile[MAX_PATH];
|
||||
strcpy(newFile, RecentFile[id-RECENT_OPEN]);
|
||||
RemoveFromRecentList(newFile);
|
||||
if(SS.LoadFromFile(newFile)) {
|
||||
strcpy(SS.saveFile, newFile);
|
||||
AddToRecentList(newFile);
|
||||
} else {
|
||||
strcpy(SS.saveFile, "");
|
||||
SS.NewFile();
|
||||
}
|
||||
SS.AfterNewFile();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(id) {
|
||||
case GraphicsWindow::MNU_NEW:
|
||||
if(!SS.OkayToStartNewFile()) break;
|
||||
|
||||
strcpy(SS.saveFile, "");
|
||||
SS.NewFile();
|
||||
SS.AfterNewFile();
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_OPEN: {
|
||||
if(!SS.OkayToStartNewFile()) break;
|
||||
|
||||
char newFile[MAX_PATH] = "";
|
||||
if(GetOpenFile(newFile, SLVS_EXT, SLVS_PATTERN)) {
|
||||
if(SS.LoadFromFile(newFile)) {
|
||||
strcpy(SS.saveFile, newFile);
|
||||
AddToRecentList(newFile);
|
||||
} else {
|
||||
strcpy(SS.saveFile, "");
|
||||
SS.NewFile();
|
||||
}
|
||||
SS.AfterNewFile();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_SAVE:
|
||||
SS.GetFilenameAndSave(false);
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_SAVE_AS:
|
||||
SS.GetFilenameAndSave(true);
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_EXPORT_PNG: {
|
||||
char exportFile[MAX_PATH] = "";
|
||||
if(!GetSaveFile(exportFile, PNG_EXT, PNG_PATTERN)) break;
|
||||
SS.ExportAsPngTo(exportFile);
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_EXPORT_VIEW: {
|
||||
char exportFile[MAX_PATH] = "";
|
||||
if(!GetSaveFile(exportFile, VEC_EXT, VEC_PATTERN)) break;
|
||||
|
||||
// If the user is exporting something where it would be
|
||||
// inappropriate to include the constraints, then warn.
|
||||
if(SS.GW.showConstraints &&
|
||||
(StringEndsIn(exportFile, ".txt") ||
|
||||
fabs(SS.exportOffset) > LENGTH_EPS))
|
||||
{
|
||||
Message("Constraints are currently shown, and will be exported "
|
||||
"in the toolpath. This is probably not what you want; "
|
||||
"hide them by clicking the link at the top of the "
|
||||
"text window.");
|
||||
}
|
||||
|
||||
SS.ExportViewOrWireframeTo(exportFile, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_EXPORT_WIREFRAME: {
|
||||
char exportFile[MAX_PATH] = "";
|
||||
if(!GetSaveFile(exportFile, V3D_EXT, V3D_PATTERN)) break;
|
||||
SS.ExportViewOrWireframeTo(exportFile, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_EXPORT_SECTION: {
|
||||
char exportFile[MAX_PATH] = "";
|
||||
if(!GetSaveFile(exportFile, VEC_EXT, VEC_PATTERN)) break;
|
||||
SS.ExportSectionTo(exportFile);
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_EXPORT_MESH: {
|
||||
char exportFile[MAX_PATH] = "";
|
||||
if(!GetSaveFile(exportFile, MESH_EXT, MESH_PATTERN)) break;
|
||||
SS.ExportMeshTo(exportFile);
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_EXPORT_SURFACES: {
|
||||
char exportFile[MAX_PATH] = "";
|
||||
if(!GetSaveFile(exportFile, SRF_EXT, SRF_PATTERN)) break;
|
||||
StepFileWriter sfw;
|
||||
ZERO(&sfw);
|
||||
sfw.ExportSurfacesTo(exportFile);
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_EXIT:
|
||||
if(!SS.OkayToStartNewFile()) break;
|
||||
SS.Exit();
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
|
||||
SS.UpdateWindowTitle();
|
||||
}
|
||||
|
||||
void SolveSpace::MenuAnalyze(int id) {
|
||||
SS.GW.GroupSelection();
|
||||
#define gs (SS.GW.gs)
|
||||
|
||||
switch(id) {
|
||||
case GraphicsWindow::MNU_STEP_DIM:
|
||||
if(gs.constraints == 1 && gs.n == 0) {
|
||||
Constraint *c = SK.GetConstraint(gs.constraint[0]);
|
||||
if(c->HasLabel() && !c->reference) {
|
||||
SS.TW.shown.dimFinish = c->valA;
|
||||
SS.TW.shown.dimSteps = 10;
|
||||
SS.TW.shown.dimIsDistance =
|
||||
(c->type != Constraint::ANGLE) &&
|
||||
(c->type != Constraint::LENGTH_RATIO);
|
||||
SS.TW.shown.constraint = c->h;
|
||||
SS.TW.shown.screen = TextWindow::SCREEN_STEP_DIMENSION;
|
||||
|
||||
// The step params are specified in the text window,
|
||||
// so force that to be shown.
|
||||
SS.GW.ForceTextWindowShown();
|
||||
|
||||
SS.later.showTW = true;
|
||||
SS.GW.ClearSelection();
|
||||
} else {
|
||||
Error("Constraint must have a label, and must not be "
|
||||
"a reference dimension.");
|
||||
}
|
||||
} else {
|
||||
Error("Bad selection for step dimension; select a constraint.");
|
||||
}
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_NAKED_EDGES: {
|
||||
SS.nakedEdges.Clear();
|
||||
|
||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
||||
SMesh *m = &(g->displayMesh);
|
||||
SKdNode *root = SKdNode::From(m);
|
||||
bool inters, leaks;
|
||||
root->MakeCertainEdgesInto(&(SS.nakedEdges),
|
||||
SKdNode::NAKED_OR_SELF_INTER_EDGES, true, &inters, &leaks);
|
||||
|
||||
InvalidateGraphics();
|
||||
|
||||
char *intersMsg = inters ?
|
||||
"The mesh is self-intersecting (NOT okay, invalid)." :
|
||||
"The mesh is not self-intersecting (okay, valid).";
|
||||
char *leaksMsg = leaks ?
|
||||
"The mesh has naked edges (NOT okay, invalid)." :
|
||||
"The mesh is watertight (okay, valid).";
|
||||
|
||||
char cntMsg[1024];
|
||||
sprintf(cntMsg, "\n\nThe model contains %d triangles, from "
|
||||
"%d surfaces.",
|
||||
g->displayMesh.l.n, g->runningShell.surface.n);
|
||||
|
||||
if(SS.nakedEdges.l.n == 0) {
|
||||
Message("%s\n\n%s\n\nZero problematic edges, good.%s",
|
||||
intersMsg, leaksMsg, cntMsg);
|
||||
} else {
|
||||
Error("%s\n\n%s\n\n%d problematic edges, bad.%s",
|
||||
intersMsg, leaksMsg, SS.nakedEdges.l.n, cntMsg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_INTERFERENCE: {
|
||||
SS.nakedEdges.Clear();
|
||||
|
||||
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
||||
SKdNode *root = SKdNode::From(m);
|
||||
bool inters, leaks;
|
||||
root->MakeCertainEdgesInto(&(SS.nakedEdges),
|
||||
SKdNode::SELF_INTER_EDGES, false, &inters, &leaks);
|
||||
|
||||
InvalidateGraphics();
|
||||
|
||||
if(inters) {
|
||||
Error("%d edges interfere with other triangles, bad.",
|
||||
SS.nakedEdges.l.n);
|
||||
} else {
|
||||
Message("The assembly does not interfere, good.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_VOLUME: {
|
||||
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
||||
|
||||
double vol = 0;
|
||||
int i;
|
||||
for(i = 0; i < m->l.n; i++) {
|
||||
STriangle tr = m->l.elem[i];
|
||||
|
||||
// Translate to place vertex A at (x, y, 0)
|
||||
Vector trans = Vector::From(tr.a.x, tr.a.y, 0);
|
||||
tr.a = (tr.a).Minus(trans);
|
||||
tr.b = (tr.b).Minus(trans);
|
||||
tr.c = (tr.c).Minus(trans);
|
||||
|
||||
// Rotate to place vertex B on the y-axis. Depending on
|
||||
// whether the triangle is CW or CCW, C is either to the
|
||||
// right or to the left of the y-axis. This handles the
|
||||
// sign of our normal.
|
||||
Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
|
||||
u = u.WithMagnitude(1);
|
||||
Vector v = Vector::From(tr.b.x, tr.b.y, 0);
|
||||
v = v.WithMagnitude(1);
|
||||
Vector n = Vector::From(0, 0, 1);
|
||||
|
||||
tr.a = (tr.a).DotInToCsys(u, v, n);
|
||||
tr.b = (tr.b).DotInToCsys(u, v, n);
|
||||
tr.c = (tr.c).DotInToCsys(u, v, n);
|
||||
|
||||
n = tr.Normal().WithMagnitude(1);
|
||||
|
||||
// Triangles on edge don't contribute
|
||||
if(fabs(n.z) < LENGTH_EPS) continue;
|
||||
|
||||
// The plane has equation p dot n = a dot n
|
||||
double d = (tr.a).Dot(n);
|
||||
// nx*x + ny*y + nz*z = d
|
||||
// nz*z = d - nx*x - ny*y
|
||||
double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;
|
||||
|
||||
double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
|
||||
double xc = tr.c.x, yb = tr.b.y;
|
||||
|
||||
// I asked Maple for
|
||||
// int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
|
||||
double integral =
|
||||
(1.0/3)*(
|
||||
A*(mbc-mac)+
|
||||
(1.0/2)*B*(mbc*mbc-mac*mac)
|
||||
)*(xc*xc*xc)+
|
||||
(1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
|
||||
C*yb*xc+
|
||||
(1.0/2)*B*yb*yb*xc;
|
||||
|
||||
vol += integral;
|
||||
}
|
||||
|
||||
char msg[1024];
|
||||
sprintf(msg, "The volume of the solid model is:\n\n"
|
||||
" %.3f %s^3",
|
||||
vol / pow(SS.MmPerUnit(), 3),
|
||||
SS.UnitName());
|
||||
|
||||
if(SS.viewUnits == SolveSpace::UNIT_MM) {
|
||||
sprintf(msg+strlen(msg), "\n %.2f mL", vol/(10*10*10));
|
||||
}
|
||||
strcpy(msg+strlen(msg),
|
||||
"\n\nCurved surfaces have been approximated as triangles.\n"
|
||||
"This introduces error, typically of around 1%.");
|
||||
Message("%s", msg);
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_AREA: {
|
||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
||||
if(g->polyError.how != Group::POLY_GOOD) {
|
||||
Error("This group does not contain a correctly-formed "
|
||||
"2d closed area. It is open, not coplanar, or self-"
|
||||
"intersecting.");
|
||||
break;
|
||||
}
|
||||
SEdgeList sel;
|
||||
ZERO(&sel);
|
||||
g->polyLoops.MakeEdgesInto(&sel);
|
||||
SPolygon sp;
|
||||
ZERO(&sp);
|
||||
sel.AssemblePolygon(&sp, NULL, true);
|
||||
sp.normal = sp.ComputeNormal();
|
||||
sp.FixContourDirections();
|
||||
double area = sp.SignedArea();
|
||||
double scale = SS.MmPerUnit();
|
||||
Message("The area of the region sketched in this group is:\n\n"
|
||||
" %.3f %s^2\n\n"
|
||||
"Curves have been approximated as piecewise linear.\n"
|
||||
"This introduces error, typically of around 1%%.",
|
||||
area / (scale*scale),
|
||||
SS.UnitName());
|
||||
sel.Clear();
|
||||
sp.Clear();
|
||||
break;
|
||||
}
|
||||
|
||||
case GraphicsWindow::MNU_SHOW_DOF:
|
||||
// This works like a normal solve, except that it calculates
|
||||
// which variables are free/bound at the same time.
|
||||
SS.GenerateAll(0, INT_MAX, true);
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_TRACE_PT:
|
||||
if(gs.points == 1 && gs.n == 1) {
|
||||
SS.traced.point = gs.point[0];
|
||||
SS.GW.ClearSelection();
|
||||
} else {
|
||||
Error("Bad selection for trace; select a single point.");
|
||||
}
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_STOP_TRACING: {
|
||||
char exportFile[MAX_PATH] = "";
|
||||
if(GetSaveFile(exportFile, CSV_EXT, CSV_PATTERN)) {
|
||||
FILE *f = fopen(exportFile, "wb");
|
||||
if(f) {
|
||||
int i;
|
||||
SContour *sc = &(SS.traced.path);
|
||||
for(i = 0; i < sc->l.n; i++) {
|
||||
Vector p = sc->l.elem[i].p;
|
||||
double s = SS.exportScale;
|
||||
fprintf(f, "%.10f, %.10f, %.10f\r\n",
|
||||
p.x/s, p.y/s, p.z/s);
|
||||
}
|
||||
fclose(f);
|
||||
} else {
|
||||
Error("Couldn't write to '%s'", exportFile);
|
||||
}
|
||||
}
|
||||
// Clear the trace, and stop tracing
|
||||
SS.traced.point = Entity::NO_ENTITY;
|
||||
SS.traced.path.l.Clear();
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
}
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
void SolveSpace::MenuHelp(int id) {
|
||||
switch(id) {
|
||||
case GraphicsWindow::MNU_WEBSITE:
|
||||
OpenWebsite("http://solvespace.com/helpmenu");
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_ABOUT:
|
||||
Message("This is SolveSpace version 2.0.\n\n"
|
||||
"For more information, see http://solvespace.com/\n\n"
|
||||
"Built " __TIME__ " " __DATE__ ".\n\n"
|
||||
"Copyright 2008-2013 Jonathan Westhues.\n"
|
||||
"All Rights Reserved.");
|
||||
break;
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
37
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,37 @@
|
|||
# platform utilities
|
||||
|
||||
if(WIN32)
|
||||
set(util_SOURCES
|
||||
platform/w32util.cpp)
|
||||
else()
|
||||
set(util_SOURCES
|
||||
platform/unixutil.cpp)
|
||||
endif()
|
||||
|
||||
# libslvs
|
||||
|
||||
set(libslvs_SOURCES
|
||||
util.cpp
|
||||
entity.cpp
|
||||
expr.cpp
|
||||
constraint.cpp
|
||||
constrainteq.cpp
|
||||
system.cpp)
|
||||
|
||||
set(libslvs_HEADERS
|
||||
solvespace.h)
|
||||
|
||||
add_library(slvs SHARED
|
||||
${libslvs_SOURCES}
|
||||
${libslvs_HEADERS}
|
||||
${util_SOURCES}
|
||||
lib.cpp)
|
||||
|
||||
target_compile_definitions(slvs
|
||||
PRIVATE -DLIBRARY)
|
||||
|
||||
target_include_directories(slvs
|
||||
PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
set_target_properties(slvs PROPERTIES
|
||||
PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/include/slvs.h)
|
|
@ -9,7 +9,7 @@
|
|||
char *Constraint::DescriptionString(void) {
|
||||
static char ret[1024];
|
||||
|
||||
char *s;
|
||||
const char *s;
|
||||
switch(type) {
|
||||
case POINTS_COINCIDENT: s = "pts-coincident"; break;
|
||||
case PT_PT_DISTANCE: s = "pt-pt-distance"; break;
|
||||
|
@ -52,6 +52,8 @@ char *Constraint::DescriptionString(void) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifndef LIBRARY
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Delete all constraints with the specified type, entityA, ptA. We use this
|
||||
// when auto-removing constraints that would become redundant.
|
||||
|
@ -349,7 +351,7 @@ void Constraint::MenuConstrain(int id) {
|
|||
c.entityA = gs.entity[0];
|
||||
c.ptA = gs.point[0];
|
||||
c.ptB = gs.point[1];
|
||||
} else if(gs.lineSegments == 1 &&
|
||||
} else if(gs.lineSegments == 1 &&
|
||||
((gs.workplanes == 1 && gs.n == 2) ||
|
||||
(gs.n == 1)))
|
||||
{
|
||||
|
@ -490,7 +492,7 @@ void Constraint::MenuConstrain(int id) {
|
|||
// normal vector; allow that, since the numerical
|
||||
// constraint does
|
||||
SWAP(Vector, ru, rv);
|
||||
}
|
||||
}
|
||||
fu = fu.Dot(ru) > 0 ? ru : ru.ScaledBy(-1);
|
||||
fv = fv.Dot(rv) > 0 ? rv : rv.ScaledBy(-1);
|
||||
|
||||
|
@ -548,7 +550,7 @@ void Constraint::MenuConstrain(int id) {
|
|||
return;
|
||||
}
|
||||
|
||||
Entity *ea = SK.GetEntity(c.entityA),
|
||||
Entity *ea = SK.GetEntity(c.entityA),
|
||||
*eb = SK.GetEntity(c.entityB);
|
||||
if(ea->type == Entity::LINE_SEGMENT &&
|
||||
eb->type == Entity::LINE_SEGMENT)
|
||||
|
@ -707,3 +709,5 @@ void Constraint::MenuConstrain(int id) {
|
|||
InvalidateGraphics();
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -389,7 +389,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
|
|||
// the line segment, and choose the longer of these.
|
||||
ExprVector eap = ea.Minus(ep);
|
||||
ExprVector ebp = eb.Minus(ep);
|
||||
ExprVector elp =
|
||||
ExprVector elp =
|
||||
(ebp.Magnitude()->Eval() > eap.Magnitude()->Eval()) ?
|
||||
ebp : eap;
|
||||
|
||||
|
@ -413,7 +413,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
|
|||
EntityBase *normal = SK.GetEntity(circle->normal);
|
||||
ExprVector u = normal->NormalExprsU(),
|
||||
v = normal->NormalExprsV();
|
||||
|
||||
|
||||
Expr *du = (center.Minus(pt)).Dot(u),
|
||||
*dv = (center.Minus(pt)).Dot(v);
|
||||
|
||||
|
@ -443,7 +443,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
|
|||
EntityBase *ln = SK.GetEntity(entityA);
|
||||
EntityBase *a = SK.GetEntity(ln->point[0]);
|
||||
EntityBase *b = SK.GetEntity(ln->point[1]);
|
||||
|
||||
|
||||
Expr *au, *av, *bu, *bv;
|
||||
a->PointGetExprsInWorkplane(workplane, &au, &av);
|
||||
b->PointGetExprsInWorkplane(workplane, &bu, &bv);
|
||||
|
@ -601,7 +601,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
|
|||
ExprVector bu = b->NormalExprsU(),
|
||||
bv = b->NormalExprsV(),
|
||||
bn = b->NormalExprsN();
|
||||
|
||||
|
||||
AddEq(l, VectorsParallel(0, an, bn), 0);
|
||||
AddEq(l, VectorsParallel(1, an, bn), 1);
|
||||
Expr *d1 = au.Dot(bv);
|
||||
|
@ -628,8 +628,15 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
|
|||
if(type == ANGLE) {
|
||||
// The direction cosine is equal to the cosine of the
|
||||
// specified angle
|
||||
Expr *rads = exA->Times(Expr::From(PI/180));
|
||||
AddEq(l, c->Minus(rads->Cos()), 0);
|
||||
Expr *rads = exA->Times(Expr::From(PI/180)),
|
||||
*rc = rads->Cos();
|
||||
double arc = fabs(rc->Eval());
|
||||
// avoid false detection of inconsistent systems by gaining
|
||||
// up as the difference in dot products gets small at small
|
||||
// angles; doubles still have plenty of precision, only
|
||||
// problem is that rank test
|
||||
Expr *mult = Expr::From(arc > 0.99 ? 0.01/(1.00001 - arc) : 1);
|
||||
AddEq(l, (c->Minus(rc))->Times(mult), 0);
|
||||
} else {
|
||||
// The dot product (and therefore the direction cosine)
|
||||
// is equal to zero, perpendicular.
|
||||
|
@ -662,7 +669,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
|
|||
EntityBase *line = SK.GetEntity(entityB);
|
||||
|
||||
ExprVector ac = SK.GetEntity(arc->point[0])->PointGetExprs();
|
||||
ExprVector ap =
|
||||
ExprVector ap =
|
||||
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetExprs();
|
||||
|
||||
ExprVector ld = line->VectorGetExprs();
|
||||
|
@ -675,7 +682,7 @@ void ConstraintBase::GenerateReal(IdList<Equation,hEquation> *l) {
|
|||
case CUBIC_LINE_TANGENT: {
|
||||
EntityBase *cubic = SK.GetEntity(entityA);
|
||||
EntityBase *line = SK.GetEntity(entityB);
|
||||
|
||||
|
||||
ExprVector a;
|
||||
if(other) {
|
||||
a = cubic->CubicGetFinishTangentExprs();
|
|
@ -7,9 +7,6 @@
|
|||
#ifndef __DSC_H
|
||||
#define __DSC_H
|
||||
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
class Vector;
|
||||
class Vector4;
|
||||
class Point2d;
|
||||
|
@ -33,7 +30,7 @@ public:
|
|||
Quaternion ScaledBy(double s);
|
||||
double Magnitude(void);
|
||||
Quaternion WithMagnitude(double s);
|
||||
|
||||
|
||||
// Call a rotation matrix [ u' v' n' ]'; this returns the first and
|
||||
// second rows, where that matrix is generated by this quaternion
|
||||
Vector RotationU(void);
|
||||
|
@ -50,7 +47,7 @@ public:
|
|||
class Vector {
|
||||
public:
|
||||
double x, y, z;
|
||||
|
||||
|
||||
static Vector From(double x, double y, double z);
|
||||
static Vector From(hParam x, hParam y, hParam z);
|
||||
static Vector AtIntersectionOfPlanes(Vector n1, double d1,
|
||||
|
@ -101,7 +98,7 @@ public:
|
|||
static bool BoundingBoxIntersectsLine(Vector amax, Vector amin,
|
||||
Vector p0, Vector p1, bool segment);
|
||||
bool OutsideAndNotOn(Vector maxv, Vector minv);
|
||||
Vector InPerspective(Vector u, Vector v, Vector n,
|
||||
Vector InPerspective(Vector u, Vector v, Vector n,
|
||||
Vector origin, double cameraTan);
|
||||
Point2d Project2d(Vector u, Vector v);
|
||||
Point2d ProjectXy(void);
|
||||
|
@ -111,7 +108,7 @@ public:
|
|||
class Vector4 {
|
||||
public:
|
||||
double w, x, y, z;
|
||||
|
||||
|
||||
static Vector4 From(double w, double x, double y, double z);
|
||||
static Vector4 From(double w, Vector v3);
|
||||
static Vector4 Blend(Vector4 a, Vector4 b, double t);
|
||||
|
@ -232,8 +229,8 @@ public:
|
|||
int n;
|
||||
int elemsAllocated;
|
||||
|
||||
DWORD MaximumId(void) {
|
||||
DWORD id = 0;
|
||||
uint32_t MaximumId(void) {
|
||||
uint32_t id = 0;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < n; i++) {
|
|
@ -280,7 +280,7 @@ void EntityBase::NormalForceTo(Quaternion q) {
|
|||
break;
|
||||
case NORMAL_N_ROT: {
|
||||
Quaternion qp = q.Times(numNormal.Inverse());
|
||||
|
||||
|
||||
SK.GetParam(param[0])->val = qp.w;
|
||||
SK.GetParam(param[1])->val = qp.vx;
|
||||
SK.GetParam(param[2])->val = qp.vy;
|
||||
|
@ -756,8 +756,8 @@ void EntityBase::GenerateEquations(IdList<Equation,hEquation> *l) {
|
|||
ConstraintBase *c = &(SK.constraint.elem[i]);
|
||||
if(c->group.v != group.v) continue;
|
||||
if(c->type != Constraint::POINTS_COINCIDENT) continue;
|
||||
|
||||
if((c->ptA.v == point[1].v && c->ptB.v == point[2].v) ||
|
||||
|
||||
if((c->ptA.v == point[1].v && c->ptB.v == point[2].v) ||
|
||||
(c->ptA.v == point[2].v && c->ptB.v == point[1].v))
|
||||
{
|
||||
break;
|