diff --git a/Makefile b/Makefile deleted file mode 100644 index 1a7e3fc..0000000 --- a/Makefile +++ /dev/null @@ -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 - diff --git a/bitmapextra.table b/bitmapextra.table deleted file mode 100644 index 338290d..0000000 --- a/bitmapextra.table +++ /dev/null @@ -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, - diff --git a/bitmapfont.table b/bitmapfont.table deleted file mode 100644 index d444070..0000000 --- a/bitmapfont.table +++ /dev/null @@ -1,2179 +0,0 @@ -static const BYTE FontTexture[256*16*16] = { - 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 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, 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, 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, - - 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, 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, 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, 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, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 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, 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, 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 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, 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, 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, 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, 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, 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, - - 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, 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, 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, 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, - 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, 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, 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, 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, - 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, 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, 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, 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, - 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, 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, 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, 0, 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, 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, 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, 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, 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, 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, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 0, 0, 255, 0, 0, 255, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, -255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 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, 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, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 255, 255, 255, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 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, 0, 0, 0, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 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, 255, 255, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 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, - 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, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 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, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 255, - 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 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, 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, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 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, 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, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 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, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 255, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 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, 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, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 255, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 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, 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, 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, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 255, 255, 0, 0, 255, - 0, 0, 0, 0, 0, 255, 255, 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, 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, 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, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 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, 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, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 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, 0, 0, 0, - - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, - 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, - 0, 255, 0, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, - 0, 255, 0, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 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, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 255, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 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, 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, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 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, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 0, 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, 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, - - 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, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 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, - 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 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, 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, 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, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 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, 0, 0, 0, 0, 0, 0, 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, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 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, - 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, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 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, 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, - - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 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, 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, 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, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, - 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 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, 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, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 255, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 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, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 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, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 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, 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, 0, 0, - - 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 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, 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, 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, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 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, 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, 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, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 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, 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, 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, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 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, 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, - - 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, 0, 255, 0, 255, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 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, 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, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 255, 0, - 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 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, 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, - 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 0, 0, 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, 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, - 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, 0, 0, 0, 255, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 0, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 0, 255, 255, 0, 0, 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, 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, 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, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 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, 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, 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, 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, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 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, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 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, 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, - 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, 255, 255, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 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, 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, - 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, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 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, 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, 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, 255, 255, 255, 255, 255, 255, 255, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 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, 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, 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, 0, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 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, 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, - 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 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, 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, 255, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 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, 255, 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, 0, 0, 0, 0, 0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, - 0, 0, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, - 0, 0, 0, 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, 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, - 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, - -#include "bitmapextra.table" -}; diff --git a/bsp.cpp b/bsp.cpp deleted file mode 100644 index b6e3d47..0000000 --- a/bsp.cpp +++ /dev/null @@ -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); -} - diff --git a/clipboard.cpp b/clipboard.cpp deleted file mode 100644 index 0888d0e..0000000 --- a/clipboard.cpp +++ /dev/null @@ -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 *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 *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); -} - diff --git a/confscreen.cpp b/confscreen.cpp deleted file mode 100644 index 433a12c..0000000 --- a/confscreen.cpp +++ /dev/null @@ -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; -} - diff --git a/describescreen.cpp b/describescreen.cpp deleted file mode 100644 index f09570e..0000000 --- a/describescreen.cpp +++ /dev/null @@ -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; -} - diff --git a/draw.cpp b/draw.cpp deleted file mode 100644 index c9f4e05..0000000 --- a/draw.cpp +++ /dev/null @@ -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(); - } -} - diff --git a/drawconstraint.cpp b/drawconstraint.cpp deleted file mode 100644 index 9d713bd..0000000 --- a/drawconstraint.cpp +++ /dev/null @@ -1,1101 +0,0 @@ -//----------------------------------------------------------------------------- -// Given a constraint, draw a graphical and user-selectable representation -// of that constraint on-screen. We can either draw with gl, or compute the -// distance from a point (the location of the mouse pointer) to the lines -// that we would have drawn, for selection. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -void Constraint::LineDrawOrGetDistance(Vector a, Vector b) { - if(dogd.drawing) { - // Draw comments in the specified style, but everything else in the - // default style for constraints. - hStyle hs; - if(type == COMMENT && disp.style.v) { - hs = disp.style; - } else { - hs.v = Style::CONSTRAINT; - } - - if(dogd.sel) { - dogd.sel->AddEdge(a, b, hs.v); - } else { - // The only constraints with styles should be comments, so don't - // check otherwise, save looking up the styles constantly. - if(type == COMMENT && Style::Width(hs) >= 3.0) { - glxFatLine(a, b, Style::Width(hs) / SS.GW.scale); - } else { - glBegin(GL_LINE_STRIP); - glxVertex3v(a); - glxVertex3v(b); - glEnd(); - } - } - } else { - Point2d ap = SS.GW.ProjectPoint(a); - Point2d bp = SS.GW.ProjectPoint(b); - - double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true); - dogd.dmin = min(dogd.dmin, d); - } - dogd.refp = (a.Plus(b)).ScaledBy(0.5); -} - -static void LineCallback(void *fndata, Vector a, Vector b) -{ - Constraint *c = (Constraint *)fndata; - c->LineDrawOrGetDistance(a, b); -} - -double Constraint::EllipticalInterpolation(double rx, double ry, double theta) { - double ex = rx*cos(theta); - double ey = ry*sin(theta); - double v = sqrt(ex*ex + ey*ey); - - return v; -} - -char *Constraint::Label(void) { - static char Ret[1024]; - if(type == ANGLE) { - sprintf(Ret, "%.2f", valA); - } else if(type == LENGTH_RATIO) { - sprintf(Ret, "%.3f:1", valA); - } else if(type == COMMENT) { - strcpy(Ret, comment.str); - } else if(type == DIAMETER) { - // leading spaces for diameter symbol - sprintf(Ret, " %s", SS.MmToString(valA)); - } else { - // valA has units of distance - strcpy(Ret, SS.MmToString(fabs(valA))); - } - if(reference) { - strcat(Ret, " REF"); - } - return Ret; -} - -void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) { - double th; - if(type == COMMENT) { - th = Style::TextHeight(disp.style); - } else { - th = DEFAULT_TEXT_HEIGHT; - } - - char *s = Label(); - double swidth = glxStrWidth(s, th), - sheight = glxStrHeight(th); - - // By default, the reference is from the center; but the style could - // specify otherwise if one is present, and it could also specify a - // rotation. - if(type == COMMENT && disp.style.v) { - Style *st = Style::Get(disp.style); - // rotation first - double rads = st->textAngle*PI/180; - double c = cos(rads), s = sin(rads); - Vector pr = gr, pu = gu; - gr = pr.ScaledBy( c).Plus(pu.ScaledBy(s)); - gu = pr.ScaledBy(-s).Plus(pu.ScaledBy(c)); - // then origin - int o = st->textOrigin; - if(o & Style::ORIGIN_LEFT) ref = ref.Plus(gr.WithMagnitude(swidth/2)); - if(o & Style::ORIGIN_RIGHT) ref = ref.Minus(gr.WithMagnitude(swidth/2)); - if(o & Style::ORIGIN_BOT) ref = ref.Plus(gu.WithMagnitude(sheight/2)); - if(o & Style::ORIGIN_TOP) ref = ref.Minus(gu.WithMagnitude(sheight/2)); - } - - if(labelPos) { - // labelPos is from the top left corner (for the text box used to - // edit things), but ref is from the center. - *labelPos = ref.Minus(gr.WithMagnitude(swidth/2)).Minus( - gu.WithMagnitude(sheight/2)); - } - - - if(dogd.drawing) { - glxWriteTextRefCenter(s, th, ref, gr, gu, LineCallback, this); - } else { - double l = swidth/2 - sheight/2; - l = max(l, 5/SS.GW.scale); - Point2d a = SS.GW.ProjectPoint(ref.Minus(gr.WithMagnitude(l))); - Point2d b = SS.GW.ProjectPoint(ref.Plus (gr.WithMagnitude(l))); - double d = dogd.mp.DistanceToLine(a, b.Minus(a), true); - - dogd.dmin = min(dogd.dmin, d - (th / 2)); - dogd.refp = ref; - } -} - -void Constraint::StippledLine(Vector a, Vector b) { - glLineStipple(4, 0x5555); - glEnable(GL_LINE_STIPPLE); - LineDrawOrGetDistance(a, b); - glDisable(GL_LINE_STIPPLE); -} - -void Constraint::DoProjectedPoint(Vector *r) { - Vector p = r->ProjectInto(workplane); - StippledLine(p, *r); - *r = p; -} - -//----------------------------------------------------------------------------- -// There is a rectangular box, aligned to our display axes (projRight, projUp) -// centered at ref. This is where a dimension label will be drawn. We want to -// draw a line from A to B. If that line would intersect the label box, then -// trim the line to leave a gap for it, and return zero. If not, then extend -// the line to almost meet the box, and return either positive or negative, -// depending whether that extension was from A or from B. -//----------------------------------------------------------------------------- -int Constraint::DoLineTrimmedAgainstBox(Vector ref, Vector a, Vector b) { - Vector gu = SS.GW.projUp.WithMagnitude(1), - gr = SS.GW.projRight.WithMagnitude(1); - - double pixels = 1.0 / SS.GW.scale; - char *s = Label(); - double swidth = glxStrWidth(s, DEFAULT_TEXT_HEIGHT) + 4*pixels, - sheight = glxStrHeight(DEFAULT_TEXT_HEIGHT) + 8*pixels; - - struct { - Vector n; - double d; - } planes[4]; - // reference pos is the center of box occupied by text; build a rectangle - // around that, aligned to axes gr and gu, from four planes will all four - // normals pointing inward - planes[0].n = gu.ScaledBy(-1); planes[0].d = -(gu.Dot(ref) + sheight/2); - planes[1].n = gu; planes[1].d = gu.Dot(ref) - sheight/2; - planes[2].n = gr; planes[2].d = gr.Dot(ref) - swidth/2; - planes[3].n = gr.ScaledBy(-1); planes[3].d = -(gr.Dot(ref) + swidth/2); - - double tmin = VERY_POSITIVE, tmax = VERY_NEGATIVE; - Vector dl = b.Minus(a); - - for(int i = 0; i < 4; i++) { - bool parallel; - Vector p = Vector::AtIntersectionOfPlaneAndLine( - planes[i].n, planes[i].d, - a, b, ¶llel); - if(parallel) continue; - - int j; - for(j = 0; j < 4; j++) { - double d = (planes[j].n).Dot(p) - planes[j].d; - if(d < -LENGTH_EPS) break; - } - if(j < 4) continue; - - double t = (p.Minus(a)).DivPivoting(dl); - tmin = min(t, tmin); - tmax = max(t, tmax); - } - - int within = 0; - - if(tmin > -0.01 && tmin < 1.01 && tmax > -0.01 && tmax < 1.01) { - // Both in range; so there's pieces of the line on both sides of the - // label box. - LineDrawOrGetDistance(a, a.Plus(dl.ScaledBy(tmin))); - LineDrawOrGetDistance(a.Plus(dl.ScaledBy(tmax)), b); - } else if(tmin > -0.01 && tmin < 1.01) { - // Only one intersection in range; so the box is right on top of the - // endpoint - LineDrawOrGetDistance(a, a.Plus(dl.ScaledBy(tmin))); - } else if(tmax > -0.01 && tmax < 1.01) { - // Likewise. - LineDrawOrGetDistance(a.Plus(dl.ScaledBy(tmax)), b); - } else { - // The line does not intersect the label; so the line should get - // extended to just barely meet the label. - if(tmin < 0.01 && tmax < 0.01) { - LineDrawOrGetDistance(a.Plus(dl.ScaledBy(tmax)), b); - within = 1; - } else if(tmin > 0.99 && tmax > 0.99) { - LineDrawOrGetDistance(a, a.Plus(dl.ScaledBy(tmin))); - within = -1; - } else { - // This will happen if the entire line lies within the box. - LineDrawOrGetDistance(a, b); - } - } - // 0 means the label lies within the line, negative means it's outside - // and closer to b, positive means outside and closer to a. - return within; -} - -//----------------------------------------------------------------------------- -// Draw a line with arrows on both ends, and possibly a gap in the middle for -// the dimension. We will use these for most length dimensions. The length -// being dimensioned is from A to B; but those points get extended perpendicular -// to the line AB, until the line between the extensions crosses ref (the -// center of the label). -//----------------------------------------------------------------------------- -void Constraint::DoLineWithArrows(Vector ref, Vector a, Vector b, - bool onlyOneExt) -{ - Vector gn = (SS.GW.projRight.Cross(SS.GW.projUp)).WithMagnitude(1); - double pixels = 1.0 / SS.GW.scale; - - Vector ab = a.Minus(b); - Vector ar = a.Minus(ref); - // Normal to a plane containing the line and the label origin. - Vector n = ab.Cross(ar); - // Within that plane, and normal to the line AB; so that's our extension - // line. - Vector out = ab.Cross(n).WithMagnitude(1); - out = out.ScaledBy(-out.Dot(ar)); - - Vector ae = a.Plus(out), be = b.Plus(out); - - // Extension lines extend 10 pixels beyond where the arrows get - // drawn (which is at the same offset perpendicular from AB as the - // label). - LineDrawOrGetDistance(a, ae.Plus(out.WithMagnitude(10*pixels))); - if(!onlyOneExt) { - LineDrawOrGetDistance(b, be.Plus(out.WithMagnitude(10*pixels))); - } - - int within = DoLineTrimmedAgainstBox(ref, ae, be); - - // Arrow heads are 13 pixels long, with an 18 degree half-angle. - double theta = 18*PI/180; - Vector arrow = (be.Minus(ae)).WithMagnitude(13*pixels); - - if(within != 0) { - arrow = arrow.ScaledBy(-1); - Vector seg = (be.Minus(ae)).WithMagnitude(18*pixels); - if(within < 0) LineDrawOrGetDistance(ae, ae.Minus(seg)); - if(within > 0) LineDrawOrGetDistance(be, be.Plus(seg)); - } - - LineDrawOrGetDistance(ae, ae.Plus(arrow.RotatedAbout(n, theta))); - LineDrawOrGetDistance(ae, ae.Plus(arrow.RotatedAbout(n, -theta))); - arrow = arrow.ScaledBy(-1); - LineDrawOrGetDistance(be, be.Plus(arrow.RotatedAbout(n, theta))); - LineDrawOrGetDistance(be, be.Plus(arrow.RotatedAbout(n, -theta))); -} - -void Constraint::DoEqualLenTicks(Vector a, Vector b, Vector gn) { - Vector m = (a.ScaledBy(1.0/3)).Plus(b.ScaledBy(2.0/3)); - Vector ab = a.Minus(b); - Vector n = (gn.Cross(ab)).WithMagnitude(10/SS.GW.scale); - - LineDrawOrGetDistance(m.Minus(n), m.Plus(n)); -} - -void Constraint::DoEqualRadiusTicks(hEntity he) { - Entity *circ = SK.GetEntity(he); - - Vector center = SK.GetEntity(circ->point[0])->PointGetNum(); - double r = circ->CircleGetRadiusNum(); - Quaternion q = circ->Normal()->NormalGetNum(); - Vector u = q.RotationU(), v = q.RotationV(); - - double theta; - if(circ->type == Entity::CIRCLE) { - theta = PI/2; - } else if(circ->type == Entity::ARC_OF_CIRCLE) { - double thetaa, thetab, dtheta; - circ->ArcGetAngles(&thetaa, &thetab, &dtheta); - theta = thetaa + dtheta/2; - } else oops(); - - Vector d = u.ScaledBy(cos(theta)).Plus(v.ScaledBy(sin(theta))); - d = d.ScaledBy(r); - Vector p = center.Plus(d); - Vector tick = d.WithMagnitude(10/SS.GW.scale); - LineDrawOrGetDistance(p.Plus(tick), p.Minus(tick)); -} - -void Constraint::DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db, - Vector offset, Vector *ref) -{ - Vector gr = SS.GW.projRight.ScaledBy(1/SS.GW.scale); - Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale); - - if(workplane.v != Entity::FREE_IN_3D.v) { - a0 = a0.ProjectInto(workplane); - b0 = b0.ProjectInto(workplane); - da = da.ProjectVectorInto(workplane); - db = db.ProjectVectorInto(workplane); - } - - bool skew; - Vector pi = Vector::AtIntersectionOfLines(a0, a0.Plus(da), - b0, b0.Plus(db), &skew); - - if(!skew) { - *ref = pi.Plus(offset); - // We draw in a coordinate system centered at the intersection point. - // One basis vector is da, and the other is normal to da and in - // the plane that contains our lines (so normal to its normal). - Vector dna = (da.Cross(db)).Cross(da); - da = da.WithMagnitude(1); dna = dna.WithMagnitude(1); - - Vector rm = (*ref).Minus(pi); - double rda = rm.Dot(da), rdna = rm.Dot(dna); - double r = sqrt(rda*rda + rdna*rdna); - double c = (da.Dot(db))/(da.Magnitude()*db.Magnitude()); - double thetaf = acos(c); - - Vector m = da.ScaledBy(cos(thetaf/2)).Plus( - dna.ScaledBy(sin(thetaf/2))); - if(m.Dot(rm) < 0) { - da = da.ScaledBy(-1); dna = dna.ScaledBy(-1); - } - - Vector prev = da.ScaledBy(r).Plus(pi); - int i, n = 30; - for(i = 0; i <= n; i++) { - double theta = (i*thetaf)/n; - Vector p = da. ScaledBy(r*cos(theta)).Plus( - dna.ScaledBy(r*sin(theta))).Plus(pi); - LineDrawOrGetDistance(prev, p); - prev = p; - } - - // The elliptical approximation isn't exactly right, but the correct - // calculation (against the bounding box of the text) would be rather - // complex and this looks pretty good. - double tl = atan2(rm.Dot(gu), rm.Dot(gr)); - double adj = EllipticalInterpolation( - glxStrWidth(Label(), DEFAULT_TEXT_HEIGHT)/2, - glxStrHeight(DEFAULT_TEXT_HEIGHT)/2, - tl); - *ref = (*ref).Plus(rm.WithMagnitude(adj + 3/SS.GW.scale)); - } else { - // The lines are skew; no wonderful way to illustrate that. - *ref = a0.Plus(b0); - *ref = (*ref).ScaledBy(0.5).Plus(disp.offset); - gu = gu.WithMagnitude(1); - Vector trans = - (*ref).Plus(gu.ScaledBy(-1.5*glxStrHeight(DEFAULT_TEXT_HEIGHT))); - glxWriteTextRefCenter("angle between skew lines", DEFAULT_TEXT_HEIGHT, - trans, gr, gu, LineCallback, this); - } -} - -void Constraint::DrawOrGetDistance(Vector *labelPos) { - if(!SS.GW.showConstraints) return; - Group *g = SK.GetGroup(group); - // If the group is hidden, then the constraints are hidden and not - // able to be selected. - if(!(g->visible)) return; - // And likewise if the group is not the active group; except for comments - // with an assigned style. - if(g->h.v != SS.GW.activeGroup.v && !(type == COMMENT && disp.style.v)) { - return; - } - if(disp.style.v) { - Style *s = Style::Get(disp.style); - if(!s->visible) return; - } - - // Unit vectors that describe our current view of the scene. One pixel - // long, not one actual unit. - Vector gr = SS.GW.projRight.ScaledBy(1/SS.GW.scale); - Vector gu = SS.GW.projUp.ScaledBy(1/SS.GW.scale); - Vector gn = (gr.Cross(gu)).WithMagnitude(1/SS.GW.scale); - - switch(type) { - case PT_PT_DISTANCE: { - Vector ap = SK.GetEntity(ptA)->PointGetNum(); - Vector bp = SK.GetEntity(ptB)->PointGetNum(); - - if(workplane.v != Entity::FREE_IN_3D.v) { - DoProjectedPoint(&ap); - DoProjectedPoint(&bp); - } - - Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); - - DoLineWithArrows(ref, ap, bp, false); - DoLabel(ref, labelPos, gr, gu); - break; - } - - case PROJ_PT_DISTANCE: { - Vector ap = SK.GetEntity(ptA)->PointGetNum(), - bp = SK.GetEntity(ptB)->PointGetNum(), - dp = (bp.Minus(ap)), - pp = SK.GetEntity(entityA)->VectorGetNum(); - - Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); - - pp = pp.WithMagnitude(1); - double d = dp.Dot(pp); - Vector bpp = ap.Plus(pp.ScaledBy(d)); - StippledLine(ap, bpp); - StippledLine(bp, bpp); - - DoLineWithArrows(ref, ap, bpp, false); - DoLabel(ref, labelPos, gr, gu); - break; - } - - case PT_FACE_DISTANCE: - case PT_PLANE_DISTANCE: { - Vector pt = SK.GetEntity(ptA)->PointGetNum(); - Entity *enta = SK.GetEntity(entityA); - Vector n, p; - if(type == PT_PLANE_DISTANCE) { - n = enta->Normal()->NormalN(); - p = enta->WorkplaneGetOffset(); - } else { - n = enta->FaceGetNormalNum(); - p = enta->FaceGetPointNum(); - } - - double d = (p.Minus(pt)).Dot(n); - Vector closest = pt.Plus(n.WithMagnitude(d)); - - Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset); - - if(!pt.Equals(closest)) { - DoLineWithArrows(ref, pt, closest, true); - } - - DoLabel(ref, labelPos, gr, gu); - break; - } - - case PT_LINE_DISTANCE: { - Vector pt = SK.GetEntity(ptA)->PointGetNum(); - Entity *line = SK.GetEntity(entityA); - Vector lA = SK.GetEntity(line->point[0])->PointGetNum(); - Vector lB = SK.GetEntity(line->point[1])->PointGetNum(); - Vector dl = lB.Minus(lA); - - if(workplane.v != Entity::FREE_IN_3D.v) { - lA = lA.ProjectInto(workplane); - lB = lB.ProjectInto(workplane); - DoProjectedPoint(&pt); - } - - // Find the closest point on the line - Vector closest = pt.ClosestPointOnLine(lA, dl); - - Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(disp.offset); - DoLabel(ref, labelPos, gr, gu); - - if(!pt.Equals(closest)) { - DoLineWithArrows(ref, pt, closest, true); - } - - if(workplane.v != Entity::FREE_IN_3D.v) { - // Draw the projection marker from the closest point on the - // projected line to the projected point on the real line. - Vector lAB = (lA.Minus(lB)); - double t = (lA.Minus(closest)).DivPivoting(lAB); - - Vector lA = SK.GetEntity(line->point[0])->PointGetNum(); - Vector lB = SK.GetEntity(line->point[1])->PointGetNum(); - - Vector c2 = (lA.ScaledBy(1-t)).Plus(lB.ScaledBy(t)); - DoProjectedPoint(&c2); - } - break; - } - - case DIAMETER: { - Entity *circle = SK.GetEntity(entityA); - Vector center = SK.GetEntity(circle->point[0])->PointGetNum(); - Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum(); - Vector n = q.RotationN().WithMagnitude(1); - double r = circle->CircleGetRadiusNum(); - - Vector ref = center.Plus(disp.offset); - // Force the label into the same plane as the circle. - ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center))); - - Vector mark = ref.Minus(center); - mark = mark.WithMagnitude(mark.Magnitude()-r); - DoLineTrimmedAgainstBox(ref, ref, ref.Minus(mark)); - - Vector topLeft; - DoLabel(ref, &topLeft, gr, gu); - if(labelPos) *labelPos = topLeft; - - // Draw the diameter symbol - Vector dc = topLeft; - dc = dc.Plus(gu.WithMagnitude(5/SS.GW.scale)); - dc = dc.Plus(gr.WithMagnitude(9/SS.GW.scale)); - double dr = 5/SS.GW.scale; - double theta, dtheta = (2*PI)/12; - for(theta = 0; theta < 2*PI-0.01; theta += dtheta) { - LineDrawOrGetDistance( - dc.Plus(gu.WithMagnitude(cos(theta)*dr)).Plus( - gr.WithMagnitude(sin(theta)*dr)), - dc.Plus(gu.WithMagnitude(cos(theta+dtheta)*dr)).Plus( - gr.WithMagnitude(sin(theta+dtheta)*dr))); - } - theta = 25*(PI/180); - dr *= 1.7; - dtheta = PI; - LineDrawOrGetDistance( - dc.Plus(gu.WithMagnitude(cos(theta)*dr)).Plus( - gr.WithMagnitude(sin(theta)*dr)), - dc.Plus(gu.WithMagnitude(cos(theta+dtheta)*dr)).Plus( - gr.WithMagnitude(sin(theta+dtheta)*dr))); - break; - } - - case POINTS_COINCIDENT: { - if(!dogd.drawing) { - for(int i = 0; i < 2; i++) { - Vector p = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum(); - Point2d pp = SS.GW.ProjectPoint(p); - // The point is selected within a radius of 7, from the - // same center; so if the point is visible, then this - // constraint cannot be selected. But that's okay. - dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 3); - dogd.refp = p; - } - break; - } - - // Let's adjust the color of this constraint to have the same - // rough luma as the point color, so that the constraint does not - // stand out in an ugly way. - DWORD cd = Style::Color(Style::DATUM), - cc = Style::Color(Style::CONSTRAINT); - // convert from 8-bit color to a vector - Vector vd = Vector::From(REDf(cd), GREENf(cd), BLUEf(cd)), - vc = Vector::From(REDf(cc), GREENf(cc), BLUEf(cc)); - // and scale the constraint color to have the same magnitude as - // the datum color, maybe a bit dimmer - vc = vc.WithMagnitude(vd.Magnitude()*0.9); - // and set the color to that. - glxColorRGB(RGBf(vc.x, vc.y, vc.z)); - - for(int a = 0; a < 2; a++) { - Vector r = SS.GW.projRight.ScaledBy((a+1)/SS.GW.scale); - Vector d = SS.GW.projUp.ScaledBy((2-a)/SS.GW.scale); - for(int i = 0; i < 2; i++) { - Vector p = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum(); - glBegin(GL_QUADS); - glxVertex3v(p.Plus (r).Plus (d)); - glxVertex3v(p.Plus (r).Minus(d)); - glxVertex3v(p.Minus(r).Minus(d)); - glxVertex3v(p.Minus(r).Plus (d)); - glEnd(); - } - - } - break; - } - - case PT_ON_CIRCLE: - case PT_ON_LINE: - case PT_ON_FACE: - case PT_IN_PLANE: { - double s = 8/SS.GW.scale; - Vector p = SK.GetEntity(ptA)->PointGetNum(); - Vector r, d; - if(type == PT_ON_FACE) { - Vector n = SK.GetEntity(entityA)->FaceGetNormalNum(); - r = n.Normal(0); - d = n.Normal(1); - } else if(type == PT_IN_PLANE) { - EntityBase *n = SK.GetEntity(entityA)->Normal(); - r = n->NormalU(); - d = n->NormalV(); - } else { - r = gr; - d = gu; - s *= (6.0/8); // draw these a little smaller - } - r = r.WithMagnitude(s); d = d.WithMagnitude(s); - LineDrawOrGetDistance(p.Plus (r).Plus (d), p.Plus (r).Minus(d)); - LineDrawOrGetDistance(p.Plus (r).Minus(d), p.Minus(r).Minus(d)); - LineDrawOrGetDistance(p.Minus(r).Minus(d), p.Minus(r).Plus (d)); - LineDrawOrGetDistance(p.Minus(r).Plus (d), p.Plus (r).Plus (d)); - break; - } - - case WHERE_DRAGGED: { - Vector p = SK.GetEntity(ptA)->PointGetNum(), - u = p.Plus(gu.WithMagnitude(8/SS.GW.scale)).Plus( - gr.WithMagnitude(8/SS.GW.scale)), - uu = u.Minus(gu.WithMagnitude(5/SS.GW.scale)), - ur = u.Minus(gr.WithMagnitude(5/SS.GW.scale)); - // Draw four little crop marks, uniformly spaced (by ninety - // degree rotations) around the point. - int i; - for(i = 0; i < 4; i++) { - LineDrawOrGetDistance(u, uu); - LineDrawOrGetDistance(u, ur); - u = u.RotatedAbout(p, gn, PI/2); - ur = ur.RotatedAbout(p, gn, PI/2); - uu = uu.RotatedAbout(p, gn, PI/2); - } - break; - } - - case SAME_ORIENTATION: { - for(int i = 0; i < 2; i++) { - Entity *e = SK.GetEntity(i == 0 ? entityA : entityB); - Quaternion q = e->NormalGetNum(); - Vector n = q.RotationN().WithMagnitude(25/SS.GW.scale); - Vector u = q.RotationU().WithMagnitude(6/SS.GW.scale); - Vector p = SK.GetEntity(e->point[0])->PointGetNum(); - p = p.Plus(n.WithMagnitude(10/SS.GW.scale)); - - LineDrawOrGetDistance(p.Plus(u), p.Minus(u).Plus(n)); - LineDrawOrGetDistance(p.Minus(u), p.Plus(u).Plus(n)); - } - break; - } - - case EQUAL_ANGLE: { - Vector ref; - Entity *a = SK.GetEntity(entityA); - Entity *b = SK.GetEntity(entityB); - Entity *c = SK.GetEntity(entityC); - Entity *d = SK.GetEntity(entityD); - - Vector a0 = a->VectorGetRefPoint(); - Vector b0 = b->VectorGetRefPoint(); - Vector c0 = c->VectorGetRefPoint(); - Vector d0 = d->VectorGetRefPoint(); - Vector da = a->VectorGetNum(); - Vector db = b->VectorGetNum(); - Vector dc = c->VectorGetNum(); - Vector dd = d->VectorGetNum(); - - if(other) da = da.ScaledBy(-1); - - DoArcForAngle(a0, da, b0, db, - da.WithMagnitude(40/SS.GW.scale), &ref); - DoArcForAngle(c0, dc, d0, dd, - dc.WithMagnitude(40/SS.GW.scale), &ref); - - break; - } - - case ANGLE: { - Entity *a = SK.GetEntity(entityA); - Entity *b = SK.GetEntity(entityB); - - Vector a0 = a->VectorGetRefPoint(); - Vector b0 = b->VectorGetRefPoint(); - Vector da = a->VectorGetNum(); - Vector db = b->VectorGetNum(); - if(other) da = da.ScaledBy(-1); - - Vector ref; - DoArcForAngle(a0, da, b0, db, disp.offset, &ref); - DoLabel(ref, labelPos, gr, gu); - break; - } - - case PERPENDICULAR: { - Vector u, v; - Vector rn, ru; - if(workplane.v == Entity::FREE_IN_3D.v) { - rn = gn; - ru = gu; - } else { - EntityBase *normal = SK.GetEntity(workplane)->Normal(); - rn = normal->NormalN(); - ru = normal->NormalV(); // ru meaning r_up, not u/v - } - - for(int i = 0; i < 2; i++) { - Entity *e = SK.GetEntity(i == 0 ? entityA : entityB); - - if(i == 0) { - // Calculate orientation of perpendicular sign only - // once, so that it's the same both times it's drawn - u = e->VectorGetNum(); - u = u.WithMagnitude(16/SS.GW.scale); - v = (rn.Cross(u)).WithMagnitude(16/SS.GW.scale); - // a bit of bias to stop it from flickering between the - // two possibilities - if(fabs(u.Dot(ru)) < fabs(v.Dot(ru)) + LENGTH_EPS) { - SWAP(Vector, u, v); - } - if(u.Dot(ru) < 0) u = u.ScaledBy(-1); - } - - Vector p = e->VectorGetRefPoint(); - Vector s = p.Plus(u).Plus(v); - LineDrawOrGetDistance(s, s.Plus(v)); - - Vector m = s.Plus(v.ScaledBy(0.5)); - LineDrawOrGetDistance(m, m.Plus(u)); - } - break; - } - - case CURVE_CURVE_TANGENT: - case CUBIC_LINE_TANGENT: - case ARC_LINE_TANGENT: { - Vector textAt, u, v; - - if(type == ARC_LINE_TANGENT) { - Entity *arc = SK.GetEntity(entityA); - Entity *norm = SK.GetEntity(arc->normal); - Vector c = SK.GetEntity(arc->point[0])->PointGetNum(); - Vector p = - SK.GetEntity(arc->point[other ? 2 : 1])->PointGetNum(); - Vector r = p.Minus(c); - textAt = p.Plus(r.WithMagnitude(14/SS.GW.scale)); - u = norm->NormalU(); - v = norm->NormalV(); - } else if(type == CUBIC_LINE_TANGENT) { - Vector n; - if(workplane.v == Entity::FREE_IN_3D.v) { - u = gr; - v = gu; - n = gn; - } else { - EntityBase *wn = SK.GetEntity(workplane)->Normal(); - u = wn->NormalU(); - v = wn->NormalV(); - n = wn->NormalN(); - } - - Entity *cubic = SK.GetEntity(entityA); - Vector p = other ? cubic->CubicGetFinishNum() : - cubic->CubicGetStartNum(); - Vector dir = SK.GetEntity(entityB)->VectorGetNum(); - Vector out = n.Cross(dir); - textAt = p.Plus(out.WithMagnitude(14/SS.GW.scale)); - } else { - Vector n, dir; - EntityBase *wn = SK.GetEntity(workplane)->Normal(); - u = wn->NormalU(); - v = wn->NormalV(); - n = wn->NormalN(); - EntityBase *eA = SK.GetEntity(entityA); - // Big pain; we have to get a vector tangent to the curve - // at the shared point, which could be from either a cubic - // or an arc. - if(other) { - textAt = eA->EndpointFinish(); - if(eA->type == Entity::CUBIC) { - dir = eA->CubicGetFinishTangentNum(); - } else { - dir = SK.GetEntity(eA->point[0])->PointGetNum().Minus( - SK.GetEntity(eA->point[2])->PointGetNum()); - dir = n.Cross(dir); - } - } else { - textAt = eA->EndpointStart(); - if(eA->type == Entity::CUBIC) { - dir = eA->CubicGetStartTangentNum(); - } else { - dir = SK.GetEntity(eA->point[0])->PointGetNum().Minus( - SK.GetEntity(eA->point[1])->PointGetNum()); - dir = n.Cross(dir); - } - } - dir = n.Cross(dir); - textAt = textAt.Plus(dir.WithMagnitude(14/SS.GW.scale)); - } - - if(dogd.drawing) { - glxWriteTextRefCenter("T", DEFAULT_TEXT_HEIGHT, - textAt, u, v, LineCallback, this); - } else { - dogd.refp = textAt; - Point2d ref = SS.GW.ProjectPoint(dogd.refp); - dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10); - } - break; - } - - case PARALLEL: { - for(int i = 0; i < 2; i++) { - Entity *e = SK.GetEntity(i == 0 ? entityA : entityB); - Vector n = e->VectorGetNum(); - n = n.WithMagnitude(25/SS.GW.scale); - Vector u = (gn.Cross(n)).WithMagnitude(4/SS.GW.scale); - Vector p = e->VectorGetRefPoint(); - - LineDrawOrGetDistance(p.Plus(u), p.Plus(u).Plus(n)); - LineDrawOrGetDistance(p.Minus(u), p.Minus(u).Plus(n)); - } - break; - } - - case EQUAL_RADIUS: { - for(int i = 0; i < 2; i++) { - DoEqualRadiusTicks(i == 0 ? entityA : entityB); - } - break; - } - - case EQUAL_LINE_ARC_LEN: { - Entity *line = SK.GetEntity(entityA); - DoEqualLenTicks( - SK.GetEntity(line->point[0])->PointGetNum(), - SK.GetEntity(line->point[1])->PointGetNum(), - gn); - - DoEqualRadiusTicks(entityB); - break; - } - - case LENGTH_RATIO: - case EQUAL_LENGTH_LINES: { - Vector a, b; - for(int i = 0; i < 2; i++) { - Entity *e = SK.GetEntity(i == 0 ? entityA : entityB); - a = SK.GetEntity(e->point[0])->PointGetNum(); - b = SK.GetEntity(e->point[1])->PointGetNum(); - - if(workplane.v != Entity::FREE_IN_3D.v) { - DoProjectedPoint(&a); - DoProjectedPoint(&b); - } - - DoEqualLenTicks(a, b, gn); - } - if(type == LENGTH_RATIO) { - Vector ref = ((a.Plus(b)).ScaledBy(0.5)).Plus(disp.offset); - DoLabel(ref, labelPos, gr, gu); - } - break; - } - - case EQ_LEN_PT_LINE_D: { - Entity *forLen = SK.GetEntity(entityA); - Vector a = SK.GetEntity(forLen->point[0])->PointGetNum(), - b = SK.GetEntity(forLen->point[1])->PointGetNum(); - if(workplane.v != Entity::FREE_IN_3D.v) { - DoProjectedPoint(&a); - DoProjectedPoint(&b); - } - DoEqualLenTicks(a, b, gn); - - Entity *ln = SK.GetEntity(entityB); - Vector la = SK.GetEntity(ln->point[0])->PointGetNum(), - lb = SK.GetEntity(ln->point[1])->PointGetNum(); - Vector pt = SK.GetEntity(ptA)->PointGetNum(); - if(workplane.v != Entity::FREE_IN_3D.v) { - DoProjectedPoint(&pt); - la = la.ProjectInto(workplane); - lb = lb.ProjectInto(workplane); - } - - Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la)); - LineDrawOrGetDistance(pt, closest); - DoEqualLenTicks(pt, closest, gn); - break; - } - - case EQ_PT_LN_DISTANCES: { - for(int i = 0; i < 2; i++) { - Entity *ln = SK.GetEntity(i == 0 ? entityA : entityB); - Vector la = SK.GetEntity(ln->point[0])->PointGetNum(), - lb = SK.GetEntity(ln->point[1])->PointGetNum(); - Entity *pte = SK.GetEntity(i == 0 ? ptA : ptB); - Vector pt = pte->PointGetNum(); - - if(workplane.v != Entity::FREE_IN_3D.v) { - DoProjectedPoint(&pt); - la = la.ProjectInto(workplane); - lb = lb.ProjectInto(workplane); - } - - Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la)); - - LineDrawOrGetDistance(pt, closest); - DoEqualLenTicks(pt, closest, gn); - } - break; - } - - { - Vector n; - case SYMMETRIC: - n = SK.GetEntity(entityA)->Normal()->NormalN(); goto s; - case SYMMETRIC_HORIZ: - n = SK.GetEntity(workplane)->Normal()->NormalU(); goto s; - case SYMMETRIC_VERT: - n = SK.GetEntity(workplane)->Normal()->NormalV(); goto s; - case SYMMETRIC_LINE: { - Entity *ln = SK.GetEntity(entityA); - Vector la = SK.GetEntity(ln->point[0])->PointGetNum(), - lb = SK.GetEntity(ln->point[1])->PointGetNum(); - la = la.ProjectInto(workplane); - lb = lb.ProjectInto(workplane); - n = lb.Minus(la); - Vector nw = SK.GetEntity(workplane)->Normal()->NormalN(); - n = n.RotatedAbout(nw, PI/2); - goto s; - } -s: - Vector a = SK.GetEntity(ptA)->PointGetNum(); - Vector b = SK.GetEntity(ptB)->PointGetNum(); - - for(int i = 0; i < 2; i++) { - Vector tail = (i == 0) ? a : b; - Vector d = (i == 0) ? b : a; - d = d.Minus(tail); - // Project the direction in which the arrow is drawn normal - // to the symmetry plane; for projected symmetry constraints, - // they might not be in the same direction, even when the - // constraint is fully solved. - d = n.ScaledBy(d.Dot(n)); - d = d.WithMagnitude(20/SS.GW.scale); - Vector tip = tail.Plus(d); - - LineDrawOrGetDistance(tail, tip); - d = d.WithMagnitude(9/SS.GW.scale); - LineDrawOrGetDistance(tip, tip.Minus(d.RotatedAbout(gn, 0.6))); - LineDrawOrGetDistance(tip, tip.Minus(d.RotatedAbout(gn, -0.6))); - } - break; - } - - case AT_MIDPOINT: - case HORIZONTAL: - case VERTICAL: - if(entityA.v) { - Vector r, u, n; - if(workplane.v == Entity::FREE_IN_3D.v) { - r = gr; u = gu; n = gn; - } else { - r = SK.GetEntity(workplane)->Normal()->NormalU(); - u = SK.GetEntity(workplane)->Normal()->NormalV(); - n = r.Cross(u); - } - // For "at midpoint", this branch is always taken. - Entity *e = SK.GetEntity(entityA); - Vector a = SK.GetEntity(e->point[0])->PointGetNum(); - Vector b = SK.GetEntity(e->point[1])->PointGetNum(); - Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5)); - Vector offset = (a.Minus(b)).Cross(n); - offset = offset.WithMagnitude(13/SS.GW.scale); - // Draw midpoint constraint on other side of line, so that - // a line can be midpoint and horizontal at same time. - if(type == AT_MIDPOINT) offset = offset.ScaledBy(-1); - - if(dogd.drawing) { - char *s = (type == HORIZONTAL) ? "H" : ( - (type == VERTICAL) ? "V" : ( - (type == AT_MIDPOINT) ? "M" : NULL)); - - glxWriteTextRefCenter(s, DEFAULT_TEXT_HEIGHT, - m.Plus(offset), r, u, LineCallback, this); - } else { - dogd.refp = m.Plus(offset); - Point2d ref = SS.GW.ProjectPoint(dogd.refp); - dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-10); - } - } else { - Vector a = SK.GetEntity(ptA)->PointGetNum(); - Vector b = SK.GetEntity(ptB)->PointGetNum(); - - Entity *w = SK.GetEntity(workplane); - Vector cu = w->Normal()->NormalU(); - Vector cv = w->Normal()->NormalV(); - Vector cn = w->Normal()->NormalN(); - - int i; - for(i = 0; i < 2; i++) { - Vector o = (i == 0) ? a : b; - Vector oo = (i == 0) ? a.Minus(b) : b.Minus(a); - Vector d = (type == HORIZONTAL) ? cu : cv; - if(oo.Dot(d) < 0) d = d.ScaledBy(-1); - - Vector dp = cn.Cross(d); - d = d.WithMagnitude(14/SS.GW.scale); - Vector c = o.Minus(d); - LineDrawOrGetDistance(o, c); - d = d.WithMagnitude(3/SS.GW.scale); - dp = dp.WithMagnitude(2/SS.GW.scale); - if(dogd.drawing) { - glBegin(GL_QUADS); - glxVertex3v((c.Plus(d)).Plus(dp)); - glxVertex3v((c.Minus(d)).Plus(dp)); - glxVertex3v((c.Minus(d)).Minus(dp)); - glxVertex3v((c.Plus(d)).Minus(dp)); - glEnd(); - } else { - Point2d ref = SS.GW.ProjectPoint(c); - dogd.dmin = min(dogd.dmin, ref.DistanceTo(dogd.mp)-6); - } - } - } - break; - - case COMMENT: { - if(disp.style.v) { - glLineWidth(Style::Width(disp.style)); - glxColorRGB(Style::Color(disp.style)); - } - Vector u, v; - if(workplane.v == Entity::FREE_IN_3D.v) { - u = gr; - v = gu; - } else { - EntityBase *norm = SK.GetEntity(workplane)->Normal(); - u = norm->NormalU(); - v = norm->NormalV(); - } - DoLabel(disp.offset, labelPos, u, v); - break; - } - - default: oops(); - } -} - -void Constraint::Draw(void) { - dogd.drawing = true; - dogd.sel = NULL; - - glLineWidth(Style::Width(Style::CONSTRAINT)); - glxColorRGB(Style::Color(Style::CONSTRAINT)); - - DrawOrGetDistance(NULL); -} - -double Constraint::GetDistance(Point2d mp) { - dogd.drawing = false; - dogd.sel = NULL; - dogd.mp = mp; - dogd.dmin = 1e12; - - DrawOrGetDistance(NULL); - - return dogd.dmin; -} - -Vector Constraint::GetLabelPos(void) { - dogd.drawing = false; - dogd.sel = NULL; - dogd.mp.x = 0; dogd.mp.y = 0; - dogd.dmin = 1e12; - - Vector p; - DrawOrGetDistance(&p); - return p; -} - -Vector Constraint::GetReferencePos(void) { - dogd.drawing = false; - dogd.sel = NULL; - - dogd.refp = SS.GW.offset.ScaledBy(-1); - DrawOrGetDistance(NULL); - - return dogd.refp; -} - -void Constraint::GetEdges(SEdgeList *sel) { - dogd.drawing = true; - dogd.sel = sel; - DrawOrGetDistance(NULL); - dogd.sel = NULL; -} - diff --git a/drawentity.cpp b/drawentity.cpp deleted file mode 100644 index 800a491..0000000 --- a/drawentity.cpp +++ /dev/null @@ -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 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(); -} - diff --git a/export.cpp b/export.cpp deleted file mode 100644 index 974190d..0000000 --- a/export.cpp +++ /dev/null @@ -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 - -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 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; -} - diff --git a/exportstep.cpp b/exportstep.cpp deleted file mode 100644 index ddc59d6..0000000 --- a/exportstep.cpp +++ /dev/null @@ -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 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 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(); -} - diff --git a/exportvector.cpp b/exportvector.cpp deleted file mode 100644 index 15d9254..0000000 --- a/exportvector.cpp +++ /dev/null @@ -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, -"\r\n" -"\r\n" -"\r\n" -"Exported SVG\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, "\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, -"\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\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); -} - diff --git a/exposed/Makefile b/exposed/Makefile deleted file mode 100644 index 388a7cd..0000000 --- a/exposed/Makefile +++ /dev/null @@ -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 - - diff --git a/exposed/obj/t b/exposed/obj/t deleted file mode 100644 index e69de29..0000000 diff --git a/extlib/libpng.lib b/extlib/libpng.lib deleted file mode 100644 index c182791..0000000 Binary files a/extlib/libpng.lib and /dev/null differ diff --git a/extlib/png.h b/extlib/png.h deleted file mode 100644 index 2e194b7..0000000 --- a/extlib/png.h +++ /dev/null @@ -1,3569 +0,0 @@ -/* png.h - header file for PNG reference library - * - * libpng version 1.2.29 - May 8, 2008 - * Copyright (c) 1998-2008 Glenn Randers-Pehrson - * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) - * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) - * - * Authors and maintainers: - * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat - * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger - * libpng versions 0.97, January 1998, through 1.2.29 - May 8, 2008: Glenn - * See also "Contributing Authors", below. - * - * Note about libpng version numbers: - * - * Due to various miscommunications, unforeseen code incompatibilities - * and occasional factors outside the authors' control, version numbering - * on the library has not always been consistent and straightforward. - * The following table summarizes matters since version 0.89c, which was - * the first widely used release: - * - * source png.h png.h shared-lib - * version string int version - * ------- ------ ----- ---------- - * 0.89c "1.0 beta 3" 0.89 89 1.0.89 - * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] - * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] - * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] - * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] - * 0.97c 0.97 97 2.0.97 - * 0.98 0.98 98 2.0.98 - * 0.99 0.99 98 2.0.99 - * 0.99a-m 0.99 99 2.0.99 - * 1.00 1.00 100 2.1.0 [100 should be 10000] - * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] - * 1.0.1 png.h string is 10001 2.1.0 - * 1.0.1a-e identical to the 10002 from here on, the shared library - * 1.0.2 source version) 10002 is 2.V where V is the source code - * 1.0.2a-b 10003 version, except as noted. - * 1.0.3 10003 - * 1.0.3a-d 10004 - * 1.0.4 10004 - * 1.0.4a-f 10005 - * 1.0.5 (+ 2 patches) 10005 - * 1.0.5a-d 10006 - * 1.0.5e-r 10100 (not source compatible) - * 1.0.5s-v 10006 (not binary compatible) - * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) - * 1.0.6d-f 10007 (still binary incompatible) - * 1.0.6g 10007 - * 1.0.6h 10007 10.6h (testing xy.z so-numbering) - * 1.0.6i 10007 10.6i - * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) - * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) - * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) - * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) - * 1.0.7 1 10007 (still compatible) - * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 - * 1.0.8rc1 1 10008 2.1.0.8rc1 - * 1.0.8 1 10008 2.1.0.8 - * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 - * 1.0.9rc1 1 10009 2.1.0.9rc1 - * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 - * 1.0.9rc2 1 10009 2.1.0.9rc2 - * 1.0.9 1 10009 2.1.0.9 - * 1.0.10beta1 1 10010 2.1.0.10beta1 - * 1.0.10rc1 1 10010 2.1.0.10rc1 - * 1.0.10 1 10010 2.1.0.10 - * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 - * 1.0.11rc1 1 10011 2.1.0.11rc1 - * 1.0.11 1 10011 2.1.0.11 - * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 - * 1.0.12rc1 2 10012 2.1.0.12rc1 - * 1.0.12 2 10012 2.1.0.12 - * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) - * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 - * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 - * 1.2.0rc1 3 10200 3.1.2.0rc1 - * 1.2.0 3 10200 3.1.2.0 - * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 - * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 - * 1.2.1 3 10201 3.1.2.1 - * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 - * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 - * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 - * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 - * 1.0.13 10 10013 10.so.0.1.0.13 - * 1.2.2 12 10202 12.so.0.1.2.2 - * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 - * 1.2.3 12 10203 12.so.0.1.2.3 - * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 - * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 - * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 - * 1.0.14 10 10014 10.so.0.1.0.14 - * 1.2.4 13 10204 12.so.0.1.2.4 - * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 - * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 - * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 - * 1.0.15 10 10015 10.so.0.1.0.15 - * 1.2.5 13 10205 12.so.0.1.2.5 - * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 - * 1.0.16 10 10016 10.so.0.1.0.16 - * 1.2.6 13 10206 12.so.0.1.2.6 - * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 - * 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 - * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 - * 1.0.17 10 10017 10.so.0.1.0.17 - * 1.2.7 13 10207 12.so.0.1.2.7 - * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 - * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 - * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 - * 1.0.18 10 10018 10.so.0.1.0.18 - * 1.2.8 13 10208 12.so.0.1.2.8 - * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 - * 1.2.9beta4-11 13 10209 12.so.0.9[.0] - * 1.2.9rc1 13 10209 12.so.0.9[.0] - * 1.2.9 13 10209 12.so.0.9[.0] - * 1.2.10beta1-8 13 10210 12.so.0.10[.0] - * 1.2.10rc1-3 13 10210 12.so.0.10[.0] - * 1.2.10 13 10210 12.so.0.10[.0] - * 1.2.11beta1-4 13 10211 12.so.0.11[.0] - * 1.0.19rc1-5 10 10019 10.so.0.19[.0] - * 1.2.11rc1-5 13 10211 12.so.0.11[.0] - * 1.0.19 10 10019 10.so.0.19[.0] - * 1.2.11 13 10211 12.so.0.11[.0] - * 1.0.20 10 10020 10.so.0.20[.0] - * 1.2.12 13 10212 12.so.0.12[.0] - * 1.2.13beta1 13 10213 12.so.0.13[.0] - * 1.0.21 10 10021 10.so.0.21[.0] - * 1.2.13 13 10213 12.so.0.13[.0] - * 1.2.14beta1-2 13 10214 12.so.0.14[.0] - * 1.0.22rc1 10 10022 10.so.0.22[.0] - * 1.2.14rc1 13 10214 12.so.0.14[.0] - * 1.0.22 10 10022 10.so.0.22[.0] - * 1.2.14 13 10214 12.so.0.14[.0] - * 1.2.15beta1-6 13 10215 12.so.0.15[.0] - * 1.0.23rc1-5 10 10023 10.so.0.23[.0] - * 1.2.15rc1-5 13 10215 12.so.0.15[.0] - * 1.0.23 10 10023 10.so.0.23[.0] - * 1.2.15 13 10215 12.so.0.15[.0] - * 1.2.16beta1-2 13 10216 12.so.0.16[.0] - * 1.2.16rc1 13 10216 12.so.0.16[.0] - * 1.0.24 10 10024 10.so.0.24[.0] - * 1.2.16 13 10216 12.so.0.16[.0] - * 1.2.17beta1-2 13 10217 12.so.0.17[.0] - * 1.0.25rc1 10 10025 10.so.0.25[.0] - * 1.2.17rc1-3 13 10217 12.so.0.17[.0] - * 1.0.25 10 10025 10.so.0.25[.0] - * 1.2.17 13 10217 12.so.0.17[.0] - * 1.0.26 10 10026 10.so.0.26[.0] - * 1.2.18 13 10218 12.so.0.18[.0] - * 1.2.19beta1-31 13 10219 12.so.0.19[.0] - * 1.0.27rc1-6 10 10027 10.so.0.27[.0] - * 1.2.19rc1-6 13 10219 12.so.0.19[.0] - * 1.0.27 10 10027 10.so.0.27[.0] - * 1.2.19 13 10219 12.so.0.19[.0] - * 1.2.20beta01-04 13 10220 12.so.0.20[.0] - * 1.0.28rc1-6 10 10028 10.so.0.28[.0] - * 1.2.20rc1-6 13 10220 12.so.0.20[.0] - * 1.0.28 10 10028 10.so.0.28[.0] - * 1.2.20 13 10220 12.so.0.20[.0] - * 1.2.21beta1-2 13 10221 12.so.0.21[.0] - * 1.2.21rc1-3 13 10221 12.so.0.21[.0] - * 1.0.29 10 10029 10.so.0.29[.0] - * 1.2.21 13 10221 12.so.0.21[.0] - * 1.2.22beta1-4 13 10222 12.so.0.22[.0] - * 1.0.30rc1 10 10030 10.so.0.30[.0] - * 1.2.22rc1 13 10222 12.so.0.22[.0] - * 1.0.30 10 10030 10.so.0.30[.0] - * 1.2.22 13 10222 12.so.0.22[.0] - * 1.2.23beta01-05 13 10223 12.so.0.23[.0] - * 1.2.23rc01 13 10223 12.so.0.23[.0] - * 1.2.23 13 10223 12.so.0.23[.0] - * 1.2.24beta01-02 13 10224 12.so.0.24[.0] - * 1.2.24rc01 13 10224 12.so.0.24[.0] - * 1.2.24 13 10224 12.so.0.24[.0] - * 1.2.25beta01-06 13 10225 12.so.0.25[.0] - * 1.2.25rc01-02 13 10225 12.so.0.25[.0] - * 1.0.31 10 10031 10.so.0.31[.0] - * 1.2.25 13 10225 12.so.0.25[.0] - * 1.2.26beta01-06 13 10226 12.so.0.26[.0] - * 1.2.26rc01 13 10226 12.so.0.26[.0] - * 1.2.26 13 10226 12.so.0.26[.0] - * 1.0.32 10 10032 10.so.0.32[.0] - * 1.2.27beta01-06 13 10227 12.so.0.27[.0] - * 1.2.27rc01 13 10227 12.so.0.27[.0] - * 1.0.33 10 10033 10.so.0.33[.0] - * 1.2.27 13 10227 12.so.0.27[.0] - * 1.0.34 10 10034 10.so.0.34[.0] - * 1.2.28 13 10228 12.so.0.28[.0] - * 1.2.29beta01-03 13 10229 12.so.0.29[.0] - * 1.2.29rc01 13 10229 12.so.0.29[.0] - * 1.0.35 10 10035 10.so.0.35[.0] - * 1.2.29 13 10229 12.so.0.29[.0] - * - * Henceforth the source version will match the shared-library major - * and minor numbers; the shared-library major version number will be - * used for changes in backward compatibility, as it is intended. The - * PNG_LIBPNG_VER macro, which is not used within libpng but is available - * for applications, is an unsigned integer of the form xyyzz corresponding - * to the source version x.y.z (leading zeros in y and z). Beta versions - * were given the previous public release number plus a letter, until - * version 1.0.6j; from then on they were given the upcoming public - * release number plus "betaNN" or "rcNN". - * - * Binary incompatibility exists only when applications make direct access - * to the info_ptr or png_ptr members through png.h, and the compiled - * application is loaded with a different version of the library. - * - * DLLNUM will change each time there are forward or backward changes - * in binary compatibility (e.g., when a new feature is added). - * - * See libpng.txt or libpng.3 for more information. The PNG specification - * is available as a W3C Recommendation and as an ISO Specification, - * defines should NOT be changed. - */ -#define PNG_INFO_gAMA 0x0001 -#define PNG_INFO_sBIT 0x0002 -#define PNG_INFO_cHRM 0x0004 -#define PNG_INFO_PLTE 0x0008 -#define PNG_INFO_tRNS 0x0010 -#define PNG_INFO_bKGD 0x0020 -#define PNG_INFO_hIST 0x0040 -#define PNG_INFO_pHYs 0x0080 -#define PNG_INFO_oFFs 0x0100 -#define PNG_INFO_tIME 0x0200 -#define PNG_INFO_pCAL 0x0400 -#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ -#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ -#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ -#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ -#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ - -/* This is used for the transformation routines, as some of them - * change these values for the row. It also should enable using - * the routines for other purposes. - */ -typedef struct png_row_info_struct -{ - png_uint_32 width; /* width of row */ - png_uint_32 rowbytes; /* number of bytes in row */ - png_byte color_type; /* color type of row */ - png_byte bit_depth; /* bit depth of row */ - png_byte channels; /* number of channels (1, 2, 3, or 4) */ - png_byte pixel_depth; /* bits per pixel (depth * channels) */ -} png_row_info; - -typedef png_row_info FAR * png_row_infop; -typedef png_row_info FAR * FAR * png_row_infopp; - -/* These are the function types for the I/O functions and for the functions - * that allow the user to override the default I/O functions with his or her - * own. The png_error_ptr type should match that of user-supplied warning - * and error functions, while the png_rw_ptr type should match that of the - * user read/write data functions. - */ -typedef struct png_struct_def png_struct; -typedef png_struct FAR * png_structp; - -typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); -typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); -typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); -typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, - int)); -typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, - int)); - -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); -typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); -typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, - png_uint_32, int)); -#endif - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) -typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, - png_row_infop, png_bytep)); -#endif - -#if defined(PNG_USER_CHUNKS_SUPPORTED) -typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); -#endif -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) -typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); -#endif - -/* Transform masks for the high-level interface */ -#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ -#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ -#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ -#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ -#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ -#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ -#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ -#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ -#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ -#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ -#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ -#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ -#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only */ - -/* Flags for MNG supported features */ -#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 -#define PNG_FLAG_MNG_FILTER_64 0x04 -#define PNG_ALL_MNG_FEATURES 0x05 - -typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); -typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); - -/* The structure that holds the information to read and write PNG files. - * The only people who need to care about what is inside of this are the - * people who will be modifying the library for their own special needs. - * It should NOT be accessed directly by an application, except to store - * the jmp_buf. - */ - -struct png_struct_def -{ -#ifdef PNG_SETJMP_SUPPORTED - jmp_buf jmpbuf; /* used in png_error */ -#endif - png_error_ptr error_fn; /* function for printing errors and aborting */ - png_error_ptr warning_fn; /* function for printing warnings */ - png_voidp error_ptr; /* user supplied struct for error functions */ - png_rw_ptr write_data_fn; /* function for writing output data */ - png_rw_ptr read_data_fn; /* function for reading input data */ - png_voidp io_ptr; /* ptr to application struct for I/O functions */ - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) - png_user_transform_ptr read_user_transform_fn; /* user read transform */ -#endif - -#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) - png_user_transform_ptr write_user_transform_fn; /* user write transform */ -#endif - -/* These were added in libpng-1.0.2 */ -#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) - png_voidp user_transform_ptr; /* user supplied struct for user transform */ - png_byte user_transform_depth; /* bit depth of user transformed pixels */ - png_byte user_transform_channels; /* channels in user transformed pixels */ -#endif -#endif - - png_uint_32 mode; /* tells us where we are in the PNG file */ - png_uint_32 flags; /* flags indicating various things to libpng */ - png_uint_32 transformations; /* which transformations to perform */ - - z_stream zstream; /* pointer to decompression structure (below) */ - png_bytep zbuf; /* buffer for zlib */ - png_size_t zbuf_size; /* size of zbuf */ - int zlib_level; /* holds zlib compression level */ - int zlib_method; /* holds zlib compression method */ - int zlib_window_bits; /* holds zlib compression window bits */ - int zlib_mem_level; /* holds zlib compression memory level */ - int zlib_strategy; /* holds zlib compression strategy */ - - png_uint_32 width; /* width of image in pixels */ - png_uint_32 height; /* height of image in pixels */ - png_uint_32 num_rows; /* number of rows in current pass */ - png_uint_32 usr_width; /* width of row at start of write */ - png_uint_32 rowbytes; /* size of row in bytes */ - png_uint_32 irowbytes; /* size of current interlaced row in bytes */ - png_uint_32 iwidth; /* width of current interlaced row in pixels */ - png_uint_32 row_number; /* current row in interlace pass */ - png_bytep prev_row; /* buffer to save previous (unfiltered) row */ - png_bytep row_buf; /* buffer to save current (unfiltered) row */ -#ifndef PNG_NO_WRITE_FILTERING - png_bytep sub_row; /* buffer to save "sub" row when filtering */ - png_bytep up_row; /* buffer to save "up" row when filtering */ - png_bytep avg_row; /* buffer to save "avg" row when filtering */ - png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ -#endif - png_row_info row_info; /* used for transformation routines */ - - png_uint_32 idat_size; /* current IDAT size for read */ - png_uint_32 crc; /* current chunk CRC value */ - png_colorp palette; /* palette from the input file */ - png_uint_16 num_palette; /* number of color entries in palette */ - png_uint_16 num_trans; /* number of transparency values */ - png_byte chunk_name[5]; /* null-terminated name of current chunk */ - png_byte compression; /* file compression type (always 0) */ - png_byte filter; /* file filter type (always 0) */ - png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ - png_byte pass; /* current interlace pass (0 - 6) */ - png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ - png_byte color_type; /* color type of file */ - png_byte bit_depth; /* bit depth of file */ - png_byte usr_bit_depth; /* bit depth of users row */ - png_byte pixel_depth; /* number of bits per pixel */ - png_byte channels; /* number of channels in file */ - png_byte usr_channels; /* channels at start of write */ - png_byte sig_bytes; /* magic bytes read/written from start of file */ - -#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) -#ifdef PNG_LEGACY_SUPPORTED - png_byte filler; /* filler byte for pixel expansion */ -#else - png_uint_16 filler; /* filler bytes for pixel expansion */ -#endif -#endif - -#if defined(PNG_bKGD_SUPPORTED) - png_byte background_gamma_type; -# ifdef PNG_FLOATING_POINT_SUPPORTED - float background_gamma; -# endif - png_color_16 background; /* background color in screen gamma space */ -#if defined(PNG_READ_GAMMA_SUPPORTED) - png_color_16 background_1; /* background normalized to gamma 1.0 */ -#endif -#endif /* PNG_bKGD_SUPPORTED */ - -#if defined(PNG_WRITE_FLUSH_SUPPORTED) - png_flush_ptr output_flush_fn;/* Function for flushing output */ - png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ - png_uint_32 flush_rows; /* number of rows written since last flush */ -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ -#ifdef PNG_FLOATING_POINT_SUPPORTED - float gamma; /* file gamma value */ - float screen_gamma; /* screen gamma value (display_exponent) */ -#endif -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - png_bytep gamma_table; /* gamma table for 8-bit depth files */ - png_bytep gamma_from_1; /* converts from 1.0 to screen */ - png_bytep gamma_to_1; /* converts from file to 1.0 */ - png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ - png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ - png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) - png_color_8 sig_bit; /* significant bits in each available channel */ -#endif - -#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) - png_color_8 shift; /* shift for significant bit tranformation */ -#endif - -#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ - || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - png_bytep trans; /* transparency values for paletted files */ - png_color_16 trans_values; /* transparency values for non-paletted files */ -#endif - - png_read_status_ptr read_row_fn; /* called after each row is decoded */ - png_write_status_ptr write_row_fn; /* called after each row is encoded */ -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED - png_progressive_info_ptr info_fn; /* called after header data fully read */ - png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ - png_progressive_end_ptr end_fn; /* called after image is complete */ - png_bytep save_buffer_ptr; /* current location in save_buffer */ - png_bytep save_buffer; /* buffer for previously read data */ - png_bytep current_buffer_ptr; /* current location in current_buffer */ - png_bytep current_buffer; /* buffer for recently used data */ - png_uint_32 push_length; /* size of current input chunk */ - png_uint_32 skip_length; /* bytes to skip in input data */ - png_size_t save_buffer_size; /* amount of data now in save_buffer */ - png_size_t save_buffer_max; /* total size of save_buffer */ - png_size_t buffer_size; /* total amount of available input data */ - png_size_t current_buffer_size; /* amount of data now in current_buffer */ - int process_mode; /* what push library is currently doing */ - int cur_palette; /* current push library palette index */ - -# if defined(PNG_TEXT_SUPPORTED) - png_size_t current_text_size; /* current size of text input data */ - png_size_t current_text_left; /* how much text left to read in input */ - png_charp current_text; /* current text chunk buffer */ - png_charp current_text_ptr; /* current location in current_text */ -# endif /* PNG_TEXT_SUPPORTED */ -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ - -#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) -/* for the Borland special 64K segment handler */ - png_bytepp offset_table_ptr; - png_bytep offset_table; - png_uint_16 offset_table_number; - png_uint_16 offset_table_count; - png_uint_16 offset_table_count_free; -#endif - -#if defined(PNG_READ_DITHER_SUPPORTED) - png_bytep palette_lookup; /* lookup table for dithering */ - png_bytep dither_index; /* index translation for palette files */ -#endif - -#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) - png_uint_16p hist; /* histogram */ -#endif - -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) - png_byte heuristic_method; /* heuristic for row filter selection */ - png_byte num_prev_filters; /* number of weights for previous rows */ - png_bytep prev_filters; /* filter type(s) of previous row(s) */ - png_uint_16p filter_weights; /* weight(s) for previous line(s) */ - png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ - png_uint_16p filter_costs; /* relative filter calculation cost */ - png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ -#endif - -#if defined(PNG_TIME_RFC1123_SUPPORTED) - png_charp time_buffer; /* String to hold RFC 1123 time text */ -#endif - -/* New members added in libpng-1.0.6 */ - -#ifdef PNG_FREE_ME_SUPPORTED - png_uint_32 free_me; /* flags items libpng is responsible for freeing */ -#endif - -#if defined(PNG_USER_CHUNKS_SUPPORTED) - png_voidp user_chunk_ptr; - png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ -#endif - -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) - int num_chunk_list; - png_bytep chunk_list; -#endif - -/* New members added in libpng-1.0.3 */ -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - png_byte rgb_to_gray_status; - /* These were changed from png_byte in libpng-1.0.6 */ - png_uint_16 rgb_to_gray_red_coeff; - png_uint_16 rgb_to_gray_green_coeff; - png_uint_16 rgb_to_gray_blue_coeff; -#endif - -/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ -#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ - defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ - defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) -/* changed from png_byte to png_uint_32 at version 1.2.0 */ -#ifdef PNG_1_0_X - png_byte mng_features_permitted; -#else - png_uint_32 mng_features_permitted; -#endif /* PNG_1_0_X */ -#endif - -/* New member added in libpng-1.0.7 */ -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - png_fixed_point int_gamma; -#endif - -/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ -#if defined(PNG_MNG_FEATURES_SUPPORTED) - png_byte filter_type; -#endif - -#if defined(PNG_1_0_X) -/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ - png_uint_32 row_buf_size; -#endif - -/* New members added in libpng-1.2.0 */ -#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) -# if !defined(PNG_1_0_X) -# if defined(PNG_MMX_CODE_SUPPORTED) - png_byte mmx_bitdepth_threshold; - png_uint_32 mmx_rowbytes_threshold; -# endif - png_uint_32 asm_flags; -# endif -#endif - -/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ -#ifdef PNG_USER_MEM_SUPPORTED - png_voidp mem_ptr; /* user supplied struct for mem functions */ - png_malloc_ptr malloc_fn; /* function for allocating memory */ - png_free_ptr free_fn; /* function for freeing memory */ -#endif - -/* New member added in libpng-1.0.13 and 1.2.0 */ - png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ - -#if defined(PNG_READ_DITHER_SUPPORTED) -/* The following three members were added at version 1.0.14 and 1.2.4 */ - png_bytep dither_sort; /* working sort array */ - png_bytep index_to_palette; /* where the original index currently is */ - /* in the palette */ - png_bytep palette_to_index; /* which original index points to this */ - /* palette color */ -#endif - -/* New members added in libpng-1.0.16 and 1.2.6 */ - png_byte compression_type; - -#ifdef PNG_SET_USER_LIMITS_SUPPORTED - png_uint_32 user_width_max; - png_uint_32 user_height_max; -#endif - -/* New member added in libpng-1.0.25 and 1.2.17 */ -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) - /* storage for unknown chunk that the library doesn't recognize. */ - png_unknown_chunk unknown_chunk; -#endif - -/* New members added in libpng-1.2.26 */ - png_uint_32 old_big_row_buf_size, old_prev_row_size; -}; - - -/* This triggers a compiler error in png.c, if png.c and png.h - * do not agree upon the version number. - */ -typedef png_structp version_1_2_29; - -typedef png_struct FAR * FAR * png_structpp; - -/* Here are the function definitions most commonly used. This is not - * the place to find out how to use libpng. See libpng.txt for the - * full explanation, see example.c for the summary. This just provides - * a simple one line description of the use of each function. - */ - -/* Returns the version number of the library */ -extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); - -/* Tell lib we have already handled the first magic bytes. - * Handling more than 8 bytes from the beginning of the file is an error. - */ -extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, - int num_bytes)); - -/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a - * PNG file. Returns zero if the supplied bytes match the 8-byte PNG - * signature, and non-zero otherwise. Having num_to_check == 0 or - * start > 7 will always fail (ie return non-zero). - */ -extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, - png_size_t num_to_check)); - -/* Simple signature checking function. This is the same as calling - * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). - */ -extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); - -/* Allocate and initialize png_ptr struct for reading, and any other memory. */ -extern PNG_EXPORT(png_structp,png_create_read_struct) - PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn)); - -/* Allocate and initialize png_ptr struct for writing, and any other memory */ -extern PNG_EXPORT(png_structp,png_create_write_struct) - PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn)); - -#ifdef PNG_WRITE_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) - PNGARG((png_structp png_ptr)); -#endif - -#ifdef PNG_WRITE_SUPPORTED -extern PNG_EXPORT(void,png_set_compression_buffer_size) - PNGARG((png_structp png_ptr, png_uint_32 size)); -#endif - -/* Reset the compression stream */ -extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); - -/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ -#ifdef PNG_USER_MEM_SUPPORTED -extern PNG_EXPORT(png_structp,png_create_read_struct_2) - PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, - png_malloc_ptr malloc_fn, png_free_ptr free_fn)); -extern PNG_EXPORT(png_structp,png_create_write_struct_2) - PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, - png_malloc_ptr malloc_fn, png_free_ptr free_fn)); -#endif - -/* Write a PNG chunk - size, type, (optional) data, CRC. */ -extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, - png_bytep chunk_name, png_bytep data, png_size_t length)); - -/* Write the start of a PNG chunk - length and chunk name. */ -extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, - png_bytep chunk_name, png_uint_32 length)); - -/* Write the data of a PNG chunk started with png_write_chunk_start(). */ -extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, - png_bytep data, png_size_t length)); - -/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ -extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); - -/* Allocate and initialize the info structure */ -extern PNG_EXPORT(png_infop,png_create_info_struct) - PNGARG((png_structp png_ptr)); - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Initialize the info structure (old interface - DEPRECATED) */ -extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); -#undef png_info_init -#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ - png_sizeof(png_info)); -#endif - -extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, - png_size_t png_info_struct_size)); - -/* Writes all the PNG information before the image. */ -extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, - png_infop info_ptr)); -extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, - png_infop info_ptr)); - -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED -/* read the information before the actual image data. */ -extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, - png_infop info_ptr)); -#endif - -#if defined(PNG_TIME_RFC1123_SUPPORTED) -extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) - PNGARG((png_structp png_ptr, png_timep ptime)); -#endif - -#if !defined(_WIN32_WCE) -/* "time.h" functions are not supported on WindowsCE */ -#if defined(PNG_WRITE_tIME_SUPPORTED) -/* convert from a struct tm to png_time */ -extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, - struct tm FAR * ttime)); - -/* convert from time_t to png_time. Uses gmtime() */ -extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, - time_t ttime)); -#endif /* PNG_WRITE_tIME_SUPPORTED */ -#endif /* _WIN32_WCE */ - -#if defined(PNG_READ_EXPAND_SUPPORTED) -/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ -extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); -#if !defined(PNG_1_0_X) -extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp - png_ptr)); -#endif -extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); -extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Deprecated */ -extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); -#endif -#endif - -#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -/* Use blue, green, red order for pixels. */ -extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) -/* Expand the grayscale to 24-bit RGB if necessary. */ -extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) -/* Reduce RGB to grayscale. */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, - int error_action, double red, double green )); -#endif -extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, - int error_action, png_fixed_point red, png_fixed_point green )); -extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp - png_ptr)); -#endif - -extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, - png_colorp palette)); - -#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) -extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ - defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) -extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ - defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) -extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) -/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ -extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, - png_uint_32 filler, int flags)); -/* The values of the PNG_FILLER_ defines should NOT be changed */ -#define PNG_FILLER_BEFORE 0 -#define PNG_FILLER_AFTER 1 -/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ -#if !defined(PNG_1_0_X) -extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, - png_uint_32 filler, int flags)); -#endif -#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ - -#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -/* Swap bytes in 16-bit depth files. */ -extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) -/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ -extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) -/* Swap packing order of pixels in bytes. */ -extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) -/* Converts files to legal bit depths. */ -extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, - png_color_8p true_bits)); -#endif - -#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ - defined(PNG_WRITE_INTERLACING_SUPPORTED) -/* Have the code handle the interlacing. Returns the number of passes. */ -extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) -/* Invert monochrome files */ -extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) -/* Handle alpha and tRNS by replacing with a background color. */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, - png_color_16p background_color, int background_gamma_code, - int need_expand, double background_gamma)); -#endif -#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 -#define PNG_BACKGROUND_GAMMA_SCREEN 1 -#define PNG_BACKGROUND_GAMMA_FILE 2 -#define PNG_BACKGROUND_GAMMA_UNIQUE 3 -#endif - -#if defined(PNG_READ_16_TO_8_SUPPORTED) -/* strip the second byte of information from a 16-bit depth file. */ -extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_DITHER_SUPPORTED) -/* Turn on dithering, and reduce the palette to the number of colors available. */ -extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, - png_colorp palette, int num_palette, int maximum_colors, - png_uint_16p histogram, int full_dither)); -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) -/* Handle gamma correction. Screen_gamma=(display_exponent) */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, - double screen_gamma, double default_file_gamma)); -#endif -#endif - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ - defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) -/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ -/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ -extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, - int empty_plte_permitted)); -#endif -#endif - -#if defined(PNG_WRITE_FLUSH_SUPPORTED) -/* Set how many lines between output flushes - 0 for no flushing */ -extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); -/* Flush the current PNG output buffer */ -extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); -#endif - -/* optional update palette with requested transformations */ -extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); - -/* optional call to update the users info structure */ -extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, - png_infop info_ptr)); - -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED -/* read one or more rows of image data. */ -extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, - png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); -#endif - -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED -/* read a row of data. */ -extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, - png_bytep row, - png_bytep display_row)); -#endif - -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED -/* read the whole image into memory at once. */ -extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, - png_bytepp image)); -#endif - -/* write a row of image data */ -extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, - png_bytep row)); - -/* write a few rows of image data */ -extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, - png_bytepp row, png_uint_32 num_rows)); - -/* write the image data */ -extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, - png_bytepp image)); - -/* writes the end of the PNG file. */ -extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, - png_infop info_ptr)); - -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED -/* read the end of the PNG file. */ -extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, - png_infop info_ptr)); -#endif - -/* free any memory associated with the png_info_struct */ -extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, - png_infopp info_ptr_ptr)); - -/* free any memory associated with the png_struct and the png_info_structs */ -extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp - png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); - -/* free all memory used by the read (old method - NOT DLL EXPORTED) */ -extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, - png_infop end_info_ptr)); - -/* free any memory associated with the png_struct and the png_info_structs */ -extern PNG_EXPORT(void,png_destroy_write_struct) - PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); - -/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ -extern void png_write_destroy PNGARG((png_structp png_ptr)); - -/* set the libpng method of handling chunk CRC errors */ -extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, - int crit_action, int ancil_action)); - -/* Values for png_set_crc_action() to say how to handle CRC errors in - * ancillary and critical chunks, and whether to use the data contained - * therein. Note that it is impossible to "discard" data in a critical - * chunk. For versions prior to 0.90, the action was always error/quit, - * whereas in version 0.90 and later, the action for CRC errors in ancillary - * chunks is warn/discard. These values should NOT be changed. - * - * value action:critical action:ancillary - */ -#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ -#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ -#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ -#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ -#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ -#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ - -/* These functions give the user control over the scan-line filtering in - * libpng and the compression methods used by zlib. These functions are - * mainly useful for testing, as the defaults should work with most users. - * Those users who are tight on memory or want faster performance at the - * expense of compression can modify them. See the compression library - * header file (zlib.h) for an explination of the compression functions. - */ - -/* set the filtering method(s) used by libpng. Currently, the only valid - * value for "method" is 0. - */ -extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, - int filters)); - -/* Flags for png_set_filter() to say which filters to use. The flags - * are chosen so that they don't conflict with real filter types - * below, in case they are supplied instead of the #defined constants. - * These values should NOT be changed. - */ -#define PNG_NO_FILTERS 0x00 -#define PNG_FILTER_NONE 0x08 -#define PNG_FILTER_SUB 0x10 -#define PNG_FILTER_UP 0x20 -#define PNG_FILTER_AVG 0x40 -#define PNG_FILTER_PAETH 0x80 -#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ - PNG_FILTER_AVG | PNG_FILTER_PAETH) - -/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. - * These defines should NOT be changed. - */ -#define PNG_FILTER_VALUE_NONE 0 -#define PNG_FILTER_VALUE_SUB 1 -#define PNG_FILTER_VALUE_UP 2 -#define PNG_FILTER_VALUE_AVG 3 -#define PNG_FILTER_VALUE_PAETH 4 -#define PNG_FILTER_VALUE_LAST 5 - -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ -/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ - * defines, either the default (minimum-sum-of-absolute-differences), or - * the experimental method (weighted-minimum-sum-of-absolute-differences). - * - * Weights are factors >= 1.0, indicating how important it is to keep the - * filter type consistent between rows. Larger numbers mean the current - * filter is that many times as likely to be the same as the "num_weights" - * previous filters. This is cumulative for each previous row with a weight. - * There needs to be "num_weights" values in "filter_weights", or it can be - * NULL if the weights aren't being specified. Weights have no influence on - * the selection of the first row filter. Well chosen weights can (in theory) - * improve the compression for a given image. - * - * Costs are factors >= 1.0 indicating the relative decoding costs of a - * filter type. Higher costs indicate more decoding expense, and are - * therefore less likely to be selected over a filter with lower computational - * costs. There needs to be a value in "filter_costs" for each valid filter - * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't - * setting the costs. Costs try to improve the speed of decompression without - * unduly increasing the compressed image size. - * - * A negative weight or cost indicates the default value is to be used, and - * values in the range [0.0, 1.0) indicate the value is to remain unchanged. - * The default values for both weights and costs are currently 1.0, but may - * change if good general weighting/cost heuristics can be found. If both - * the weights and costs are set to 1.0, this degenerates the WEIGHTED method - * to the UNWEIGHTED method, but with added encoding time/computation. - */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, - int heuristic_method, int num_weights, png_doublep filter_weights, - png_doublep filter_costs)); -#endif -#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ - -/* Heuristic used for row filter selection. These defines should NOT be - * changed. - */ -#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ -#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ -#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ -#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ - -/* Set the library compression level. Currently, valid values range from - * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 - * (0 - no compression, 9 - "maximal" compression). Note that tests have - * shown that zlib compression levels 3-6 usually perform as well as level 9 - * for PNG images, and do considerably fewer caclulations. In the future, - * these values may not correspond directly to the zlib compression levels. - */ -extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, - int level)); - -extern PNG_EXPORT(void,png_set_compression_mem_level) - PNGARG((png_structp png_ptr, int mem_level)); - -extern PNG_EXPORT(void,png_set_compression_strategy) - PNGARG((png_structp png_ptr, int strategy)); - -extern PNG_EXPORT(void,png_set_compression_window_bits) - PNGARG((png_structp png_ptr, int window_bits)); - -extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, - int method)); - -/* These next functions are called for input/output, memory, and error - * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, - * and call standard C I/O routines such as fread(), fwrite(), and - * fprintf(). These functions can be made to use other I/O routines - * at run time for those applications that need to handle I/O in a - * different manner by calling png_set_???_fn(). See libpng.txt for - * more information. - */ - -#if !defined(PNG_NO_STDIO) -/* Initialize the input/output for the PNG file to the default functions. */ -extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); -#endif - -/* Replace the (error and abort), and warning functions with user - * supplied functions. If no messages are to be printed you must still - * write and use replacement functions. The replacement error_fn should - * still do a longjmp to the last setjmp location if you are using this - * method of error handling. If error_fn or warning_fn is NULL, the - * default function will be used. - */ - -extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, - png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); - -/* Return the user pointer associated with the error functions */ -extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); - -/* Replace the default data output functions with a user supplied one(s). - * If buffered output is not used, then output_flush_fn can be set to NULL. - * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time - * output_flush_fn will be ignored (and thus can be NULL). - */ -extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, - png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); - -/* Replace the default data input function with a user supplied one. */ -extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, - png_voidp io_ptr, png_rw_ptr read_data_fn)); - -/* Return the user pointer associated with the I/O functions */ -extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); - -extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, - png_read_status_ptr read_row_fn)); - -extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, - png_write_status_ptr write_row_fn)); - -#ifdef PNG_USER_MEM_SUPPORTED -/* Replace the default memory allocation functions with user supplied one(s). */ -extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, - png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); -/* Return the user pointer associated with the memory functions */ -extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); -#endif - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) -extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp - png_ptr, png_user_transform_ptr read_user_transform_fn)); -#endif - -#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) -extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp - png_ptr, png_user_transform_ptr write_user_transform_fn)); -#endif - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) -extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp - png_ptr, png_voidp user_transform_ptr, int user_transform_depth, - int user_transform_channels)); -/* Return the user pointer associated with the user transform functions */ -extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) - PNGARG((png_structp png_ptr)); -#endif - -#ifdef PNG_USER_CHUNKS_SUPPORTED -extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, - png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); -extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp - png_ptr)); -#endif - -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -/* Sets the function callbacks for the push reader, and a pointer to a - * user-defined structure available to the callback functions. - */ -extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, - png_voidp progressive_ptr, - png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, - png_progressive_end_ptr end_fn)); - -/* returns the user pointer associated with the push read functions */ -extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) - PNGARG((png_structp png_ptr)); - -/* function to be called when data becomes available */ -extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); - -/* function that combines rows. Not very much different than the - * png_combine_row() call. Is this even used????? - */ -extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, - png_bytep old_row, png_bytep new_row)); -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ - -extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, - png_uint_32 size)); - -#if defined(PNG_1_0_X) -# define png_malloc_warn png_malloc -#else -/* Added at libpng version 1.2.4 */ -extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, - png_uint_32 size)); -#endif - -/* frees a pointer allocated by png_malloc() */ -extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); - -#if defined(PNG_1_0_X) -/* Function to allocate memory for zlib. */ -extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, - uInt size)); - -/* Function to free memory for zlib */ -extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); -#endif - -/* Free data that was allocated internally */ -extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 free_me, int num)); -#ifdef PNG_FREE_ME_SUPPORTED -/* Reassign responsibility for freeing existing data, whether allocated - * by libpng or by the application */ -extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, - png_infop info_ptr, int freer, png_uint_32 mask)); -#endif -/* assignments for png_data_freer */ -#define PNG_DESTROY_WILL_FREE_DATA 1 -#define PNG_SET_WILL_FREE_DATA 1 -#define PNG_USER_WILL_FREE_DATA 2 -/* Flags for png_ptr->free_me and info_ptr->free_me */ -#define PNG_FREE_HIST 0x0008 -#define PNG_FREE_ICCP 0x0010 -#define PNG_FREE_SPLT 0x0020 -#define PNG_FREE_ROWS 0x0040 -#define PNG_FREE_PCAL 0x0080 -#define PNG_FREE_SCAL 0x0100 -#define PNG_FREE_UNKN 0x0200 -#define PNG_FREE_LIST 0x0400 -#define PNG_FREE_PLTE 0x1000 -#define PNG_FREE_TRNS 0x2000 -#define PNG_FREE_TEXT 0x4000 -#define PNG_FREE_ALL 0x7fff -#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ - -#ifdef PNG_USER_MEM_SUPPORTED -extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, - png_uint_32 size)); -extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, - png_voidp ptr)); -#endif - -extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, - png_voidp s1, png_voidp s2, png_uint_32 size)); - -extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, - png_voidp s1, int value, png_uint_32 size)); - -#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ -extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, - int check)); -#endif /* USE_FAR_KEYWORD */ - -#ifndef PNG_NO_ERROR_TEXT -/* Fatal error in PNG image of libpng - can't continue */ -extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, - png_const_charp error_message)); - -/* The same, but the chunk name is prepended to the error string. */ -extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, - png_const_charp error_message)); -#else -/* Fatal error in PNG image of libpng - can't continue */ -extern PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)); -#endif - -#ifndef PNG_NO_WARNINGS -/* Non-fatal error in libpng. Can continue, but may have a problem. */ -extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, - png_const_charp warning_message)); - -#ifdef PNG_READ_SUPPORTED -/* Non-fatal error in libpng, chunk name is prepended to message. */ -extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, - png_const_charp warning_message)); -#endif /* PNG_READ_SUPPORTED */ -#endif /* PNG_NO_WARNINGS */ - -/* The png_set_ functions are for storing values in the png_info_struct. - * Similarly, the png_get_ calls are used to read values from the - * png_info_struct, either storing the parameters in the passed variables, or - * setting pointers into the png_info_struct where the data is stored. The - * png_get_ functions return a non-zero value if the data was available - * in info_ptr, or return zero and do not change any of the parameters if the - * data was not available. - * - * These functions should be used instead of directly accessing png_info - * to avoid problems with future changes in the size and internal layout of - * png_info_struct. - */ -/* Returns "flag" if chunk data is valid in info_ptr. */ -extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, -png_infop info_ptr, png_uint_32 flag)); - -/* Returns number of bytes needed to hold a transformed row. */ -extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -#if defined(PNG_INFO_IMAGE_SUPPORTED) -/* Returns row_pointers, which is an array of pointers to scanlines that was -returned from png_read_png(). */ -extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, -png_infop info_ptr)); -/* Set row_pointers, which is an array of pointers to scanlines for use -by png_write_png(). */ -extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_bytepp row_pointers)); -#endif - -/* Returns number of color channels in image. */ -extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -#ifdef PNG_EASY_ACCESS_SUPPORTED -/* Returns image width in pixels. */ -extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -/* Returns image height in pixels. */ -extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -/* Returns image bit_depth. */ -extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -/* Returns image color_type. */ -extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -/* Returns image filter_type. */ -extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -/* Returns image interlace_type. */ -extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -/* Returns image compression_type. */ -extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -/* Returns image resolution in pixels per meter, from pHYs chunk data. */ -extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -/* Returns pixel aspect ratio, computed from pHYs chunk data. */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -#endif - -/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ -extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp -png_ptr, png_infop info_ptr)); - -#endif /* PNG_EASY_ACCESS_SUPPORTED */ - -/* Returns pointer to signature string read from PNG header */ -extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -#if defined(PNG_bKGD_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_color_16p *background)); -#endif - -#if defined(PNG_bKGD_SUPPORTED) -extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_color_16p background)); -#endif - -#if defined(PNG_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, - png_infop info_ptr, double *white_x, double *white_y, double *red_x, - double *red_y, double *green_x, double *green_y, double *blue_x, - double *blue_y)); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point - *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, - png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point - *int_blue_x, png_fixed_point *int_blue_y)); -#endif -#endif - -#if defined(PNG_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, - png_infop info_ptr, double white_x, double white_y, double red_x, - double red_y, double green_x, double green_y, double blue_x, double blue_y)); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, - png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point - int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, - png_fixed_point int_blue_y)); -#endif -#endif - -#if defined(PNG_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, - png_infop info_ptr, double *file_gamma)); -#endif -extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_fixed_point *int_file_gamma)); -#endif - -#if defined(PNG_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, - png_infop info_ptr, double file_gamma)); -#endif -extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_fixed_point int_file_gamma)); -#endif - -#if defined(PNG_hIST_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_16p *hist)); -#endif - -#if defined(PNG_hIST_SUPPORTED) -extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_16p hist)); -#endif - -extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, - int *bit_depth, int *color_type, int *interlace_method, - int *compression_method, int *filter_method)); - -extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, - int color_type, int interlace_method, int compression_method, - int filter_method)); - -#if defined(PNG_oFFs_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, - int *unit_type)); -#endif - -#if defined(PNG_oFFs_SUPPORTED) -extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, - int unit_type)); -#endif - -#if defined(PNG_pCAL_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, - int *type, int *nparams, png_charp *units, png_charpp *params)); -#endif - -#if defined(PNG_pCAL_SUPPORTED) -extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, - int type, int nparams, png_charp units, png_charpp params)); -#endif - -#if defined(PNG_pHYs_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); -#endif - -#if defined(PNG_pHYs_SUPPORTED) -extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); -#endif - -extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_colorp *palette, int *num_palette)); - -extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_colorp palette, int num_palette)); - -#if defined(PNG_sBIT_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_color_8p *sig_bit)); -#endif - -#if defined(PNG_sBIT_SUPPORTED) -extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_color_8p sig_bit)); -#endif - -#if defined(PNG_sRGB_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, - png_infop info_ptr, int *intent)); -#endif - -#if defined(PNG_sRGB_SUPPORTED) -extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, - png_infop info_ptr, int intent)); -extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, - png_infop info_ptr, int intent)); -#endif - -#if defined(PNG_iCCP_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_charpp name, int *compression_type, - png_charpp profile, png_uint_32 *proflen)); - /* Note to maintainer: profile should be png_bytepp */ -#endif - -#if defined(PNG_iCCP_SUPPORTED) -extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_charp name, int compression_type, - png_charp profile, png_uint_32 proflen)); - /* Note to maintainer: profile should be png_bytep */ -#endif - -#if defined(PNG_sPLT_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_sPLT_tpp entries)); -#endif - -#if defined(PNG_sPLT_SUPPORTED) -extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_sPLT_tp entries, int nentries)); -#endif - -#if defined(PNG_TEXT_SUPPORTED) -/* png_get_text also returns the number of text chunks in *num_text */ -extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_textp *text_ptr, int *num_text)); -#endif - -/* - * Note while png_set_text() will accept a structure whose text, - * language, and translated keywords are NULL pointers, the structure - * returned by png_get_text will always contain regular - * zero-terminated C strings. They might be empty strings but - * they will never be NULL pointers. - */ - -#if defined(PNG_TEXT_SUPPORTED) -extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_textp text_ptr, int num_text)); -#endif - -#if defined(PNG_tIME_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_timep *mod_time)); -#endif - -#if defined(PNG_tIME_SUPPORTED) -extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_timep mod_time)); -#endif - -#if defined(PNG_tRNS_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_bytep *trans, int *num_trans, - png_color_16p *trans_values)); -#endif - -#if defined(PNG_tRNS_SUPPORTED) -extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_bytep trans, int num_trans, - png_color_16p trans_values)); -#endif - -#if defined(PNG_tRNS_SUPPORTED) -#endif - -#if defined(PNG_sCAL_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, - png_infop info_ptr, int *unit, double *width, double *height)); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, - png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); -#endif -#endif -#endif /* PNG_sCAL_SUPPORTED */ - -#if defined(PNG_sCAL_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, - png_infop info_ptr, int unit, double width, double height)); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, - png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); -#endif -#endif -#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ - -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) -/* provide a list of chunks and how they are to be handled, if the built-in - handling or default unknown chunk handling is not desired. Any chunks not - listed will be handled in the default manner. The IHDR and IEND chunks - must not be listed. - keep = 0: follow default behaviour - = 1: do not keep - = 2: keep only if safe-to-copy - = 3: keep even if unsafe-to-copy -*/ -extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp - png_ptr, int keep, png_bytep chunk_list, int num_chunks)); -extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); -extern PNG_EXPORT(void, png_set_unknown_chunk_location) - PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); -extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp - png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); -#endif -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED -PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep - chunk_name)); -#endif - -/* Png_free_data() will turn off the "valid" flag for anything it frees. - If you need to turn it off for a chunk that your application has freed, - you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ -extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, - png_infop info_ptr, int mask)); - -#if defined(PNG_INFO_IMAGE_SUPPORTED) -/* The "params" pointer is currently not used and is for future expansion. */ -extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, - png_infop info_ptr, - int transforms, - png_voidp params)); -extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, - png_infop info_ptr, - int transforms, - png_voidp params)); -#endif - -/* Define PNG_DEBUG at compile time for debugging information. Higher - * numbers for PNG_DEBUG mean more debugging information. This has - * only been added since version 0.95 so it is not implemented throughout - * libpng yet, but more support will be added as needed. - */ -#ifdef PNG_DEBUG -#if (PNG_DEBUG > 0) -#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) -#include -#if (PNG_DEBUG > 1) -#define png_debug(l,m) _RPT0(_CRT_WARN,m) -#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m,p1) -#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2) -#endif -#else /* PNG_DEBUG_FILE || !_MSC_VER */ -#ifndef PNG_DEBUG_FILE -#define PNG_DEBUG_FILE stderr -#endif /* PNG_DEBUG_FILE */ -#if (PNG_DEBUG > 1) -#define png_debug(l,m) \ -{ \ - int num_tabs=l; \ - fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ - (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ -} -#define png_debug1(l,m,p1) \ -{ \ - int num_tabs=l; \ - fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ - (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ -} -#define png_debug2(l,m,p1,p2) \ -{ \ - int num_tabs=l; \ - fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ - (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ -} -#endif /* (PNG_DEBUG > 1) */ -#endif /* _MSC_VER */ -#endif /* (PNG_DEBUG > 0) */ -#endif /* PNG_DEBUG */ -#ifndef png_debug -#define png_debug(l, m) -#endif -#ifndef png_debug1 -#define png_debug1(l, m, p1) -#endif -#ifndef png_debug2 -#define png_debug2(l, m, p1, p2) -#endif - -extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); -extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); -extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); -extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); - -#ifdef PNG_MNG_FEATURES_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp - png_ptr, png_uint_32 mng_features_permitted)); -#endif - -/* For use in png_set_keep_unknown, added to version 1.2.6 */ -#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 -#define PNG_HANDLE_CHUNK_NEVER 1 -#define PNG_HANDLE_CHUNK_IF_SAFE 2 -#define PNG_HANDLE_CHUNK_ALWAYS 3 - -/* Added to version 1.2.0 */ -#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) -#if defined(PNG_MMX_CODE_SUPPORTED) -#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ -#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ -#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 -#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 -#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 -#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 -#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 -#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 -#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ - -#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ - | PNG_ASM_FLAG_MMX_READ_INTERLACE \ - | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ - | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ - | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ - | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) -#define PNG_MMX_WRITE_FLAGS ( 0 ) - -#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ - | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ - | PNG_MMX_READ_FLAGS \ - | PNG_MMX_WRITE_FLAGS ) - -#define PNG_SELECT_READ 1 -#define PNG_SELECT_WRITE 2 -#endif /* PNG_MMX_CODE_SUPPORTED */ - -#if !defined(PNG_1_0_X) -/* pngget.c */ -extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) - PNGARG((int flag_select, int *compilerID)); - -/* pngget.c */ -extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) - PNGARG((int flag_select)); - -/* pngget.c */ -extern PNG_EXPORT(png_uint_32,png_get_asm_flags) - PNGARG((png_structp png_ptr)); - -/* pngget.c */ -extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) - PNGARG((png_structp png_ptr)); - -/* pngget.c */ -extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) - PNGARG((png_structp png_ptr)); - -/* pngset.c */ -extern PNG_EXPORT(void,png_set_asm_flags) - PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); - -/* pngset.c */ -extern PNG_EXPORT(void,png_set_mmx_thresholds) - PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, - png_uint_32 mmx_rowbytes_threshold)); - -#endif /* PNG_1_0_X */ - -#if !defined(PNG_1_0_X) -/* png.c, pnggccrd.c, or pngvcrd.c */ -extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); -#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ - -/* Strip the prepended error numbers ("#nnn ") from error and warning - * messages before passing them to the error or warning handler. */ -#ifdef PNG_ERROR_NUMBERS_SUPPORTED -extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp - png_ptr, png_uint_32 strip_mode)); -#endif - -#endif /* PNG_1_0_X */ - -/* Added at libpng-1.2.6 */ -#ifdef PNG_SET_USER_LIMITS_SUPPORTED -extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp - png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); -extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp - png_ptr)); -extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp - png_ptr)); -#endif - -/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ - -#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED -/* With these routines we avoid an integer divide, which will be slower on - * most machines. However, it does take more operations than the corresponding - * divide method, so it may be slower on a few RISC systems. There are two - * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. - * - * Note that the rounding factors are NOT supposed to be the same! 128 and - * 32768 are correct for the NODIV code; 127 and 32767 are correct for the - * standard method. - * - * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] - */ - - /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ - -# define png_composite(composite, fg, alpha, bg) \ - { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ - + (png_uint_16)(bg)*(png_uint_16)(255 - \ - (png_uint_16)(alpha)) + (png_uint_16)128); \ - (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } - -# define png_composite_16(composite, fg, alpha, bg) \ - { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ - + (png_uint_32)(bg)*(png_uint_32)(65535L - \ - (png_uint_32)(alpha)) + (png_uint_32)32768L); \ - (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } - -#else /* standard method using integer division */ - -# define png_composite(composite, fg, alpha, bg) \ - (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ - (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ - (png_uint_16)127) / 255) - -# define png_composite_16(composite, fg, alpha, bg) \ - (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ - (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ - (png_uint_32)32767) / (png_uint_32)65535L) - -#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ - -/* Inline macros to do direct reads of bytes from the input buffer. These - * require that you are using an architecture that uses PNG byte ordering - * (MSB first) and supports unaligned data storage. I think that PowerPC - * in big-endian mode and 680x0 are the only ones that will support this. - * The x86 line of processors definitely do not. The png_get_int_32() - * routine also assumes we are using two's complement format for negative - * values, which is almost certainly true. - */ -#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) -# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) -# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) -# define png_get_int_32(buf) ( *((png_int_32p) (buf))) -#else -extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); -extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); -extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); -#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ -extern PNG_EXPORT(png_uint_32,png_get_uint_31) - PNGARG((png_structp png_ptr, png_bytep buf)); -/* No png_get_int_16 -- may be added if there's a real need for it. */ - -/* Place a 32-bit number into a buffer in PNG byte order (big-endian). - */ -extern PNG_EXPORT(void,png_save_uint_32) - PNGARG((png_bytep buf, png_uint_32 i)); -extern PNG_EXPORT(void,png_save_int_32) - PNGARG((png_bytep buf, png_int_32 i)); - -/* Place a 16-bit number into a buffer in PNG byte order. - * The parameter is declared unsigned int, not png_uint_16, - * just to avoid potential problems on pre-ANSI C compilers. - */ -extern PNG_EXPORT(void,png_save_uint_16) - PNGARG((png_bytep buf, unsigned int i)); -/* No png_save_int_16 -- may be added if there's a real need for it. */ - -/* ************************************************************************* */ - -/* These next functions are used internally in the code. They generally - * shouldn't be used unless you are writing code to add or replace some - * functionality in libpng. More information about most functions can - * be found in the files where the functions are located. - */ - - -/* Various modes of operation, that are visible to applications because - * they are used for unknown chunk location. - */ -#define PNG_HAVE_IHDR 0x01 -#define PNG_HAVE_PLTE 0x02 -#define PNG_HAVE_IDAT 0x04 -#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ -#define PNG_HAVE_IEND 0x10 - -#if defined(PNG_INTERNAL) - -/* More modes of operation. Note that after an init, mode is set to - * zero automatically when the structure is created. - */ -#define PNG_HAVE_gAMA 0x20 -#define PNG_HAVE_cHRM 0x40 -#define PNG_HAVE_sRGB 0x80 -#define PNG_HAVE_CHUNK_HEADER 0x100 -#define PNG_WROTE_tIME 0x200 -#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 -#define PNG_BACKGROUND_IS_GRAY 0x800 -#define PNG_HAVE_PNG_SIGNATURE 0x1000 -#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ - -/* flags for the transformations the PNG library does on the image data */ -#define PNG_BGR 0x0001 -#define PNG_INTERLACE 0x0002 -#define PNG_PACK 0x0004 -#define PNG_SHIFT 0x0008 -#define PNG_SWAP_BYTES 0x0010 -#define PNG_INVERT_MONO 0x0020 -#define PNG_DITHER 0x0040 -#define PNG_BACKGROUND 0x0080 -#define PNG_BACKGROUND_EXPAND 0x0100 - /* 0x0200 unused */ -#define PNG_16_TO_8 0x0400 -#define PNG_RGBA 0x0800 -#define PNG_EXPAND 0x1000 -#define PNG_GAMMA 0x2000 -#define PNG_GRAY_TO_RGB 0x4000 -#define PNG_FILLER 0x8000L -#define PNG_PACKSWAP 0x10000L -#define PNG_SWAP_ALPHA 0x20000L -#define PNG_STRIP_ALPHA 0x40000L -#define PNG_INVERT_ALPHA 0x80000L -#define PNG_USER_TRANSFORM 0x100000L -#define PNG_RGB_TO_GRAY_ERR 0x200000L -#define PNG_RGB_TO_GRAY_WARN 0x400000L -#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ - /* 0x800000L Unused */ -#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ -#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ - /* 0x4000000L unused */ - /* 0x8000000L unused */ - /* 0x10000000L unused */ - /* 0x20000000L unused */ - /* 0x40000000L unused */ - -/* flags for png_create_struct */ -#define PNG_STRUCT_PNG 0x0001 -#define PNG_STRUCT_INFO 0x0002 - -/* Scaling factor for filter heuristic weighting calculations */ -#define PNG_WEIGHT_SHIFT 8 -#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) -#define PNG_COST_SHIFT 3 -#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) - -/* flags for the png_ptr->flags rather than declaring a byte for each one */ -#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 -#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 -#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 -#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 -#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 -#define PNG_FLAG_ZLIB_FINISHED 0x0020 -#define PNG_FLAG_ROW_INIT 0x0040 -#define PNG_FLAG_FILLER_AFTER 0x0080 -#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 -#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 -#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 -#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 -#define PNG_FLAG_FREE_PLTE 0x1000 -#define PNG_FLAG_FREE_TRNS 0x2000 -#define PNG_FLAG_FREE_HIST 0x4000 -#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L -#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L -#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L -#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L -#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L -#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ -#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ - /* 0x800000L unused */ - /* 0x1000000L unused */ - /* 0x2000000L unused */ - /* 0x4000000L unused */ - /* 0x8000000L unused */ - /* 0x10000000L unused */ - /* 0x20000000L unused */ - /* 0x40000000L unused */ - -#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ - PNG_FLAG_CRC_ANCILLARY_NOWARN) - -#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ - PNG_FLAG_CRC_CRITICAL_IGNORE) - -#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ - PNG_FLAG_CRC_CRITICAL_MASK) - -/* save typing and make code easier to understand */ - -#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ - abs((int)((c1).green) - (int)((c2).green)) + \ - abs((int)((c1).blue) - (int)((c2).blue))) - -/* Added to libpng-1.2.6 JB */ -#define PNG_ROWBYTES(pixel_bits, width) \ - ((pixel_bits) >= 8 ? \ - ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ - (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) - -/* PNG_OUT_OF_RANGE returns true if value is outside the range - ideal-delta..ideal+delta. Each argument is evaluated twice. - "ideal" and "delta" should be constants, normally simple - integers, "value" a variable. Added to libpng-1.2.6 JB */ -#define PNG_OUT_OF_RANGE(value, ideal, delta) \ - ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) - -/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ -#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) -/* place to hold the signature string for a PNG file. */ -#ifdef PNG_USE_GLOBAL_ARRAYS - PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8]; -#else -#endif -#endif /* PNG_NO_EXTERN */ - -/* Constant strings for known chunk types. If you need to add a chunk, - * define the name here, and add an invocation of the macro in png.c and - * wherever it's needed. - */ -#define PNG_IHDR png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} -#define PNG_IDAT png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} -#define PNG_IEND png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} -#define PNG_PLTE png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} -#define PNG_bKGD png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} -#define PNG_cHRM png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} -#define PNG_gAMA png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} -#define PNG_hIST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} -#define PNG_iCCP png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} -#define PNG_iTXt png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} -#define PNG_oFFs png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} -#define PNG_pCAL png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} -#define PNG_sCAL png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} -#define PNG_pHYs png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} -#define PNG_sBIT png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} -#define PNG_sPLT png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} -#define PNG_sRGB png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} -#define PNG_tEXt png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} -#define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} -#define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} -#define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} - -#ifdef PNG_USE_GLOBAL_ARRAYS -PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5]; -#endif /* PNG_USE_GLOBAL_ARRAYS */ - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Initialize png_ptr struct for reading, and allocate any other memory. - * (old interface - DEPRECATED - use png_create_read_struct instead). - */ -extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); -#undef png_read_init -#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ - PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); -#endif - -extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, - png_const_charp user_png_ver, png_size_t png_struct_size)); -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, - png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t - png_info_size)); -#endif - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Initialize png_ptr struct for writing, and allocate any other memory. - * (old interface - DEPRECATED - use png_create_write_struct instead). - */ -extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); -#undef png_write_init -#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ - PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); -#endif - -extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, - png_const_charp user_png_ver, png_size_t png_struct_size)); -extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, - png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t - png_info_size)); - -/* Allocate memory for an internal libpng struct */ -PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); - -/* Free memory from internal libpng struct */ -PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); - -PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr - malloc_fn, png_voidp mem_ptr)); -PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, - png_free_ptr free_fn, png_voidp mem_ptr)); - -/* Free any memory that info_ptr points to and reset struct. */ -PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, - png_infop info_ptr)); - -#ifndef PNG_1_0_X -/* Function to allocate memory for zlib. */ -PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); - -/* Function to free memory for zlib */ -PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); - -#ifdef PNG_SIZE_T -/* Function to convert a sizeof an item to png_sizeof item */ - PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); -#endif - -/* Next four functions are used internally as callbacks. PNGAPI is required - * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ - -PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, - png_bytep data, png_size_t length)); - -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, - png_bytep buffer, png_size_t length)); -#endif - -PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, - png_bytep data, png_size_t length)); - -#if defined(PNG_WRITE_FLUSH_SUPPORTED) -#if !defined(PNG_NO_STDIO) -PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); -#endif -#endif -#else /* PNG_1_0_X */ -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, - png_bytep buffer, png_size_t length)); -#endif -#endif /* PNG_1_0_X */ - -/* Reset the CRC variable */ -PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); - -/* Write the "data" buffer to whatever output you are using. */ -PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, - png_size_t length)); - -/* Read data from whatever input you are using into the "data" buffer */ -PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, - png_size_t length)); - -/* Read bytes into buf, and update png_ptr->crc */ -PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, - png_size_t length)); - -/* Decompress data in a chunk that uses compression */ -#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ - defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) -PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr, - int comp_type, png_charp chunkdata, png_size_t chunklength, - png_size_t prefix_length, png_size_t *data_length)); -#endif - -/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ -PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); - -/* Read the CRC from the file and compare it to the libpng calculated CRC */ -PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); - -/* Calculate the CRC over a section of data. Note that we are only - * passing a maximum of 64K on systems that have this as a memory limit, - * since this is the maximum buffer size we can specify. - */ -PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, - png_size_t length)); - -#if defined(PNG_WRITE_FLUSH_SUPPORTED) -PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); -#endif - -/* simple function to write the signature */ -PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); - -/* write various chunks */ - -/* Write the IHDR chunk, and update the png_struct with the necessary - * information. - */ -PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, - png_uint_32 height, - int bit_depth, int color_type, int compression_method, int filter_method, - int interlace_method)); - -PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, - png_uint_32 num_pal)); - -PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, - png_size_t length)); - -PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); - -#if defined(PNG_WRITE_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point - file_gamma)); -#endif -#endif - -#if defined(PNG_WRITE_sBIT_SUPPORTED) -PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, - int color_type)); -#endif - -#if defined(PNG_WRITE_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, - double white_x, double white_y, - double red_x, double red_y, double green_x, double green_y, - double blue_x, double blue_y)); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, - png_fixed_point int_white_x, png_fixed_point int_white_y, - png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point - int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, - png_fixed_point int_blue_y)); -#endif -#endif - -#if defined(PNG_WRITE_sRGB_SUPPORTED) -PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, - int intent)); -#endif - -#if defined(PNG_WRITE_iCCP_SUPPORTED) -PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, - png_charp name, int compression_type, - png_charp profile, int proflen)); - /* Note to maintainer: profile should be png_bytep */ -#endif - -#if defined(PNG_WRITE_sPLT_SUPPORTED) -PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, - png_sPLT_tp palette)); -#endif - -#if defined(PNG_WRITE_tRNS_SUPPORTED) -PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, - png_color_16p values, int number, int color_type)); -#endif - -#if defined(PNG_WRITE_bKGD_SUPPORTED) -PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, - png_color_16p values, int color_type)); -#endif - -#if defined(PNG_WRITE_hIST_SUPPORTED) -PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, - int num_hist)); -#endif - -#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ - defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) -PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, - png_charp key, png_charpp new_key)); -#endif - -#if defined(PNG_WRITE_tEXt_SUPPORTED) -PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, - png_charp text, png_size_t text_len)); -#endif - -#if defined(PNG_WRITE_zTXt_SUPPORTED) -PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, - png_charp text, png_size_t text_len, int compression)); -#endif - -#if defined(PNG_WRITE_iTXt_SUPPORTED) -PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, - int compression, png_charp key, png_charp lang, png_charp lang_key, - png_charp text)); -#endif - -#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ -PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, - png_infop info_ptr, png_textp text_ptr, int num_text)); -#endif - -#if defined(PNG_WRITE_oFFs_SUPPORTED) -PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, - png_int_32 x_offset, png_int_32 y_offset, int unit_type)); -#endif - -#if defined(PNG_WRITE_pCAL_SUPPORTED) -PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, - png_int_32 X0, png_int_32 X1, int type, int nparams, - png_charp units, png_charpp params)); -#endif - -#if defined(PNG_WRITE_pHYs_SUPPORTED) -PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, - png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, - int unit_type)); -#endif - -#if defined(PNG_WRITE_tIME_SUPPORTED) -PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, - png_timep mod_time)); -#endif - -#if defined(PNG_WRITE_sCAL_SUPPORTED) -#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) -PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, - int unit, double width, double height)); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, - int unit, png_charp width, png_charp height)); -#endif -#endif -#endif - -/* Called when finished processing a row of data */ -PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); - -/* Internal use only. Called before first row of data */ -PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); - -#if defined(PNG_READ_GAMMA_SUPPORTED) -PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); -#endif - -/* combine a row of data, dealing with alpha, etc. if requested */ -PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, - int mask)); - -#if defined(PNG_READ_INTERLACING_SUPPORTED) -/* expand an interlaced row */ -/* OLD pre-1.0.9 interface: -PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, - png_bytep row, int pass, png_uint_32 transformations)); - */ -PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); -#endif - -/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ - -#if defined(PNG_WRITE_INTERLACING_SUPPORTED) -/* grab pixels out of a row for an interlaced pass */ -PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, - png_bytep row, int pass)); -#endif - -/* unfilter a row */ -PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, - png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); - -/* Choose the best filter to use and filter the row data */ -PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, - png_row_infop row_info)); - -/* Write out the filtered row. */ -PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, - png_bytep filtered_row)); -/* finish a row while reading, dealing with interlacing passes, etc. */ -PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); - -/* initialize the row buffers, etc. */ -PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); -/* optional call to update the users info structure */ -PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, - png_infop info_ptr)); - -/* these are the functions that do the transformations */ -#if defined(PNG_READ_FILLER_SUPPORTED) -PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, - png_bytep row, png_uint_32 filler, png_uint_32 flags)); -#endif - -#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ - defined(PNG_READ_STRIP_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, - png_bytep row, png_uint_32 flags)); -#endif - -#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) -PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) -PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop - row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) -PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_READ_PACK_SUPPORTED) -PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_SHIFT_SUPPORTED) -PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, - png_color_8p sig_bits)); -#endif - -#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) -PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_16_TO_8_SUPPORTED) -PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_DITHER_SUPPORTED) -PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, - png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); - -# if defined(PNG_CORRECT_PALETTE_SUPPORTED) -PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, - png_colorp palette, int num_palette)); -# endif -#endif - -#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_WRITE_PACK_SUPPORTED) -PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, - png_bytep row, png_uint_32 bit_depth)); -#endif - -#if defined(PNG_WRITE_SHIFT_SUPPORTED) -PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, - png_color_8p bit_depth)); -#endif - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) -#if defined(PNG_READ_GAMMA_SUPPORTED) -PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, - png_color_16p trans_values, png_color_16p background, - png_color_16p background_1, - png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, - png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, - png_uint_16pp gamma_16_to_1, int gamma_shift)); -#else -PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, - png_color_16p trans_values, png_color_16p background)); -#endif -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) -PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, - png_bytep gamma_table, png_uint_16pp gamma_16_table, - int gamma_shift)); -#endif - -#if defined(PNG_READ_EXPAND_SUPPORTED) -PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, - png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); -PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, - png_bytep row, png_color_16p trans_value)); -#endif - -/* The following decodes the appropriate chunks, and does error correction, - * then calls the appropriate callback for the chunk if it is valid. - */ - -/* decode the IHDR chunk */ -PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); - -#if defined(PNG_READ_bKGD_SUPPORTED) -PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_cHRM_SUPPORTED) -PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_gAMA_SUPPORTED) -PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_hIST_SUPPORTED) -PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_iCCP_SUPPORTED) -extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif /* PNG_READ_iCCP_SUPPORTED */ - -#if defined(PNG_READ_iTXt_SUPPORTED) -PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_oFFs_SUPPORTED) -PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_pCAL_SUPPORTED) -PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_pHYs_SUPPORTED) -PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_sBIT_SUPPORTED) -PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_sCAL_SUPPORTED) -PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_sPLT_SUPPORTED) -extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif /* PNG_READ_sPLT_SUPPORTED */ - -#if defined(PNG_READ_sRGB_SUPPORTED) -PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_tEXt_SUPPORTED) -PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_tIME_SUPPORTED) -PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_tRNS_SUPPORTED) -PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_zTXt_SUPPORTED) -PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); - -PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, - png_bytep chunk_name)); - -/* handle the transformations for reading and writing */ -PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); - -PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); - -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, - png_uint_32 length)); -PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, - png_bytep buffer, png_size_t buffer_length)); -PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, - png_bytep buffer, png_size_t buffer_length)); -PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); -PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); -PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); -#if defined(PNG_READ_tEXt_SUPPORTED) -PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); -PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, - png_infop info_ptr)); -#endif -#if defined(PNG_READ_zTXt_SUPPORTED) -PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); -PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, - png_infop info_ptr)); -#endif -#if defined(PNG_READ_iTXt_SUPPORTED) -PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); -PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, - png_infop info_ptr)); -#endif - -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ - -#ifdef PNG_MNG_FEATURES_SUPPORTED -PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, - png_bytep row)); -PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) -#if defined(PNG_MMX_CODE_SUPPORTED) -/* png.c */ /* PRIVATE */ -PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); -#endif -#endif - -#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) -PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -#if defined(PNG_pHYs_SUPPORTED) -PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, -png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); -#endif /* PNG_pHYs_SUPPORTED */ -#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ - -/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ - -#endif /* PNG_INTERNAL */ - -#ifdef __cplusplus -} -#endif - -#endif /* PNG_VERSION_INFO_ONLY */ -/* do not put anything past this line */ -#endif /* PNG_H */ diff --git a/extlib/pngconf.h b/extlib/pngconf.h deleted file mode 100644 index d1e2995..0000000 --- a/extlib/pngconf.h +++ /dev/null @@ -1,1481 +0,0 @@ - -/* pngconf.h - machine configurable file for libpng - * - * libpng version 1.2.29 - May 8, 2008 - * For conditions of distribution and use, see copyright notice in png.h - * Copyright (c) 1998-2008 Glenn Randers-Pehrson - * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) - * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) - */ - -/* Any machine specific code is near the front of this file, so if you - * are configuring libpng for a machine, you may want to read the section - * starting here down to where it starts to typedef png_color, png_text, - * and png_info. - */ - -#ifndef PNGCONF_H -#define PNGCONF_H - -#define PNG_1_2_X - -/* - * PNG_USER_CONFIG has to be defined on the compiler command line. This - * includes the resource compiler for Windows DLL configurations. - */ -#ifdef PNG_USER_CONFIG -# ifndef PNG_USER_PRIVATEBUILD -# define PNG_USER_PRIVATEBUILD -# endif -#include "pngusr.h" -#endif - -/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */ -#ifdef PNG_CONFIGURE_LIBPNG -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#endif - -/* - * Added at libpng-1.2.8 - * - * If you create a private DLL you need to define in "pngusr.h" the followings: - * #define PNG_USER_PRIVATEBUILD - * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." - * #define PNG_USER_DLLFNAME_POSTFIX - * e.g. // private DLL "libpng13gx.dll" - * #define PNG_USER_DLLFNAME_POSTFIX "gx" - * - * The following macros are also at your disposal if you want to complete the - * DLL VERSIONINFO structure. - * - PNG_USER_VERSIONINFO_COMMENTS - * - PNG_USER_VERSIONINFO_COMPANYNAME - * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS - */ - -#ifdef __STDC__ -#ifdef SPECIALBUILD -# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ - are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") -#endif - -#ifdef PRIVATEBUILD -# pragma message("PRIVATEBUILD is deprecated.\ - Use PNG_USER_PRIVATEBUILD instead.") -# define PNG_USER_PRIVATEBUILD PRIVATEBUILD -#endif -#endif /* __STDC__ */ - -#ifndef PNG_VERSION_INFO_ONLY - -/* End of material added to libpng-1.2.8 */ - -/* Added at libpng-1.2.19, removed at libpng-1.2.20 because it caused trouble - Restored at libpng-1.2.21 */ -#if !defined(PNG_NO_WARN_UNINITIALIZED_ROW) && \ - !defined(PNG_WARN_UNINITIALIZED_ROW) -# define PNG_WARN_UNINITIALIZED_ROW 1 -#endif -/* End of material added at libpng-1.2.19/1.2.21 */ - -/* This is the size of the compression buffer, and thus the size of - * an IDAT chunk. Make this whatever size you feel is best for your - * machine. One of these will be allocated per png_struct. When this - * is full, it writes the data to the disk, and does some other - * calculations. Making this an extremely small size will slow - * the library down, but you may want to experiment to determine - * where it becomes significant, if you are concerned with memory - * usage. Note that zlib allocates at least 32Kb also. For readers, - * this describes the size of the buffer available to read the data in. - * Unless this gets smaller than the size of a row (compressed), - * it should not make much difference how big this is. - */ - -#ifndef PNG_ZBUF_SIZE -# define PNG_ZBUF_SIZE 8192 -#endif - -/* Enable if you want a write-only libpng */ - -#ifndef PNG_NO_READ_SUPPORTED -# define PNG_READ_SUPPORTED -#endif - -/* Enable if you want a read-only libpng */ - -#ifndef PNG_NO_WRITE_SUPPORTED -# define PNG_WRITE_SUPPORTED -#endif - -/* Enabled by default in 1.2.0. You can disable this if you don't need to - support PNGs that are embedded in MNG datastreams */ -#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) -# ifndef PNG_MNG_FEATURES_SUPPORTED -# define PNG_MNG_FEATURES_SUPPORTED -# endif -#endif - -#ifndef PNG_NO_FLOATING_POINT_SUPPORTED -# ifndef PNG_FLOATING_POINT_SUPPORTED -# define PNG_FLOATING_POINT_SUPPORTED -# endif -#endif - -/* If you are running on a machine where you cannot allocate more - * than 64K of memory at once, uncomment this. While libpng will not - * normally need that much memory in a chunk (unless you load up a very - * large file), zlib needs to know how big of a chunk it can use, and - * libpng thus makes sure to check any memory allocation to verify it - * will fit into memory. -#define PNG_MAX_MALLOC_64K - */ -#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) -# define PNG_MAX_MALLOC_64K -#endif - -/* Special munging to support doing things the 'cygwin' way: - * 'Normal' png-on-win32 defines/defaults: - * PNG_BUILD_DLL -- building dll - * PNG_USE_DLL -- building an application, linking to dll - * (no define) -- building static library, or building an - * application and linking to the static lib - * 'Cygwin' defines/defaults: - * PNG_BUILD_DLL -- (ignored) building the dll - * (no define) -- (ignored) building an application, linking to the dll - * PNG_STATIC -- (ignored) building the static lib, or building an - * application that links to the static lib. - * ALL_STATIC -- (ignored) building various static libs, or building an - * application that links to the static libs. - * Thus, - * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and - * this bit of #ifdefs will define the 'correct' config variables based on - * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but - * unnecessary. - * - * Also, the precedence order is: - * ALL_STATIC (since we can't #undef something outside our namespace) - * PNG_BUILD_DLL - * PNG_STATIC - * (nothing) == PNG_USE_DLL - * - * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent - * of auto-import in binutils, we no longer need to worry about - * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, - * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes - * to __declspec() stuff. However, we DO need to worry about - * PNG_BUILD_DLL and PNG_STATIC because those change some defaults - * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. - */ -#if defined(__CYGWIN__) -# if defined(ALL_STATIC) -# if defined(PNG_BUILD_DLL) -# undef PNG_BUILD_DLL -# endif -# if defined(PNG_USE_DLL) -# undef PNG_USE_DLL -# endif -# if defined(PNG_DLL) -# undef PNG_DLL -# endif -# if !defined(PNG_STATIC) -# define PNG_STATIC -# endif -# else -# if defined (PNG_BUILD_DLL) -# if defined(PNG_STATIC) -# undef PNG_STATIC -# endif -# if defined(PNG_USE_DLL) -# undef PNG_USE_DLL -# endif -# if !defined(PNG_DLL) -# define PNG_DLL -# endif -# else -# if defined(PNG_STATIC) -# if defined(PNG_USE_DLL) -# undef PNG_USE_DLL -# endif -# if defined(PNG_DLL) -# undef PNG_DLL -# endif -# else -# if !defined(PNG_USE_DLL) -# define PNG_USE_DLL -# endif -# if !defined(PNG_DLL) -# define PNG_DLL -# endif -# endif -# endif -# endif -#endif - -/* This protects us against compilers that run on a windowing system - * and thus don't have or would rather us not use the stdio types: - * stdin, stdout, and stderr. The only one currently used is stderr - * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will - * prevent these from being compiled and used. #defining PNG_NO_STDIO - * will also prevent these, plus will prevent the entire set of stdio - * macros and functions (FILE *, printf, etc.) from being compiled and used, - * unless (PNG_DEBUG > 0) has been #defined. - * - * #define PNG_NO_CONSOLE_IO - * #define PNG_NO_STDIO - */ - -#if defined(_WIN32_WCE) -# include - /* Console I/O functions are not supported on WindowsCE */ -# define PNG_NO_CONSOLE_IO -# ifdef PNG_DEBUG -# undef PNG_DEBUG -# endif -#endif - -#ifdef PNG_BUILD_DLL -# ifndef PNG_CONSOLE_IO_SUPPORTED -# ifndef PNG_NO_CONSOLE_IO -# define PNG_NO_CONSOLE_IO -# endif -# endif -#endif - -# ifdef PNG_NO_STDIO -# ifndef PNG_NO_CONSOLE_IO -# define PNG_NO_CONSOLE_IO -# endif -# ifdef PNG_DEBUG -# if (PNG_DEBUG > 0) -# include -# endif -# endif -# else -# if !defined(_WIN32_WCE) -/* "stdio.h" functions are not supported on WindowsCE */ -# include -# endif -# endif - -/* This macro protects us against machines that don't have function - * prototypes (ie K&R style headers). If your compiler does not handle - * function prototypes, define this macro and use the included ansi2knr. - * I've always been able to use _NO_PROTO as the indicator, but you may - * need to drag the empty declaration out in front of here, or change the - * ifdef to suit your own needs. - */ -#ifndef PNGARG - -#ifdef OF /* zlib prototype munger */ -# define PNGARG(arglist) OF(arglist) -#else - -#ifdef _NO_PROTO -# define PNGARG(arglist) () -# ifndef PNG_TYPECAST_NULL -# define PNG_TYPECAST_NULL -# endif -#else -# define PNGARG(arglist) arglist -#endif /* _NO_PROTO */ - - -#endif /* OF */ - -#endif /* PNGARG */ - -/* Try to determine if we are compiling on a Mac. Note that testing for - * just __MWERKS__ is not good enough, because the Codewarrior is now used - * on non-Mac platforms. - */ -#ifndef MACOS -# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ - defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) -# define MACOS -# endif -#endif - -/* enough people need this for various reasons to include it here */ -#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) -# include -#endif - -#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) -# define PNG_SETJMP_SUPPORTED -#endif - -#ifdef PNG_SETJMP_SUPPORTED -/* This is an attempt to force a single setjmp behaviour on Linux. If - * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. - */ - -# ifdef __linux__ -# ifdef _BSD_SOURCE -# define PNG_SAVE_BSD_SOURCE -# undef _BSD_SOURCE -# endif -# ifdef _SETJMP_H - /* If you encounter a compiler error here, see the explanation - * near the end of INSTALL. - */ - __pngconf.h__ already includes setjmp.h; - __dont__ include it again.; -# endif -# endif /* __linux__ */ - - /* include setjmp.h for error handling */ -# include - -# ifdef __linux__ -# ifdef PNG_SAVE_BSD_SOURCE -# ifndef _BSD_SOURCE -# define _BSD_SOURCE -# endif -# undef PNG_SAVE_BSD_SOURCE -# endif -# endif /* __linux__ */ -#endif /* PNG_SETJMP_SUPPORTED */ - -#ifdef BSD -# include -#else -# include -#endif - -/* Other defines for things like memory and the like can go here. */ -#ifdef PNG_INTERNAL - -#include - -/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which - * aren't usually used outside the library (as far as I know), so it is - * debatable if they should be exported at all. In the future, when it is - * possible to have run-time registry of chunk-handling functions, some of - * these will be made available again. -#define PNG_EXTERN extern - */ -#define PNG_EXTERN - -/* Other defines specific to compilers can go here. Try to keep - * them inside an appropriate ifdef/endif pair for portability. - */ - -#if defined(PNG_FLOATING_POINT_SUPPORTED) -# if defined(MACOS) - /* We need to check that hasn't already been included earlier - * as it seems it doesn't agree with , yet we should really use - * if possible. - */ -# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) -# include -# endif -# else -# include -# endif -# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) - /* Amiga SAS/C: We must include builtin FPU functions when compiling using - * MATH=68881 - */ -# include -# endif -#endif - -/* Codewarrior on NT has linking problems without this. */ -#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) -# define PNG_ALWAYS_EXTERN -#endif - -/* This provides the non-ANSI (far) memory allocation routines. */ -#if defined(__TURBOC__) && defined(__MSDOS__) -# include -# include -#endif - -/* I have no idea why is this necessary... */ -#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ - defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) -# include -#endif - -/* This controls how fine the dithering gets. As this allocates - * a largish chunk of memory (32K), those who are not as concerned - * with dithering quality can decrease some or all of these. - */ -#ifndef PNG_DITHER_RED_BITS -# define PNG_DITHER_RED_BITS 5 -#endif -#ifndef PNG_DITHER_GREEN_BITS -# define PNG_DITHER_GREEN_BITS 5 -#endif -#ifndef PNG_DITHER_BLUE_BITS -# define PNG_DITHER_BLUE_BITS 5 -#endif - -/* This controls how fine the gamma correction becomes when you - * are only interested in 8 bits anyway. Increasing this value - * results in more memory being used, and more pow() functions - * being called to fill in the gamma tables. Don't set this value - * less then 8, and even that may not work (I haven't tested it). - */ - -#ifndef PNG_MAX_GAMMA_8 -# define PNG_MAX_GAMMA_8 11 -#endif - -/* This controls how much a difference in gamma we can tolerate before - * we actually start doing gamma conversion. - */ -#ifndef PNG_GAMMA_THRESHOLD -# define PNG_GAMMA_THRESHOLD 0.05 -#endif - -#endif /* PNG_INTERNAL */ - -/* The following uses const char * instead of char * for error - * and warning message functions, so some compilers won't complain. - * If you do not want to use const, define PNG_NO_CONST here. - */ - -#ifndef PNG_NO_CONST -# define PNG_CONST const -#else -# define PNG_CONST -#endif - -/* The following defines give you the ability to remove code from the - * library that you will not be using. I wish I could figure out how to - * automate this, but I can't do that without making it seriously hard - * on the users. So if you are not using an ability, change the #define - * to and #undef, and that part of the library will not be compiled. If - * your linker can't find a function, you may want to make sure the - * ability is defined here. Some of these depend upon some others being - * defined. I haven't figured out all the interactions here, so you may - * have to experiment awhile to get everything to compile. If you are - * creating or using a shared library, you probably shouldn't touch this, - * as it will affect the size of the structures, and this will cause bad - * things to happen if the library and/or application ever change. - */ - -/* Any features you will not be using can be undef'ed here */ - -/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user - * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS - * on the compile line, then pick and choose which ones to define without - * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED - * if you only want to have a png-compliant reader/writer but don't need - * any of the extra transformations. This saves about 80 kbytes in a - * typical installation of the library. (PNG_NO_* form added in version - * 1.0.1c, for consistency) - */ - -/* The size of the png_text structure changed in libpng-1.0.6 when - * iTXt support was added. iTXt support was turned off by default through - * libpng-1.2.x, to support old apps that malloc the png_text structure - * instead of calling png_set_text() and letting libpng malloc it. It - * was turned on by default in libpng-1.3.0. - */ - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -# ifndef PNG_NO_iTXt_SUPPORTED -# define PNG_NO_iTXt_SUPPORTED -# endif -# ifndef PNG_NO_READ_iTXt -# define PNG_NO_READ_iTXt -# endif -# ifndef PNG_NO_WRITE_iTXt -# define PNG_NO_WRITE_iTXt -# endif -#endif - -#if !defined(PNG_NO_iTXt_SUPPORTED) -# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) -# define PNG_READ_iTXt -# endif -# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) -# define PNG_WRITE_iTXt -# endif -#endif - -/* The following support, added after version 1.0.0, can be turned off here en - * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility - * with old applications that require the length of png_struct and png_info - * to remain unchanged. - */ - -#ifdef PNG_LEGACY_SUPPORTED -# define PNG_NO_FREE_ME -# define PNG_NO_READ_UNKNOWN_CHUNKS -# define PNG_NO_WRITE_UNKNOWN_CHUNKS -# define PNG_NO_READ_USER_CHUNKS -# define PNG_NO_READ_iCCP -# define PNG_NO_WRITE_iCCP -# define PNG_NO_READ_iTXt -# define PNG_NO_WRITE_iTXt -# define PNG_NO_READ_sCAL -# define PNG_NO_WRITE_sCAL -# define PNG_NO_READ_sPLT -# define PNG_NO_WRITE_sPLT -# define PNG_NO_INFO_IMAGE -# define PNG_NO_READ_RGB_TO_GRAY -# define PNG_NO_READ_USER_TRANSFORM -# define PNG_NO_WRITE_USER_TRANSFORM -# define PNG_NO_USER_MEM -# define PNG_NO_READ_EMPTY_PLTE -# define PNG_NO_MNG_FEATURES -# define PNG_NO_FIXED_POINT_SUPPORTED -#endif - -/* Ignore attempt to turn off both floating and fixed point support */ -#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ - !defined(PNG_NO_FIXED_POINT_SUPPORTED) -# define PNG_FIXED_POINT_SUPPORTED -#endif - -#ifndef PNG_NO_FREE_ME -# define PNG_FREE_ME_SUPPORTED -#endif - -#if defined(PNG_READ_SUPPORTED) - -#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ - !defined(PNG_NO_READ_TRANSFORMS) -# define PNG_READ_TRANSFORMS_SUPPORTED -#endif - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED -# ifndef PNG_NO_READ_EXPAND -# define PNG_READ_EXPAND_SUPPORTED -# endif -# ifndef PNG_NO_READ_SHIFT -# define PNG_READ_SHIFT_SUPPORTED -# endif -# ifndef PNG_NO_READ_PACK -# define PNG_READ_PACK_SUPPORTED -# endif -# ifndef PNG_NO_READ_BGR -# define PNG_READ_BGR_SUPPORTED -# endif -# ifndef PNG_NO_READ_SWAP -# define PNG_READ_SWAP_SUPPORTED -# endif -# ifndef PNG_NO_READ_PACKSWAP -# define PNG_READ_PACKSWAP_SUPPORTED -# endif -# ifndef PNG_NO_READ_INVERT -# define PNG_READ_INVERT_SUPPORTED -# endif -# ifndef PNG_NO_READ_DITHER -# define PNG_READ_DITHER_SUPPORTED -# endif -# ifndef PNG_NO_READ_BACKGROUND -# define PNG_READ_BACKGROUND_SUPPORTED -# endif -# ifndef PNG_NO_READ_16_TO_8 -# define PNG_READ_16_TO_8_SUPPORTED -# endif -# ifndef PNG_NO_READ_FILLER -# define PNG_READ_FILLER_SUPPORTED -# endif -# ifndef PNG_NO_READ_GAMMA -# define PNG_READ_GAMMA_SUPPORTED -# endif -# ifndef PNG_NO_READ_GRAY_TO_RGB -# define PNG_READ_GRAY_TO_RGB_SUPPORTED -# endif -# ifndef PNG_NO_READ_SWAP_ALPHA -# define PNG_READ_SWAP_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_READ_INVERT_ALPHA -# define PNG_READ_INVERT_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_READ_STRIP_ALPHA -# define PNG_READ_STRIP_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_READ_USER_TRANSFORM -# define PNG_READ_USER_TRANSFORM_SUPPORTED -# endif -# ifndef PNG_NO_READ_RGB_TO_GRAY -# define PNG_READ_RGB_TO_GRAY_SUPPORTED -# endif -#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ - -#if !defined(PNG_NO_PROGRESSIVE_READ) && \ - !defined(PNG_PROGRESSIVE_READ_SUPPORTED) /* if you don't do progressive */ -# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ -#endif /* about interlacing capability! You'll */ - /* still have interlacing unless you change the following line: */ - -#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */ - -#ifndef PNG_NO_READ_COMPOSITE_NODIV -# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ -# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ -# endif -#endif - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Deprecated, will be removed from version 2.0.0. - Use PNG_MNG_FEATURES_SUPPORTED instead. */ -#ifndef PNG_NO_READ_EMPTY_PLTE -# define PNG_READ_EMPTY_PLTE_SUPPORTED -#endif -#endif - -#endif /* PNG_READ_SUPPORTED */ - -#if defined(PNG_WRITE_SUPPORTED) - -# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ - !defined(PNG_NO_WRITE_TRANSFORMS) -# define PNG_WRITE_TRANSFORMS_SUPPORTED -#endif - -#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED -# ifndef PNG_NO_WRITE_SHIFT -# define PNG_WRITE_SHIFT_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_PACK -# define PNG_WRITE_PACK_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_BGR -# define PNG_WRITE_BGR_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_SWAP -# define PNG_WRITE_SWAP_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_PACKSWAP -# define PNG_WRITE_PACKSWAP_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_INVERT -# define PNG_WRITE_INVERT_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_FILLER -# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ -# endif -# ifndef PNG_NO_WRITE_SWAP_ALPHA -# define PNG_WRITE_SWAP_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_INVERT_ALPHA -# define PNG_WRITE_INVERT_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_USER_TRANSFORM -# define PNG_WRITE_USER_TRANSFORM_SUPPORTED -# endif -#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ - -#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ - !defined(PNG_WRITE_INTERLACING_SUPPORTED) -#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant - encoders, but can cause trouble - if left undefined */ -#endif - -#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ - !defined(PNG_WRITE_WEIGHTED_FILTER) && \ - defined(PNG_FLOATING_POINT_SUPPORTED) -# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED -#endif - -#ifndef PNG_NO_WRITE_FLUSH -# define PNG_WRITE_FLUSH_SUPPORTED -#endif - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ -#ifndef PNG_NO_WRITE_EMPTY_PLTE -# define PNG_WRITE_EMPTY_PLTE_SUPPORTED -#endif -#endif - -#endif /* PNG_WRITE_SUPPORTED */ - -#ifndef PNG_1_0_X -# ifndef PNG_NO_ERROR_NUMBERS -# define PNG_ERROR_NUMBERS_SUPPORTED -# endif -#endif /* PNG_1_0_X */ - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) -# ifndef PNG_NO_USER_TRANSFORM_PTR -# define PNG_USER_TRANSFORM_PTR_SUPPORTED -# endif -#endif - -#ifndef PNG_NO_STDIO -# define PNG_TIME_RFC1123_SUPPORTED -#endif - -/* This adds extra functions in pngget.c for accessing data from the - * info pointer (added in version 0.99) - * png_get_image_width() - * png_get_image_height() - * png_get_bit_depth() - * png_get_color_type() - * png_get_compression_type() - * png_get_filter_type() - * png_get_interlace_type() - * png_get_pixel_aspect_ratio() - * png_get_pixels_per_meter() - * png_get_x_offset_pixels() - * png_get_y_offset_pixels() - * png_get_x_offset_microns() - * png_get_y_offset_microns() - */ -#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) -# define PNG_EASY_ACCESS_SUPPORTED -#endif - -/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 - * and removed from version 1.2.20. The following will be removed - * from libpng-1.4.0 -*/ - -#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE) -# ifndef PNG_OPTIMIZED_CODE_SUPPORTED -# define PNG_OPTIMIZED_CODE_SUPPORTED -# endif -#endif - -#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) -# ifndef PNG_ASSEMBLER_CODE_SUPPORTED -# define PNG_ASSEMBLER_CODE_SUPPORTED -# endif - -# if defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4) - /* work around 64-bit gcc compiler bugs in gcc-3.x */ -# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) -# define PNG_NO_MMX_CODE -# endif -# endif - -# if defined(__APPLE__) -# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) -# define PNG_NO_MMX_CODE -# endif -# endif - -# if (defined(__MWERKS__) && ((__MWERKS__ < 0x0900) || macintosh)) -# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) -# define PNG_NO_MMX_CODE -# endif -# endif - -# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) -# define PNG_MMX_CODE_SUPPORTED -# endif - -#endif -/* end of obsolete code to be removed from libpng-1.4.0 */ - -#if !defined(PNG_1_0_X) -#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) -# define PNG_USER_MEM_SUPPORTED -#endif -#endif /* PNG_1_0_X */ - -/* Added at libpng-1.2.6 */ -#if !defined(PNG_1_0_X) -#ifndef PNG_SET_USER_LIMITS_SUPPORTED -#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED) -# define PNG_SET_USER_LIMITS_SUPPORTED -#endif -#endif -#endif /* PNG_1_0_X */ - -/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter - * how large, set these limits to 0x7fffffffL - */ -#ifndef PNG_USER_WIDTH_MAX -# define PNG_USER_WIDTH_MAX 1000000L -#endif -#ifndef PNG_USER_HEIGHT_MAX -# define PNG_USER_HEIGHT_MAX 1000000L -#endif - -/* These are currently experimental features, define them if you want */ - -/* very little testing */ -/* -#ifdef PNG_READ_SUPPORTED -# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED -# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED -# endif -#endif -*/ - -/* This is only for PowerPC big-endian and 680x0 systems */ -/* some testing */ -/* -#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED -# define PNG_READ_BIG_ENDIAN_SUPPORTED -#endif -*/ - -/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ -/* -#define PNG_NO_POINTER_INDEXING -*/ - -/* These functions are turned off by default, as they will be phased out. */ -/* -#define PNG_USELESS_TESTS_SUPPORTED -#define PNG_CORRECT_PALETTE_SUPPORTED -*/ - -/* Any chunks you are not interested in, you can undef here. The - * ones that allocate memory may be expecially important (hIST, - * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info - * a bit smaller. - */ - -#if defined(PNG_READ_SUPPORTED) && \ - !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ - !defined(PNG_NO_READ_ANCILLARY_CHUNKS) -# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED -#endif - -#if defined(PNG_WRITE_SUPPORTED) && \ - !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ - !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) -# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED -#endif - -#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED - -#ifdef PNG_NO_READ_TEXT -# define PNG_NO_READ_iTXt -# define PNG_NO_READ_tEXt -# define PNG_NO_READ_zTXt -#endif -#ifndef PNG_NO_READ_bKGD -# define PNG_READ_bKGD_SUPPORTED -# define PNG_bKGD_SUPPORTED -#endif -#ifndef PNG_NO_READ_cHRM -# define PNG_READ_cHRM_SUPPORTED -# define PNG_cHRM_SUPPORTED -#endif -#ifndef PNG_NO_READ_gAMA -# define PNG_READ_gAMA_SUPPORTED -# define PNG_gAMA_SUPPORTED -#endif -#ifndef PNG_NO_READ_hIST -# define PNG_READ_hIST_SUPPORTED -# define PNG_hIST_SUPPORTED -#endif -#ifndef PNG_NO_READ_iCCP -# define PNG_READ_iCCP_SUPPORTED -# define PNG_iCCP_SUPPORTED -#endif -#ifndef PNG_NO_READ_iTXt -# ifndef PNG_READ_iTXt_SUPPORTED -# define PNG_READ_iTXt_SUPPORTED -# endif -# ifndef PNG_iTXt_SUPPORTED -# define PNG_iTXt_SUPPORTED -# endif -#endif -#ifndef PNG_NO_READ_oFFs -# define PNG_READ_oFFs_SUPPORTED -# define PNG_oFFs_SUPPORTED -#endif -#ifndef PNG_NO_READ_pCAL -# define PNG_READ_pCAL_SUPPORTED -# define PNG_pCAL_SUPPORTED -#endif -#ifndef PNG_NO_READ_sCAL -# define PNG_READ_sCAL_SUPPORTED -# define PNG_sCAL_SUPPORTED -#endif -#ifndef PNG_NO_READ_pHYs -# define PNG_READ_pHYs_SUPPORTED -# define PNG_pHYs_SUPPORTED -#endif -#ifndef PNG_NO_READ_sBIT -# define PNG_READ_sBIT_SUPPORTED -# define PNG_sBIT_SUPPORTED -#endif -#ifndef PNG_NO_READ_sPLT -# define PNG_READ_sPLT_SUPPORTED -# define PNG_sPLT_SUPPORTED -#endif -#ifndef PNG_NO_READ_sRGB -# define PNG_READ_sRGB_SUPPORTED -# define PNG_sRGB_SUPPORTED -#endif -#ifndef PNG_NO_READ_tEXt -# define PNG_READ_tEXt_SUPPORTED -# define PNG_tEXt_SUPPORTED -#endif -#ifndef PNG_NO_READ_tIME -# define PNG_READ_tIME_SUPPORTED -# define PNG_tIME_SUPPORTED -#endif -#ifndef PNG_NO_READ_tRNS -# define PNG_READ_tRNS_SUPPORTED -# define PNG_tRNS_SUPPORTED -#endif -#ifndef PNG_NO_READ_zTXt -# define PNG_READ_zTXt_SUPPORTED -# define PNG_zTXt_SUPPORTED -#endif -#ifndef PNG_NO_READ_UNKNOWN_CHUNKS -# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED -# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED -# define PNG_UNKNOWN_CHUNKS_SUPPORTED -# endif -# ifndef PNG_NO_HANDLE_AS_UNKNOWN -# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED -# endif -#endif -#if !defined(PNG_NO_READ_USER_CHUNKS) && \ - defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) -# define PNG_READ_USER_CHUNKS_SUPPORTED -# define PNG_USER_CHUNKS_SUPPORTED -# ifdef PNG_NO_READ_UNKNOWN_CHUNKS -# undef PNG_NO_READ_UNKNOWN_CHUNKS -# endif -# ifdef PNG_NO_HANDLE_AS_UNKNOWN -# undef PNG_NO_HANDLE_AS_UNKNOWN -# endif -#endif -#ifndef PNG_NO_READ_OPT_PLTE -# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ -#endif /* optional PLTE chunk in RGB and RGBA images */ -#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ - defined(PNG_READ_zTXt_SUPPORTED) -# define PNG_READ_TEXT_SUPPORTED -# define PNG_TEXT_SUPPORTED -#endif - -#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ - -#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED - -#ifdef PNG_NO_WRITE_TEXT -# define PNG_NO_WRITE_iTXt -# define PNG_NO_WRITE_tEXt -# define PNG_NO_WRITE_zTXt -#endif -#ifndef PNG_NO_WRITE_bKGD -# define PNG_WRITE_bKGD_SUPPORTED -# ifndef PNG_bKGD_SUPPORTED -# define PNG_bKGD_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_cHRM -# define PNG_WRITE_cHRM_SUPPORTED -# ifndef PNG_cHRM_SUPPORTED -# define PNG_cHRM_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_gAMA -# define PNG_WRITE_gAMA_SUPPORTED -# ifndef PNG_gAMA_SUPPORTED -# define PNG_gAMA_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_hIST -# define PNG_WRITE_hIST_SUPPORTED -# ifndef PNG_hIST_SUPPORTED -# define PNG_hIST_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_iCCP -# define PNG_WRITE_iCCP_SUPPORTED -# ifndef PNG_iCCP_SUPPORTED -# define PNG_iCCP_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_iTXt -# ifndef PNG_WRITE_iTXt_SUPPORTED -# define PNG_WRITE_iTXt_SUPPORTED -# endif -# ifndef PNG_iTXt_SUPPORTED -# define PNG_iTXt_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_oFFs -# define PNG_WRITE_oFFs_SUPPORTED -# ifndef PNG_oFFs_SUPPORTED -# define PNG_oFFs_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_pCAL -# define PNG_WRITE_pCAL_SUPPORTED -# ifndef PNG_pCAL_SUPPORTED -# define PNG_pCAL_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_sCAL -# define PNG_WRITE_sCAL_SUPPORTED -# ifndef PNG_sCAL_SUPPORTED -# define PNG_sCAL_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_pHYs -# define PNG_WRITE_pHYs_SUPPORTED -# ifndef PNG_pHYs_SUPPORTED -# define PNG_pHYs_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_sBIT -# define PNG_WRITE_sBIT_SUPPORTED -# ifndef PNG_sBIT_SUPPORTED -# define PNG_sBIT_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_sPLT -# define PNG_WRITE_sPLT_SUPPORTED -# ifndef PNG_sPLT_SUPPORTED -# define PNG_sPLT_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_sRGB -# define PNG_WRITE_sRGB_SUPPORTED -# ifndef PNG_sRGB_SUPPORTED -# define PNG_sRGB_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_tEXt -# define PNG_WRITE_tEXt_SUPPORTED -# ifndef PNG_tEXt_SUPPORTED -# define PNG_tEXt_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_tIME -# define PNG_WRITE_tIME_SUPPORTED -# ifndef PNG_tIME_SUPPORTED -# define PNG_tIME_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_tRNS -# define PNG_WRITE_tRNS_SUPPORTED -# ifndef PNG_tRNS_SUPPORTED -# define PNG_tRNS_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_zTXt -# define PNG_WRITE_zTXt_SUPPORTED -# ifndef PNG_zTXt_SUPPORTED -# define PNG_zTXt_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS -# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED -# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED -# define PNG_UNKNOWN_CHUNKS_SUPPORTED -# endif -# ifndef PNG_NO_HANDLE_AS_UNKNOWN -# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED -# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED -# endif -# endif -#endif -#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ - defined(PNG_WRITE_zTXt_SUPPORTED) -# define PNG_WRITE_TEXT_SUPPORTED -# ifndef PNG_TEXT_SUPPORTED -# define PNG_TEXT_SUPPORTED -# endif -#endif - -#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ - -/* Turn this off to disable png_read_png() and - * png_write_png() and leave the row_pointers member - * out of the info structure. - */ -#ifndef PNG_NO_INFO_IMAGE -# define PNG_INFO_IMAGE_SUPPORTED -#endif - -/* need the time information for reading tIME chunks */ -#if defined(PNG_tIME_SUPPORTED) -# if !defined(_WIN32_WCE) - /* "time.h" functions are not supported on WindowsCE */ -# include -# endif -#endif - -/* Some typedefs to get us started. These should be safe on most of the - * common platforms. The typedefs should be at least as large as the - * numbers suggest (a png_uint_32 must be at least 32 bits long), but they - * don't have to be exactly that size. Some compilers dislike passing - * unsigned shorts as function parameters, so you may be better off using - * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may - * want to have unsigned int for png_uint_32 instead of unsigned long. - */ - -typedef unsigned long png_uint_32; -typedef long png_int_32; -typedef unsigned short png_uint_16; -typedef short png_int_16; -typedef unsigned char png_byte; - -/* This is usually size_t. It is typedef'ed just in case you need it to - change (I'm not sure if you will or not, so I thought I'd be safe) */ -#ifdef PNG_SIZE_T - typedef PNG_SIZE_T png_size_t; -# define png_sizeof(x) png_convert_size(sizeof (x)) -#else - typedef size_t png_size_t; -# define png_sizeof(x) sizeof (x) -#endif - -/* The following is needed for medium model support. It cannot be in the - * PNG_INTERNAL section. Needs modification for other compilers besides - * MSC. Model independent support declares all arrays and pointers to be - * large using the far keyword. The zlib version used must also support - * model independent data. As of version zlib 1.0.4, the necessary changes - * have been made in zlib. The USE_FAR_KEYWORD define triggers other - * changes that are needed. (Tim Wegner) - */ - -/* Separate compiler dependencies (problem here is that zlib.h always - defines FAR. (SJT) */ -#ifdef __BORLANDC__ -# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) -# define LDATA 1 -# else -# define LDATA 0 -# endif - /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ -# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) -# define PNG_MAX_MALLOC_64K -# if (LDATA != 1) -# ifndef FAR -# define FAR __far -# endif -# define USE_FAR_KEYWORD -# endif /* LDATA != 1 */ - /* Possibly useful for moving data out of default segment. - * Uncomment it if you want. Could also define FARDATA as - * const if your compiler supports it. (SJT) -# define FARDATA FAR - */ -# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ -#endif /* __BORLANDC__ */ - - -/* Suggest testing for specific compiler first before testing for - * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, - * making reliance oncertain keywords suspect. (SJT) - */ - -/* MSC Medium model */ -#if defined(FAR) -# if defined(M_I86MM) -# define USE_FAR_KEYWORD -# define FARDATA FAR -# include -# endif -#endif - -/* SJT: default case */ -#ifndef FAR -# define FAR -#endif - -/* At this point FAR is always defined */ -#ifndef FARDATA -# define FARDATA -#endif - -/* Typedef for floating-point numbers that are converted - to fixed-point with a multiple of 100,000, e.g., int_gamma */ -typedef png_int_32 png_fixed_point; - -/* Add typedefs for pointers */ -typedef void FAR * png_voidp; -typedef png_byte FAR * png_bytep; -typedef png_uint_32 FAR * png_uint_32p; -typedef png_int_32 FAR * png_int_32p; -typedef png_uint_16 FAR * png_uint_16p; -typedef png_int_16 FAR * png_int_16p; -typedef PNG_CONST char FAR * png_const_charp; -typedef char FAR * png_charp; -typedef png_fixed_point FAR * png_fixed_point_p; - -#ifndef PNG_NO_STDIO -#if defined(_WIN32_WCE) -typedef HANDLE png_FILE_p; -#else -typedef FILE * png_FILE_p; -#endif -#endif - -#ifdef PNG_FLOATING_POINT_SUPPORTED -typedef double FAR * png_doublep; -#endif - -/* Pointers to pointers; i.e. arrays */ -typedef png_byte FAR * FAR * png_bytepp; -typedef png_uint_32 FAR * FAR * png_uint_32pp; -typedef png_int_32 FAR * FAR * png_int_32pp; -typedef png_uint_16 FAR * FAR * png_uint_16pp; -typedef png_int_16 FAR * FAR * png_int_16pp; -typedef PNG_CONST char FAR * FAR * png_const_charpp; -typedef char FAR * FAR * png_charpp; -typedef png_fixed_point FAR * FAR * png_fixed_point_pp; -#ifdef PNG_FLOATING_POINT_SUPPORTED -typedef double FAR * FAR * png_doublepp; -#endif - -/* Pointers to pointers to pointers; i.e., pointer to array */ -typedef char FAR * FAR * FAR * png_charppp; - -#if defined(PNG_1_0_X) || defined(PNG_1_2_X) -/* SPC - Is this stuff deprecated? */ -/* It'll be removed as of libpng-1.3.0 - GR-P */ -/* libpng typedefs for types in zlib. If zlib changes - * or another compression library is used, then change these. - * Eliminates need to change all the source files. - */ -typedef charf * png_zcharp; -typedef charf * FAR * png_zcharpp; -typedef z_stream FAR * png_zstreamp; -#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ - -/* - * Define PNG_BUILD_DLL if the module being built is a Windows - * LIBPNG DLL. - * - * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. - * It is equivalent to Microsoft predefined macro _DLL that is - * automatically defined when you compile using the share - * version of the CRT (C Run-Time library) - * - * The cygwin mods make this behavior a little different: - * Define PNG_BUILD_DLL if you are building a dll for use with cygwin - * Define PNG_STATIC if you are building a static library for use with cygwin, - * -or- if you are building an application that you want to link to the - * static library. - * PNG_USE_DLL is defined by default (no user action needed) unless one of - * the other flags is defined. - */ - -#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) -# define PNG_DLL -#endif -/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. - * When building a static lib, default to no GLOBAL ARRAYS, but allow - * command-line override - */ -#if defined(__CYGWIN__) -# if !defined(PNG_STATIC) -# if defined(PNG_USE_GLOBAL_ARRAYS) -# undef PNG_USE_GLOBAL_ARRAYS -# endif -# if !defined(PNG_USE_LOCAL_ARRAYS) -# define PNG_USE_LOCAL_ARRAYS -# endif -# else -# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) -# if defined(PNG_USE_GLOBAL_ARRAYS) -# undef PNG_USE_GLOBAL_ARRAYS -# endif -# endif -# endif -# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) -# define PNG_USE_LOCAL_ARRAYS -# endif -#endif - -/* Do not use global arrays (helps with building DLL's) - * They are no longer used in libpng itself, since version 1.0.5c, - * but might be required for some pre-1.0.5c applications. - */ -#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) -# if defined(PNG_NO_GLOBAL_ARRAYS) || \ - (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER) -# define PNG_USE_LOCAL_ARRAYS -# else -# define PNG_USE_GLOBAL_ARRAYS -# endif -#endif - -#if defined(__CYGWIN__) -# undef PNGAPI -# define PNGAPI __cdecl -# undef PNG_IMPEXP -# define PNG_IMPEXP -#endif - -/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", - * you may get warnings regarding the linkage of png_zalloc and png_zfree. - * Don't ignore those warnings; you must also reset the default calling - * convention in your compiler to match your PNGAPI, and you must build - * zlib and your applications the same way you build libpng. - */ - -#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) -# ifndef PNG_NO_MODULEDEF -# define PNG_NO_MODULEDEF -# endif -#endif - -#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) -# define PNG_IMPEXP -#endif - -#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ - (( defined(_Windows) || defined(_WINDOWS) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) - -# ifndef PNGAPI -# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) -# define PNGAPI __cdecl -# else -# define PNGAPI _cdecl -# endif -# endif - -# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ - 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) -# define PNG_IMPEXP -# endif - -# if !defined(PNG_IMPEXP) - -# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol -# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol - - /* Borland/Microsoft */ -# if defined(_MSC_VER) || defined(__BORLANDC__) -# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) -# define PNG_EXPORT PNG_EXPORT_TYPE1 -# else -# define PNG_EXPORT PNG_EXPORT_TYPE2 -# if defined(PNG_BUILD_DLL) -# define PNG_IMPEXP __export -# else -# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in - VC++ */ -# endif /* Exists in Borland C++ for - C++ classes (== huge) */ -# endif -# endif - -# if !defined(PNG_IMPEXP) -# if defined(PNG_BUILD_DLL) -# define PNG_IMPEXP __declspec(dllexport) -# else -# define PNG_IMPEXP __declspec(dllimport) -# endif -# endif -# endif /* PNG_IMPEXP */ -#else /* !(DLL || non-cygwin WINDOWS) */ -# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) -# ifndef PNGAPI -# define PNGAPI _System -# endif -# else -# if 0 /* ... other platforms, with other meanings */ -# endif -# endif -#endif - -#ifndef PNGAPI -# define PNGAPI -#endif -#ifndef PNG_IMPEXP -# define PNG_IMPEXP -#endif - -#ifdef PNG_BUILDSYMS -# ifndef PNG_EXPORT -# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END -# endif -# ifdef PNG_USE_GLOBAL_ARRAYS -# ifndef PNG_EXPORT_VAR -# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT -# endif -# endif -#endif - -#ifndef PNG_EXPORT -# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol -#endif - -#ifdef PNG_USE_GLOBAL_ARRAYS -# ifndef PNG_EXPORT_VAR -# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type -# endif -#endif - -/* User may want to use these so they are not in PNG_INTERNAL. Any library - * functions that are passed far data must be model independent. - */ - -#ifndef PNG_ABORT -# define PNG_ABORT() abort() -#endif - -#ifdef PNG_SETJMP_SUPPORTED -# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) -#else -# define png_jmpbuf(png_ptr) \ - (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) -#endif - -#if defined(USE_FAR_KEYWORD) /* memory model independent fns */ -/* use this to make far-to-near assignments */ -# define CHECK 1 -# define NOCHECK 0 -# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) -# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) -# define png_snprintf _fsnprintf /* Added to v 1.2.19 */ -# define png_strlen _fstrlen -# define png_memcmp _fmemcmp /* SJT: added */ -# define png_memcpy _fmemcpy -# define png_memset _fmemset -#else /* use the usual functions */ -# define CVT_PTR(ptr) (ptr) -# define CVT_PTR_NOCHECK(ptr) (ptr) -# ifndef PNG_NO_SNPRINTF -# ifdef _MSC_VER -# define png_snprintf _snprintf /* Added to v 1.2.19 */ -# define png_snprintf2 _snprintf -# define png_snprintf6 _snprintf -# else -# define png_snprintf snprintf /* Added to v 1.2.19 */ -# define png_snprintf2 snprintf -# define png_snprintf6 snprintf -# endif -# else - /* You don't have or don't want to use snprintf(). Caution: Using - * sprintf instead of snprintf exposes your application to accidental - * or malevolent buffer overflows. If you don't have snprintf() - * as a general rule you should provide one (you can get one from - * Portable OpenSSH). */ -# define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1) -# define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2) -# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ - sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) -# endif -# define png_strlen strlen -# define png_memcmp memcmp /* SJT: added */ -# define png_memcpy memcpy -# define png_memset memset -#endif -/* End of memory model independent support */ - -/* Just a little check that someone hasn't tried to define something - * contradictory. - */ -#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) -# undef PNG_ZBUF_SIZE -# define PNG_ZBUF_SIZE 65536L -#endif - -/* Added at libpng-1.2.8 */ -#endif /* PNG_VERSION_INFO_ONLY */ - -#endif /* PNGCONF_H */ diff --git a/extlib/si/si.h b/extlib/si/si.h deleted file mode 100644 index 04aaf9b..0000000 --- a/extlib/si/si.h +++ /dev/null @@ -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 - -#include "spwmacro.h" -#include "spwdata.h" -#include "siSync.h" - -#include -#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_ */ diff --git a/extlib/si/siSync.h b/extlib/si/siSync.h deleted file mode 100644 index 174f6a3..0000000 --- a/extlib/si/siSync.h +++ /dev/null @@ -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_ */ diff --git a/extlib/si/siSyncPriv.h b/extlib/si/siSyncPriv.h deleted file mode 100644 index 131cf3e..0000000 --- a/extlib/si/siSyncPriv.h +++ /dev/null @@ -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_ */ diff --git a/extlib/si/siapp.h b/extlib/si/siapp.h deleted file mode 100644 index eb8f222..0000000 --- a/extlib/si/siapp.h +++ /dev/null @@ -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 */ diff --git a/extlib/si/siapp.lib b/extlib/si/siapp.lib deleted file mode 100644 index 5dbb5c6..0000000 Binary files a/extlib/si/siapp.lib and /dev/null differ diff --git a/extlib/si/spwdata.h b/extlib/si/spwdata.h deleted file mode 100644 index f54a070..0000000 --- a/extlib/si/spwdata.h +++ /dev/null @@ -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 - -#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 */ - diff --git a/extlib/si/spwerror.h b/extlib/si/spwerror.h deleted file mode 100644 index 208d2c7..0000000 --- a/extlib/si/spwerror.h +++ /dev/null @@ -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_ */ diff --git a/extlib/si/spwmacro.h b/extlib/si/spwmacro.h deleted file mode 100644 index 10fcf16..0000000 --- a/extlib/si/spwmacro.h +++ /dev/null @@ -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 diff --git a/extlib/zconf.h b/extlib/zconf.h deleted file mode 100644 index 03a9431..0000000 --- a/extlib/zconf.h +++ /dev/null @@ -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 - /* 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 /* for off_t */ -# include /* for SEEK_* and off_t */ -# ifdef VMS -# include /* 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 */ diff --git a/extlib/zlib.h b/extlib/zlib.h deleted file mode 100644 index 0228179..0000000 --- a/extlib/zlib.h +++ /dev/null @@ -1,1357 +0,0 @@ -/* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.3, July 18th, 2005 - - Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - - - The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt - (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). -*/ - -#ifndef ZLIB_H -#define ZLIB_H - -#include "zconf.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define ZLIB_VERSION "1.2.3" -#define ZLIB_VERNUM 0x1230 - -/* - The 'zlib' compression library provides in-memory compression and - decompression functions, including integrity checks of the uncompressed - data. This version of the library supports only one compression method - (deflation) but other algorithms will be added later and will have the same - stream interface. - - Compression can be done in a single step if the buffers are large - enough (for example if an input file is mmap'ed), or can be done by - repeated calls of the compression function. In the latter case, the - application must provide more input and/or consume the output - (providing more output space) before each call. - - The compressed data format used by default by the in-memory functions is - the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped - around a deflate stream, which is itself documented in RFC 1951. - - The library also supports reading and writing files in gzip (.gz) format - with an interface similar to that of stdio using the functions that start - with "gz". The gzip format is different from the zlib format. gzip is a - gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - - This library can optionally read and write gzip streams in memory as well. - - The zlib format was designed to be compact and fast for use in memory - and on communications channels. The gzip format was designed for single- - file compression on file systems, has a larger header than zlib to maintain - directory information, and uses a different, slower check method than zlib. - - The library does not install any signal handler. The decoder checks - the consistency of the compressed data, so the library should never - crash even in case of corrupted input. -*/ - -typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); -typedef void (*free_func) OF((voidpf opaque, voidpf address)); - -struct internal_state; - -typedef struct z_stream_s { - Bytef *next_in; /* next input byte */ - uInt avail_in; /* number of bytes available at next_in */ - uLong total_in; /* total nb of input bytes read so far */ - - Bytef *next_out; /* next output byte should be put there */ - uInt avail_out; /* remaining free space at next_out */ - uLong total_out; /* total nb of bytes output so far */ - - char *msg; /* last error message, NULL if no error */ - struct internal_state FAR *state; /* not visible by applications */ - - alloc_func zalloc; /* used to allocate the internal state */ - free_func zfree; /* used to free the internal state */ - voidpf opaque; /* private data object passed to zalloc and zfree */ - - int data_type; /* best guess about the data type: binary or text */ - uLong adler; /* adler32 value of the uncompressed data */ - uLong reserved; /* reserved for future use */ -} z_stream; - -typedef z_stream FAR *z_streamp; - -/* - gzip header information passed to and from zlib routines. See RFC 1952 - for more details on the meanings of these fields. -*/ -typedef struct gz_header_s { - int text; /* true if compressed data believed to be text */ - uLong time; /* modification time */ - int xflags; /* extra flags (not used when writing a gzip file) */ - int os; /* operating system */ - Bytef *extra; /* pointer to extra field or Z_NULL if none */ - uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ - uInt extra_max; /* space at extra (only when reading header) */ - Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ - uInt name_max; /* space at name (only when reading header) */ - Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ - uInt comm_max; /* space at comment (only when reading header) */ - int hcrc; /* true if there was or will be a header crc */ - int done; /* true when done reading gzip header (not used - when writing a gzip file) */ -} gz_header; - -typedef gz_header FAR *gz_headerp; - -/* - The application must update next_in and avail_in when avail_in has - dropped to zero. It must update next_out and avail_out when avail_out - has dropped to zero. The application must initialize zalloc, zfree and - opaque before calling the init function. All other fields are set by the - compression library and must not be updated by the application. - - The opaque value provided by the application will be passed as the first - parameter for calls of zalloc and zfree. This can be useful for custom - memory management. The compression library attaches no meaning to the - opaque value. - - zalloc must return Z_NULL if there is not enough memory for the object. - If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. - - On 16-bit systems, the functions zalloc and zfree must be able to allocate - exactly 65536 bytes, but will not be required to allocate more than this - if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, - pointers returned by zalloc for objects of exactly 65536 bytes *must* - have their offset normalized to zero. The default allocation function - provided by this library ensures this (see zutil.c). To reduce memory - requirements and avoid any allocation of 64K objects, at the expense of - compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). - - The fields total_in and total_out can be used for statistics or - progress reports. After compression, total_in holds the total size of - the uncompressed data and may be saved for use in the decompressor - (particularly if the decompressor wants to decompress everything in - a single step). -*/ - - /* constants */ - -#define Z_NO_FLUSH 0 -#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ -#define Z_SYNC_FLUSH 2 -#define Z_FULL_FLUSH 3 -#define Z_FINISH 4 -#define Z_BLOCK 5 -/* Allowed flush values; see deflate() and inflate() below for details */ - -#define Z_OK 0 -#define Z_STREAM_END 1 -#define Z_NEED_DICT 2 -#define Z_ERRNO (-1) -#define Z_STREAM_ERROR (-2) -#define Z_DATA_ERROR (-3) -#define Z_MEM_ERROR (-4) -#define Z_BUF_ERROR (-5) -#define Z_VERSION_ERROR (-6) -/* Return codes for the compression/decompression functions. Negative - * values are errors, positive values are used for special but normal events. - */ - -#define Z_NO_COMPRESSION 0 -#define Z_BEST_SPEED 1 -#define Z_BEST_COMPRESSION 9 -#define Z_DEFAULT_COMPRESSION (-1) -/* compression levels */ - -#define Z_FILTERED 1 -#define Z_HUFFMAN_ONLY 2 -#define Z_RLE 3 -#define Z_FIXED 4 -#define Z_DEFAULT_STRATEGY 0 -/* compression strategy; see deflateInit2() below for details */ - -#define Z_BINARY 0 -#define Z_TEXT 1 -#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ -#define Z_UNKNOWN 2 -/* Possible values of the data_type field (though see inflate()) */ - -#define Z_DEFLATED 8 -/* The deflate compression method (the only one supported in this version) */ - -#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ - -#define zlib_version zlibVersion() -/* for compatibility with versions < 1.0.2 */ - - /* basic functions */ - -ZEXTERN const char * ZEXPORT zlibVersion OF((void)); -/* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is - not compatible with the zlib.h header file used by the application. - This check is automatically made by deflateInit and inflateInit. - */ - -/* -ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); - - Initializes the internal stream state for compression. The fields - zalloc, zfree and opaque must be initialized before by the caller. - If zalloc and zfree are set to Z_NULL, deflateInit updates them to - use default allocation functions. - - The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: - 1 gives best speed, 9 gives best compression, 0 gives no compression at - all (the input data is simply copied a block at a time). - Z_DEFAULT_COMPRESSION requests a default compromise between speed and - compression (currently equivalent to level 6). - - deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if level is not a valid compression level, - Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible - with the version assumed by the caller (ZLIB_VERSION). - msg is set to null if there is no error message. deflateInit does not - perform any compression: this will be done by deflate(). -*/ - - -ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); -/* - deflate compresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce some - output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. deflate performs one or both of the - following actions: - - - Compress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in and avail_in are updated and - processing will resume at this point for the next call of deflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. This action is forced if the parameter flush is non zero. - Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary (in interactive applications). - Some output may be provided even if flush is not set. - - Before the call of deflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating avail_in or avail_out accordingly; avail_out - should never be zero before the call. The application can consume the - compressed output when it wants, for example when the output buffer is full - (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK - and with zero avail_out, it must be called again after making room in the - output buffer because there might be more output pending. - - Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to - decide how much data to accumualte before producing output, in order to - maximize compression. - - If the parameter flush is set to Z_SYNC_FLUSH, all pending output is - flushed to the output buffer and the output is aligned on a byte boundary, so - that the decompressor can get all input data available so far. (In particular - avail_in is zero after the call if enough output space has been provided - before the call.) Flushing may degrade compression for some compression - algorithms and so it should be used only when necessary. - - If flush is set to Z_FULL_FLUSH, all output is flushed as with - Z_SYNC_FLUSH, and the compression state is reset so that decompression can - restart from this point if previous compressed data has been damaged or if - random access is desired. Using Z_FULL_FLUSH too often can seriously degrade - compression. - - If deflate returns with avail_out == 0, this function must be called again - with the same value of the flush parameter and more output space (updated - avail_out), until the flush is complete (deflate returns with non-zero - avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that - avail_out is greater than six to avoid repeated flush markers due to - avail_out == 0 on return. - - If the parameter flush is set to Z_FINISH, pending input is processed, - pending output is flushed and deflate returns with Z_STREAM_END if there - was enough output space; if deflate returns with Z_OK, this function must be - called again with Z_FINISH and more output space (updated avail_out) but no - more input data, until it returns with Z_STREAM_END or an error. After - deflate has returned Z_STREAM_END, the only possible operations on the - stream are deflateReset or deflateEnd. - - Z_FINISH can be used immediately after deflateInit if all the compression - is to be done in a single step. In this case, avail_out must be at least - the value returned by deflateBound (see below). If deflate does not return - Z_STREAM_END, then it must be called again as described above. - - deflate() sets strm->adler to the adler32 checksum of all input read - so far (that is, total_in bytes). - - deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered - binary. This field is only for information purposes and does not affect - the compression algorithm in any manner. - - deflate() returns Z_OK if some progress has been made (more input - processed or more output produced), Z_STREAM_END if all input has been - consumed and all output has been produced (only when flush is set to - Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not - fatal, and deflate() can be called again with more input and more output - space to continue compressing. -*/ - - -ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. - - deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the - stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, - msg may be set but then points to a static string (which must not be - deallocated). -*/ - - -/* -ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); - - Initializes the internal stream state for decompression. The fields - next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the exact - value depends on the compression method), inflateInit determines the - compression method from the zlib header and allocates all data structures - accordingly; otherwise the allocation will be deferred to the first call of - inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to - use default allocation functions. - - inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller. msg is set to null if there is no error - message. inflateInit does not perform any decompression apart from reading - the zlib header if present: this will be done by inflate(). (So next_in and - avail_in may be modified, but next_out and avail_out are unchanged.) -*/ - - -ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); -/* - inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. inflate performs one or both of the - following actions: - - - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing - will resume at this point for the next call of inflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there - is no more input data or no more space in the output buffer (see below - about the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating the next_* and avail_* values accordingly. - The application can consume the uncompressed output when it wants, for - example when the output buffer is full (avail_out == 0), or after each - call of inflate(). If inflate returns Z_OK and with zero avail_out, it - must be called again after making room in the output buffer because there - might be more output pending. - - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, - Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much - output as possible to the output buffer. Z_BLOCK requests that inflate() stop - if and when it gets to the next deflate block boundary. When decoding the - zlib or gzip format, this will cause inflate() to return immediately after - the header and before the first block. When doing a raw inflate, inflate() - will go ahead and process the first block, and will return when it gets to - the end of that block, or when it runs out of data. - - The Z_BLOCK option assists in appending to or combining deflate streams. - Also to assist in this, on return inflate() will set strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 - if inflate() is currently decoding the last block in the deflate stream, - plus 128 if inflate() returned immediately after decoding an end-of-block - code or decoding the complete header up to just before the first byte of the - deflate stream. The end-of-block will not be indicated until all of the - uncompressed data from that block has been written to strm->next_out. The - number of unused bits may in general be greater than seven, except when - bit 7 of data_type is set, in which case the number of unused bits will be - less than eight. - - inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step - (a single call of inflate), the parameter flush should be set to - Z_FINISH. In this case all pending input is processed and all pending - output is flushed; avail_out must be large enough to hold all the - uncompressed data. (The size of the uncompressed data may have been saved - by the compressor for this purpose.) The next operation on this stream must - be inflateEnd to deallocate the decompression state. The use of Z_FINISH - is never required, but can be used to inform inflate that a faster approach - may be used for the single inflate() call. - - In this implementation, inflate() always flushes as much output as - possible to the output buffer, and always uses the faster approach on the - first call. So the only effect of the flush parameter in this implementation - is on the return value of inflate(), as noted below, or when it returns early - because Z_BLOCK is used. - - If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the adler32 checksum of the dictionary - chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the adler32 checksum of all output produced so far (that is, - total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed adler32 - checksum is equal to that saved by the compressor and returns Z_STREAM_END - only if the checksum is correct. - - inflate() will decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically. Any information - contained in the gzip header is not retained, so applications that need that - information should instead use raw inflate, see inflateInit2() below, or - inflateBack() and perform their own processing of the gzip header and - trailer. - - inflate() returns Z_OK if some progress has been made (more input processed - or more output produced), Z_STREAM_END if the end of the compressed data has - been reached and all uncompressed output has been produced, Z_NEED_DICT if a - preset dictionary is needed at this point, Z_DATA_ERROR if the input data was - corrupted (input stream not conforming to the zlib format or incorrect check - value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, - Z_BUF_ERROR if no progress is possible or if there was not enough room in the - output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and - inflate() can be called again with more input and more output space to - continue decompressing. If Z_DATA_ERROR is returned, the application may then - call inflateSync() to look for a good compression block if a partial recovery - of the data is desired. -*/ - - -ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. - - inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a - static string (which must not be deallocated). -*/ - - /* Advanced functions */ - -/* - The following functions are needed only in some special applications. -*/ - -/* -ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy)); - - This is another version of deflateInit with more compression options. The - fields next_in, zalloc, zfree and opaque must be initialized before by - the caller. - - The method parameter is the compression method. It must be Z_DEFLATED in - this version of the library. - - The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8..15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if - deflateInit is used instead. - - windowBits can also be -8..-15 for raw deflate. In this case, -windowBits - determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute an adler32 check value. - - windowBits can also be greater than 15 for optional gzip encoding. Add - 16 to windowBits to write a simple gzip header and trailer around the - compressed data instead of a zlib wrapper. The gzip header will have no - file name, no extra data, no comment, no modification time (set to zero), - no header crc, and the operating system will be set to 255 (unknown). If a - gzip stream is being written, strm->adler is a crc32 instead of an adler32. - - The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but - is slow and reduces compression ratio; memLevel=9 uses maximum memory - for optimal speed. The default value is 8. See zconf.h for total memory - usage as a function of windowBits and memLevel. - - The strategy parameter is used to tune the compression algorithm. Use the - value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a - filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no - string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman - coding and less string matching; it is somewhat intermediate between - Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as - Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy - parameter only affects the compression ratio but not the correctness of the - compressed output even if it is not set appropriately. Z_FIXED prevents the - use of dynamic Huffman codes, allowing for a simpler decoder for special - applications. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid - method). msg is set to null if there is no error message. deflateInit2 does - not perform any compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); -/* - Initializes the compression dictionary from the given byte sequence - without producing any compressed output. This function must be called - immediately after deflateInit, deflateInit2 or deflateReset, before any - call of deflate. The compressor and decompressor must use exactly the same - dictionary (see inflateSetDictionary). - - The dictionary should consist of strings (byte sequences) that are likely - to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a - dictionary is most useful when the data to be compressed is short and can be - predicted with good accuracy; the data can then be compressed better than - with the default empty dictionary. - - Depending on the size of the compression data structures selected by - deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size in - deflate or deflate2. Thus the strings most likely to be useful should be - put at the end of the dictionary, not at the front. In addition, the - current implementation of deflate will use at most the window size minus - 262 bytes of the provided dictionary. - - Upon return of this function, strm->adler is set to the adler32 value - of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The adler32 value - applies to the whole dictionary even if only a subset of the dictionary is - actually used by the compressor.) If a raw deflate was requested, then the - adler32 value is not computed and strm->adler is not set. - - deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is - inconsistent (for example if deflate has already been called for this stream - or if the compression method is bsort). deflateSetDictionary does not - perform any compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, - z_streamp source)); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when several compression strategies will be - tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed - by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and - can consume lots of memory. - - deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); -/* - This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. - The stream will keep the same compression level and any other attributes - that may have been set by deflateInit2. - - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - -ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, - int level, - int strategy)); -/* - Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2. This can be - used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different - strategy. If the compression level is changed, the input available so far - is compressed with the old level (and may be flushed); the new level will - take effect only at the next call of deflate(). - - Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to - be compressed and flushed. In particular, strm->avail_out must be non-zero. - - deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR - if strm->avail_out was zero. -*/ - -ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, - int good_length, - int max_lazy, - int nice_length, - int max_chain)); -/* - Fine tune deflate's internal compression parameters. This should only be - used by someone who understands the algorithm used by zlib's deflate for - searching for the best matching string, and even then only by the most - fanatic optimizer trying to squeeze out the last compressed bit for their - specific input data. Read the deflate.c source code for the meaning of the - max_lazy, good_length, nice_length, and max_chain parameters. - - deflateTune() can be called after deflateInit() or deflateInit2(), and - returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. - */ - -ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, - uLong sourceLen)); -/* - deflateBound() returns an upper bound on the compressed size after - deflation of sourceLen bytes. It must be called after deflateInit() - or deflateInit2(). This would be used to allocate an output buffer - for deflation in a single pass, and so would be called before deflate(). -*/ - -ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, - int bits, - int value)); -/* - deflatePrime() inserts bits in the deflate output stream. The intent - is that this function is used to start off the deflate output with the - bits leftover from a previous deflate stream when appending to it. As such, - this function can only be used for raw deflate, and must be used before the - first deflate() call after a deflateInit2() or deflateReset(). bits must be - less than or equal to 16, and that many of the least significant bits of - value will be inserted in the output. - - deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, - gz_headerp head)); -/* - deflateSetHeader() provides gzip header information for when a gzip - stream is requested by deflateInit2(). deflateSetHeader() may be called - after deflateInit2() or deflateReset() and before the first call of - deflate(). The text, time, os, extra field, name, and comment information - in the provided gz_header structure are written to the gzip header (xflag is - ignored -- the extra flags are set according to the compression level). The - caller must assure that, if not Z_NULL, name and comment are terminated with - a zero byte, and that if extra is not Z_NULL, that extra_len bytes are - available there. If hcrc is true, a gzip header crc is included. Note that - the current versions of the command-line version of gzip (up through version - 1.3.x) do not support header crc's, and will report that it is a "multi-part - gzip file" and give up. - - If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to 255, with no extra, name, or comment - fields. The gzip header is returned to the default state by deflateReset(). - - deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, - int windowBits)); - - This is another version of inflateInit with an extra parameter. The - fields next_in, avail_in, zalloc, zfree and opaque must be initialized - before by the caller. - - The windowBits parameter is the base two logarithm of the maximum window - size (the size of the history buffer). It should be in the range 8..15 for - this version of the library. The default value is 15 if inflateInit is used - instead. windowBits must be greater than or equal to the windowBits value - provided to deflateInit2() while compressing, or it must be equal to 15 if - deflateInit2() was not used. If a compressed stream with a larger window - size is given as input, inflate() will return with the error code - Z_DATA_ERROR instead of trying to allocate a larger window. - - windowBits can also be -8..-15 for raw inflate. In this case, -windowBits - determines the window size. inflate() will then process raw deflate data, - not looking for a zlib or gzip header, not generating a check value, and not - looking for any check values for comparison at the end of the stream. This - is for use with other formats that use the deflate compressed data format - such as zip. Those formats provide their own check values. If a custom - format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an adler32 or a crc32 be applied to - the uncompressed data as is done in the zlib, gzip, and zip formats. For - most applications, the zlib format should be used as is. Note that comments - above on the use in deflateInit2() applies to the magnitude of windowBits. - - windowBits can also be greater than 15 for optional gzip decoding. Add - 32 to windowBits to enable zlib and gzip decoding with automatic header - detection, or add 16 to decode only the gzip format (the zlib format will - return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is - a crc32 instead of an adler32. - - inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg - is set to null if there is no error message. inflateInit2 does not perform - any decompression apart from reading the zlib header if present: this will - be done by inflate(). (So next_in and avail_in may be modified, but next_out - and avail_out are unchanged.) -*/ - -ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); -/* - Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate, - if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the adler32 value returned by that call of inflate. - The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called - immediately after inflateInit2() or inflateReset() and before any call of - inflate() to set the dictionary. The application must insure that the - dictionary that was used for compression is provided. - - inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is - inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect adler32 value). inflateSetDictionary does not - perform any decompression: this will be done by subsequent calls of - inflate(). -*/ - -ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); -/* - Skips invalid compressed data until a full flush point (see above the - description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. - - inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR - if no more input was provided, Z_DATA_ERROR if no flush point has been found, - or Z_STREAM_ERROR if the stream structure was inconsistent. In the success - case, the application may save the current current value of total_in which - indicates where valid compressed data was found. In the error case, the - application may repeatedly call inflateSync, providing more input each time, - until success or end of the input data. -*/ - -ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, - z_streamp source)); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when randomly accessing a large stream. The - first pass through the stream can periodically record the inflate state, - allowing restarting inflate at those points when randomly accessing the - stream. - - inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); -/* - This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. - The stream will keep attributes that may have been set by inflateInit2. - - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - -ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, - int bits, - int value)); -/* - This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. - - inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, - gz_headerp head)); -/* - inflateGetHeader() requests that gzip header information be stored in the - provided gz_header structure. inflateGetHeader() may be called after - inflateInit2() or inflateReset(), and before the first call of inflate(). - As inflate() processes the gzip stream, head->done is zero until the header - is completed, at which time head->done is set to one. If a zlib stream is - being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK can be used to - force inflate() to return immediately after header processing is complete - and before any actual data is decompressed. - - The text, time, xflags, and os fields are filled in with the gzip header - contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max - contains the maximum number of bytes to write to extra. Once done is true, - extra_len contains the actual extra field length, and extra contains the - extra field, or that field truncated if extra_max is less than extra_len. - If name is not Z_NULL, then up to name_max characters are written there, - terminated with a zero unless the length is greater than name_max. If - comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When - any of extra, name, or comment are not Z_NULL and the respective field is - not present in the header, then that field is set to Z_NULL to signal its - absence. This allows the use of deflateSetHeader() with the returned - structure to duplicate the header. However if those fields are set to - allocated memory, then the application will need to save those pointers - elsewhere so that they can be eventually freed. - - If inflateGetHeader is not used, then the header information is simply - discarded. The header is always checked for validity, including the header - CRC if present. inflateReset() will reset the process to discard the header - information. The application would need to call inflateGetHeader() again to - retrieve the header from the next gzip stream. - - inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, - unsigned char FAR *window)); - - Initialize the internal stream state for decompression using inflateBack() - calls. The fields zalloc, zfree and opaque in strm must be initialized - before the call. If zalloc and zfree are Z_NULL, then the default library- - derived memory allocation routines are used. windowBits is the base two - logarithm of the window size, in the range 8..15. window is a caller - supplied buffer of that size. Except for special applications where it is - assured that deflate was used with small window sizes, windowBits must be 15 - and a 32K byte window must be supplied to be able to decompress general - deflate streams. - - See inflateBack() for the usage of these routines. - - inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the paramaters are invalid, Z_MEM_ERROR if the internal state could not - be allocated, or Z_VERSION_ERROR if the version of the library does not - match the version of the header file. -*/ - -typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); -typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); - -ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, - in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc)); -/* - inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is more efficient than inflate() for - file i/o applications in that it avoids copying between the output and the - sliding window by simply making the window itself the output buffer. This - function trusts the application to not change the output buffer passed by - the output function, at least until inflateBack() returns. - - inflateBackInit() must be called first to allocate the internal state - and to initialize the state with the user-provided window buffer. - inflateBack() may then be used multiple times to inflate a complete, raw - deflate stream with each call. inflateBackEnd() is then called to free - the allocated state. - - A raw deflate stream is one with no zlib or gzip header or trailer. - This routine would normally be used in a utility that reads zip or gzip - files and writes out uncompressed files. The utility would decode the - header and process the trailer on its own, hence this routine expects - only the raw deflate stream to decompress. This is different from the - normal behavior of inflate(), which expects either a zlib or gzip header and - trailer around the deflate stream. - - inflateBack() uses two subroutines supplied by the caller that are then - called by inflateBack() for input and output. inflateBack() calls those - routines until it reads a complete deflate stream and writes out all of the - uncompressed data, or until it encounters an error. The function's - parameters and return types are defined above in the in_func and out_func - typedefs. inflateBack() will call in(in_desc, &buf) which should return the - number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero--buf is ignored in that - case--and inflateBack() will return a buffer error. inflateBack() will call - out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() - should return zero on success, or non-zero on failure. If out() returns - non-zero, inflateBack() will return with an error. Neither in() nor out() - are permitted to change the contents of the window provided to - inflateBackInit(), which is also the buffer that out() uses to write from. - The length written by out() will be at most the window size. Any non-zero - amount of input may be provided by in(). - - For convenience, inflateBack() can be provided input on the first call by - setting strm->next_in and strm->avail_in. If that input is exhausted, then - in() will be called. Therefore strm->next_in must be initialized before - calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called - immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in - must also be initialized, and then if strm->avail_in is not zero, input will - initially be taken from strm->next_in[0 .. strm->avail_in - 1]. - - The in_desc and out_desc parameters of inflateBack() is passed as the - first parameter of in() and out() respectively when they are called. These - descriptors can be optionally used to pass any information that the caller- - supplied in() and out() functions need to do their job. - - On return, inflateBack() will set strm->next_in and strm->avail_in to - pass back any unused input that was provided by the last in() call. The - return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR - if in() or out() returned an error, Z_DATA_ERROR if there was a format - error in the deflate stream (in which case strm->msg is set to indicate the - nature of the error), or Z_STREAM_ERROR if the stream was not properly - initialized. In the case of Z_BUF_ERROR, an input or output error can be - distinguished using strm->next_in which will be Z_NULL only if in() returned - an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to - out() returning non-zero. (in() will always be called before out(), so - strm->next_in is assured to be defined if out() returns non-zero.) Note - that inflateBack() cannot return Z_OK. -*/ - -ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); -/* - All memory allocated by inflateBackInit() is freed. - - inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream - state was inconsistent. -*/ - -ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); -/* Return flags indicating compile-time options. - - Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: - 1.0: size of uInt - 3.2: size of uLong - 5.4: size of voidpf (pointer) - 7.6: size of z_off_t - - Compiler, assembler, and debug options: - 8: DEBUG - 9: ASMV or ASMINF -- use ASM code - 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention - 11: 0 (reserved) - - One-time table building (smaller code, but not thread-safe if true): - 12: BUILDFIXED -- build static block decoding tables when needed - 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed - 14,15: 0 (reserved) - - Library content (indicates missing functionality): - 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking - deflate code when not needed) - 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect - and decode gzip streams (to avoid linking crc code) - 18-19: 0 (reserved) - - Operation variations (changes in library functionality): - 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate - 21: FASTEST -- deflate algorithm with only one, lowest compression level - 22,23: 0 (reserved) - - The sprintf variant used by gzprintf (zero is best): - 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format - 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! - 26: 0 = returns value, 1 = void -- 1 means inferred string length returned - - Remainder: - 27-31: 0 (reserved) - */ - - - /* utility functions */ - -/* - The following utility functions are implemented on top of the - basic stream-oriented functions. To simplify the interface, some - default options are assumed (compression level and memory usage, - standard memory allocation functions). The source code of these - utility functions can easily be modified if you need special options. -*/ - -ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); -/* - Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be at least the value returned - by compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - This function can be used to compress a whole file at once if the - input file is mmap'ed. - compress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer. -*/ - -ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen, - int level)); -/* - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ - -ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); -/* - compressBound() returns an upper bound on the compressed size after - compress() or compress2() on sourceLen bytes. It would be used before - a compress() or compress2() call to allocate the destination buffer. -*/ - -ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); -/* - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. - This function can be used to decompress a whole file at once if the - input file is mmap'ed. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. -*/ - - -typedef voidp gzFile; - -ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); -/* - Opens a gzip (.gz) file for reading or writing. The mode parameter - is as in fopen ("rb" or "wb") but can also include a compression level - ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for - Huffman only compression as in "wb1h", or 'R' for run-length encoding - as in "wb1R". (See the description of deflateInit2 for more information - about the strategy parameter.) - - gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. - - gzopen returns NULL if the file could not be opened or if there was - insufficient memory to allocate the (de)compression state; errno - can be checked to distinguish the two cases (if errno is zero, the - zlib error is Z_MEM_ERROR). */ - -ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); -/* - gzdopen() associates a gzFile with the file descriptor fd. File - descriptors are obtained from calls like open, dup, creat, pipe or - fileno (in the file has been previously opened with fopen). - The mode parameter is as in gzopen. - The next call of gzclose on the returned gzFile will also close the - file descriptor fd, just like fclose(fdopen(fd), mode) closes the file - descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). - gzdopen returns NULL if there was insufficient memory to allocate - the (de)compression state. -*/ - -ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); -/* - Dynamically update the compression level or strategy. See the description - of deflateInit2 for the meaning of these parameters. - gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not - opened for writing. -*/ - -ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); -/* - Reads the given number of uncompressed bytes from the compressed file. - If the input file was not in gzip format, gzread copies the given number - of bytes into the buffer. - gzread returns the number of uncompressed bytes actually read (0 for - end of file, -1 for error). */ - -ZEXTERN int ZEXPORT gzwrite OF((gzFile file, - voidpc buf, unsigned len)); -/* - Writes the given number of uncompressed bytes into the compressed file. - gzwrite returns the number of uncompressed bytes actually written - (0 in case of error). -*/ - -ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); -/* - Converts, formats, and writes the args to the compressed file under - control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written (0 in case of error). The number of - uncompressed bytes written is limited to 4095. The caller should assure that - this limit is not exceeded. If it is exceeded, then gzprintf() will return - return an error (0) with nothing written. In this case, there may also be a - buffer overflow with unpredictable consequences, which is possible only if - zlib was compiled with the insecure functions sprintf() or vsprintf() - because the secure snprintf() or vsnprintf() functions were not available. -*/ - -ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); -/* - Writes the given null-terminated string to the compressed file, excluding - the terminating null character. - gzputs returns the number of characters written, or -1 in case of error. -*/ - -ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); -/* - Reads bytes from the compressed file until len-1 characters are read, or - a newline character is read and transferred to buf, or an end-of-file - condition is encountered. The string is then terminated with a null - character. - gzgets returns buf, or Z_NULL in case of error. -*/ - -ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); -/* - Writes c, converted to an unsigned char, into the compressed file. - gzputc returns the value that was written, or -1 in case of error. -*/ - -ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); -/* - Reads one byte from the compressed file. gzgetc returns this byte - or -1 in case of end of file or error. -*/ - -ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); -/* - Push one character back onto the stream to be read again later. - Only one character of push-back is allowed. gzungetc() returns the - character pushed, or -1 on failure. gzungetc() will fail if a - character has been pushed but not read yet, or if c is -1. The pushed - character will be discarded if the stream is repositioned with gzseek() - or gzrewind(). -*/ - -ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); -/* - Flushes all pending output into the compressed file. The parameter - flush is as in the deflate() function. The return value is the zlib - error number (see function gzerror below). gzflush returns Z_OK if - the flush parameter is Z_FINISH and all output could be flushed. - gzflush should be called only when strictly necessary because it can - degrade compression. -*/ - -ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, - z_off_t offset, int whence)); -/* - Sets the starting position for the next gzread or gzwrite on the - given compressed file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); - the value SEEK_END is not supported. - If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are - supported; gzseek then compresses a sequence of zeroes up to the new - starting position. - - gzseek returns the resulting offset location as measured in bytes from - the beginning of the uncompressed stream, or -1 in case of error, in - particular if the file is opened for writing and the new starting position - would be before the current position. -*/ - -ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); -/* - Rewinds the given file. This function is supported only for reading. - - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) -*/ - -ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); -/* - Returns the starting position for the next gzread or gzwrite on the - given compressed file. This position represents a number of bytes in the - uncompressed data stream. - - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) -*/ - -ZEXTERN int ZEXPORT gzeof OF((gzFile file)); -/* - Returns 1 when EOF has previously been detected reading the given - input stream, otherwise zero. -*/ - -ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); -/* - Returns 1 if file is being read directly without decompression, otherwise - zero. -*/ - -ZEXTERN int ZEXPORT gzclose OF((gzFile file)); -/* - Flushes all pending output if necessary, closes the compressed file - and deallocates all the (de)compression state. The return value is the zlib - error number (see function gzerror below). -*/ - -ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); -/* - Returns the error message for the last error which occurred on the - given compressed file. errnum is set to zlib error number. If an - error occurred in the file system and not in the compression library, - errnum is set to Z_ERRNO and the application may consult errno - to get the exact error code. -*/ - -ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); -/* - Clears the error and end-of-file flags for file. This is analogous to the - clearerr() function in stdio. This is useful for continuing to read a gzip - file that is being written concurrently. -*/ - - /* checksum functions */ - -/* - These functions are not related to compression but are exported - anyway because they might be useful in applications using the - compression library. -*/ - -ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); -/* - Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. If buf is NULL, this function returns - the required initial value for the checksum. - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed - much faster. Usage example: - - uLong adler = adler32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - adler = adler32(adler, buffer, length); - } - if (adler != original_adler) error(); -*/ - -ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, - z_off_t len2)); -/* - Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 - and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for - each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. -*/ - -ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); -/* - Update a running CRC-32 with the bytes buf[0..len-1] and return the - updated CRC-32. If buf is NULL, this function returns the required initial - value for the for the crc. Pre- and post-conditioning (one's complement) is - performed within this function so it shouldn't be done by the application. - Usage example: - - uLong crc = crc32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - crc = crc32(crc, buffer, length); - } - if (crc != original_crc) error(); -*/ - -ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); - -/* - Combine two CRC-32 check values into one. For two sequences of bytes, - seq1 and seq2 with lengths len1 and len2, CRC-32 check values were - calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 - check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. -*/ - - - /* various hacks, don't look :) */ - -/* deflateInit and inflateInit are macros to allow checking the zlib version - * and the compiler's view of z_stream: - */ -ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size)); -ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size)); -#define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) -#define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) -#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, sizeof(z_stream)) -#define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) -#define inflateBackInit(strm, windowBits, window) \ - inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, sizeof(z_stream)) - - -#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) - struct internal_state {int dummy;}; /* hack for buggy compilers */ -#endif - -ZEXTERN const char * ZEXPORT zError OF((int)); -ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); -ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); - -#ifdef __cplusplus -} -#endif - -#endif /* ZLIB_H */ diff --git a/extlib/zlib.lib b/extlib/zlib.lib deleted file mode 100644 index 307bd08..0000000 Binary files a/extlib/zlib.lib and /dev/null differ diff --git a/file.cpp b/file.cpp deleted file mode 100644 index e9221b1..0000000 --- a/file.cpp +++ /dev/null @@ -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 *m = (IdList *)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 *m = - (IdList *)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); - } - } -} - diff --git a/font.table b/font.table deleted file mode 100644 index 2bfc93d..0000000 --- a/font.table +++ /dev/null @@ -1,1774 +0,0 @@ - - -#define PEN_UP 127 - -static const struct { - int points; - int width; - signed char coord[120]; -} Font[] = { - // character 32 (' ') - { 0, 16, - { 1 - } - }, - - // character 33 ('!') - { 8, 10, - { - 5, 21, - 5, 7, - PEN_UP, PEN_UP, - 5, 2, - 4, 1, - 5, 0, - 6, 1, - 5, 2, - } - }, - - // character 34 ('"') - { 5, 16, - { - 4, 21, - 4, 14, - PEN_UP, PEN_UP, - 12, 21, - 12, 14, - } - }, - - // character 35 ('#') - { 11, 21, - { - 11, 25, - 4, -7, - PEN_UP, PEN_UP, - 17, 25, - 10, -7, - PEN_UP, PEN_UP, - 4, 12, - 18, 12, - PEN_UP, PEN_UP, - 3, 6, - 17, 6, - } - }, - - // character 36 ('$') - { 26, 20, - { - 8, 25, - 8, -4, - PEN_UP, PEN_UP, - 12, 25, - 12, -4, - PEN_UP, PEN_UP, - 17, 18, - 15, 20, - 12, 21, - 8, 21, - 5, 20, - 3, 18, - 3, 16, - 4, 14, - 5, 13, - 7, 12, - 13, 10, - 15, 9, - 16, 8, - 17, 6, - 17, 3, - 15, 1, - 12, 0, - 8, 0, - 5, 1, - 3, 3, - } - }, - - // character 37 ('%') - { 31, 24, - { - 21, 21, - 3, 0, - PEN_UP, PEN_UP, - 8, 21, - 10, 19, - 10, 17, - 9, 15, - 7, 14, - 5, 14, - 3, 16, - 3, 18, - 4, 20, - 6, 21, - 8, 21, - 10, 20, - 13, 19, - 16, 19, - 19, 20, - 21, 21, - PEN_UP, PEN_UP, - 17, 7, - 15, 6, - 14, 4, - 14, 2, - 16, 0, - 18, 0, - 20, 1, - 21, 3, - 21, 5, - 19, 7, - 17, 7, - } - }, - - // character 38 ('&') - { 34, 26, - { - 23, 12, - 23, 13, - 22, 14, - 21, 14, - 20, 13, - 19, 11, - 17, 6, - 15, 3, - 13, 1, - 11, 0, - 7, 0, - 5, 1, - 4, 2, - 3, 4, - 3, 6, - 4, 8, - 5, 9, - 12, 13, - 13, 14, - 14, 16, - 14, 18, - 13, 20, - 11, 21, - 9, 20, - 8, 18, - 8, 16, - 9, 13, - 11, 10, - 16, 3, - 18, 1, - 20, 0, - 22, 0, - 23, 1, - 23, 2, - } - }, - - // character 39 (''') - { 7, 10, - { - 5, 19, - 4, 20, - 5, 21, - 6, 20, - 6, 18, - 5, 16, - 4, 15, - } - }, - - // character 40 ('(') - { 10, 14, - { - 11, 25, - 9, 23, - 7, 20, - 5, 16, - 4, 11, - 4, 7, - 5, 2, - 7, -2, - 9, -5, - 11, -7, - } - }, - - // character 41 (')') - { 10, 14, - { - 3, 25, - 5, 23, - 7, 20, - 9, 16, - 10, 11, - 10, 7, - 9, 2, - 7, -2, - 5, -5, - 3, -7, - } - }, - - // character 42 ('*') - { 8, 16, - { - 8, 21, - 8, 9, - PEN_UP, PEN_UP, - 3, 18, - 13, 12, - PEN_UP, PEN_UP, - 13, 18, - 3, 12, - } - }, - - // character 43 ('+') - { 5, 26, - { - 13, 18, - 13, 0, - PEN_UP, PEN_UP, - 4, 9, - 22, 9, - } - }, - - // character 44 (',') - { 8, 10, - { - 6, 1, - 5, 0, - 4, 1, - 5, 2, - 6, 1, - 6, -1, - 5, -3, - 4, -4, - } - }, - - // character 45 ('-') - { 2, 26, - { - 4, 9, - 22, 9, - } - }, - - // character 46 ('.') - { 5, 10, - { - 5, 2, - 4, 1, - 5, 0, - 6, 1, - 5, 2, - } - }, - - // character 47 ('/') - { 2, 22, - { - 20, 25, - 2, -7, - } - }, - - // character 48 ('0') - { 17, 20, - { - 9, 21, - 6, 20, - 4, 17, - 3, 12, - 3, 9, - 4, 4, - 6, 1, - 9, 0, - 11, 0, - 14, 1, - 16, 4, - 17, 9, - 17, 12, - 16, 17, - 14, 20, - 11, 21, - 9, 21, - } - }, - - // character 49 ('1') - { 4, 20, - { - 6, 17, - 8, 18, - 11, 21, - 11, 0, - } - }, - - // character 50 ('2') - { 14, 20, - { - 4, 16, - 4, 17, - 5, 19, - 6, 20, - 8, 21, - 12, 21, - 14, 20, - 15, 19, - 16, 17, - 16, 15, - 15, 13, - 13, 10, - 3, 0, - 17, 0, - } - }, - - // character 51 ('3') - { 15, 20, - { - 5, 21, - 16, 21, - 10, 13, - 13, 13, - 15, 12, - 16, 11, - 17, 8, - 17, 6, - 16, 3, - 14, 1, - 11, 0, - 8, 0, - 5, 1, - 4, 2, - 3, 4, - } - }, - - // character 52 ('4') - { 6, 20, - { - 13, 21, - 3, 7, - 18, 7, - PEN_UP, PEN_UP, - 13, 21, - 13, 0, - } - }, - - // character 53 ('5') - { 17, 20, - { - 15, 21, - 5, 21, - 4, 12, - 5, 13, - 8, 14, - 11, 14, - 14, 13, - 16, 11, - 17, 8, - 17, 6, - 16, 3, - 14, 1, - 11, 0, - 8, 0, - 5, 1, - 4, 2, - 3, 4, - } - }, - - // character 54 ('6') - { 23, 20, - { - 16, 18, - 15, 20, - 12, 21, - 10, 21, - 7, 20, - 5, 17, - 4, 12, - 4, 7, - 5, 3, - 7, 1, - 10, 0, - 11, 0, - 14, 1, - 16, 3, - 17, 6, - 17, 7, - 16, 10, - 14, 12, - 11, 13, - 10, 13, - 7, 12, - 5, 10, - 4, 7, - } - }, - - // character 55 ('7') - { 5, 20, - { - 17, 21, - 7, 0, - PEN_UP, PEN_UP, - 3, 21, - 17, 21, - } - }, - - // character 56 ('8') - { 29, 20, - { - 8, 21, - 5, 20, - 4, 18, - 4, 16, - 5, 14, - 7, 13, - 11, 12, - 14, 11, - 16, 9, - 17, 7, - 17, 4, - 16, 2, - 15, 1, - 12, 0, - 8, 0, - 5, 1, - 4, 2, - 3, 4, - 3, 7, - 4, 9, - 6, 11, - 9, 12, - 13, 13, - 15, 14, - 16, 16, - 16, 18, - 15, 20, - 12, 21, - 8, 21, - } - }, - - // character 57 ('9') - { 23, 20, - { - 16, 14, - 15, 11, - 13, 9, - 10, 8, - 9, 8, - 6, 9, - 4, 11, - 3, 14, - 3, 15, - 4, 18, - 6, 20, - 9, 21, - 10, 21, - 13, 20, - 15, 18, - 16, 14, - 16, 9, - 15, 4, - 13, 1, - 10, 0, - 8, 0, - 5, 1, - 4, 3, - } - }, - - // character 58 (':') - { 11, 10, - { - 5, 14, - 4, 13, - 5, 12, - 6, 13, - 5, 14, - PEN_UP, PEN_UP, - 5, 2, - 4, 1, - 5, 0, - 6, 1, - 5, 2, - } - }, - - // character 59 (';') - { 14, 10, - { - 5, 14, - 4, 13, - 5, 12, - 6, 13, - 5, 14, - PEN_UP, PEN_UP, - 6, 1, - 5, 0, - 4, 1, - 5, 2, - 6, 1, - 6, -1, - 5, -3, - 4, -4, - } - }, - - // character 60 ('<') - { 3, 24, - { - 20, 18, - 4, 9, - 20, 0, - } - }, - - // character 61 ('=') - { 5, 26, - { - 4, 12, - 22, 12, - PEN_UP, PEN_UP, - 4, 6, - 22, 6, - } - }, - - // character 62 ('>') - { 3, 24, - { - 4, 18, - 20, 9, - 4, 0, - } - }, - - // character 63 ('?') - { 20, 18, - { - 3, 16, - 3, 17, - 4, 19, - 5, 20, - 7, 21, - 11, 21, - 13, 20, - 14, 19, - 15, 17, - 15, 15, - 14, 13, - 13, 12, - 9, 10, - 9, 7, - PEN_UP, PEN_UP, - 9, 2, - 8, 1, - 9, 0, - 10, 1, - 9, 2, - } - }, - - // character 64 ('@') - { 55, 27, - { - 18, 13, - 17, 15, - 15, 16, - 12, 16, - 10, 15, - 9, 14, - 8, 11, - 8, 8, - 9, 6, - 11, 5, - 14, 5, - 16, 6, - 17, 8, - PEN_UP, PEN_UP, - 12, 16, - 10, 14, - 9, 11, - 9, 8, - 10, 6, - 11, 5, - PEN_UP, PEN_UP, - 18, 16, - 17, 8, - 17, 6, - 19, 5, - 21, 5, - 23, 7, - 24, 10, - 24, 12, - 23, 15, - 22, 17, - 20, 19, - 18, 20, - 15, 21, - 12, 21, - 9, 20, - 7, 19, - 5, 17, - 4, 15, - 3, 12, - 3, 9, - 4, 6, - 5, 4, - 7, 2, - 9, 1, - 12, 0, - 15, 0, - 18, 1, - 20, 2, - 21, 3, - PEN_UP, PEN_UP, - 19, 16, - 18, 8, - 18, 6, - 19, 5, - } - }, - - // character 65 ('A') - { 8, 18, - { - 9, 21, - 1, 0, - PEN_UP, PEN_UP, - 9, 21, - 17, 0, - PEN_UP, PEN_UP, - 4, 7, - 14, 7, - } - }, - - // character 66 ('B') - { 23, 21, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 21, - 13, 21, - 16, 20, - 17, 19, - 18, 17, - 18, 15, - 17, 13, - 16, 12, - 13, 11, - PEN_UP, PEN_UP, - 4, 11, - 13, 11, - 16, 10, - 17, 9, - 18, 7, - 18, 4, - 17, 2, - 16, 1, - 13, 0, - 4, 0, - } - }, - - // character 67 ('C') - { 18, 21, - { - 18, 16, - 17, 18, - 15, 20, - 13, 21, - 9, 21, - 7, 20, - 5, 18, - 4, 16, - 3, 13, - 3, 8, - 4, 5, - 5, 3, - 7, 1, - 9, 0, - 13, 0, - 15, 1, - 17, 3, - 18, 5, - } - }, - - // character 68 ('D') - { 15, 21, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 21, - 11, 21, - 14, 20, - 16, 18, - 17, 16, - 18, 13, - 18, 8, - 17, 5, - 16, 3, - 14, 1, - 11, 0, - 4, 0, - } - }, - - // character 69 ('E') - { 11, 19, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 21, - 17, 21, - PEN_UP, PEN_UP, - 4, 11, - 12, 11, - PEN_UP, PEN_UP, - 4, 0, - 17, 0, - } - }, - - // character 70 ('F') - { 8, 18, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 21, - 17, 21, - PEN_UP, PEN_UP, - 4, 11, - 12, 11, - } - }, - - // character 71 ('G') - { 22, 21, - { - 18, 16, - 17, 18, - 15, 20, - 13, 21, - 9, 21, - 7, 20, - 5, 18, - 4, 16, - 3, 13, - 3, 8, - 4, 5, - 5, 3, - 7, 1, - 9, 0, - 13, 0, - 15, 1, - 17, 3, - 18, 5, - 18, 8, - PEN_UP, PEN_UP, - 13, 8, - 18, 8, - } - }, - - // character 72 ('H') - { 8, 22, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 18, 21, - 18, 0, - PEN_UP, PEN_UP, - 4, 11, - 18, 11, - } - }, - - // character 73 ('I') - { 2, 8, - { - 4, 21, - 4, 0, - } - }, - - // character 74 ('J') - { 10, 16, - { - 12, 21, - 12, 5, - 11, 2, - 10, 1, - 8, 0, - 6, 0, - 4, 1, - 3, 2, - 2, 5, - 2, 7, - } - }, - - // character 75 ('K') - { 8, 21, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 18, 21, - 4, 7, - PEN_UP, PEN_UP, - 9, 12, - 18, 0, - } - }, - - // character 76 ('L') - { 5, 17, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 0, - 16, 0, - } - }, - - // character 77 ('M') - { 11, 24, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 21, - 12, 0, - PEN_UP, PEN_UP, - 20, 21, - 12, 0, - PEN_UP, PEN_UP, - 20, 21, - 20, 0, - } - }, - - // character 78 ('N') - { 8, 22, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 21, - 18, 0, - PEN_UP, PEN_UP, - 18, 21, - 18, 0, - } - }, - - // character 79 ('O') - { 21, 22, - { - 9, 21, - 7, 20, - 5, 18, - 4, 16, - 3, 13, - 3, 8, - 4, 5, - 5, 3, - 7, 1, - 9, 0, - 13, 0, - 15, 1, - 17, 3, - 18, 5, - 19, 8, - 19, 13, - 18, 16, - 17, 18, - 15, 20, - 13, 21, - 9, 21, - } - }, - - // character 80 ('P') - { 13, 21, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 21, - 13, 21, - 16, 20, - 17, 19, - 18, 17, - 18, 14, - 17, 12, - 16, 11, - 13, 10, - 4, 10, - } - }, - - // character 81 ('Q') - { 24, 22, - { - 9, 21, - 7, 20, - 5, 18, - 4, 16, - 3, 13, - 3, 8, - 4, 5, - 5, 3, - 7, 1, - 9, 0, - 13, 0, - 15, 1, - 17, 3, - 18, 5, - 19, 8, - 19, 13, - 18, 16, - 17, 18, - 15, 20, - 13, 21, - 9, 21, - PEN_UP, PEN_UP, - 12, 4, - 18, -2, - } - }, - - // character 82 ('R') - { 16, 21, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 21, - 13, 21, - 16, 20, - 17, 19, - 18, 17, - 18, 15, - 17, 13, - 16, 12, - 13, 11, - 4, 11, - PEN_UP, PEN_UP, - 11, 11, - 18, 0, - } - }, - - // character 83 ('S') - { 20, 20, - { - 17, 18, - 15, 20, - 12, 21, - 8, 21, - 5, 20, - 3, 18, - 3, 16, - 4, 14, - 5, 13, - 7, 12, - 13, 10, - 15, 9, - 16, 8, - 17, 6, - 17, 3, - 15, 1, - 12, 0, - 8, 0, - 5, 1, - 3, 3, - } - }, - - // character 84 ('T') - { 5, 16, - { - 8, 21, - 8, 0, - PEN_UP, PEN_UP, - 1, 21, - 15, 21, - } - }, - - // character 85 ('U') - { 10, 22, - { - 4, 21, - 4, 6, - 5, 3, - 7, 1, - 10, 0, - 12, 0, - 15, 1, - 17, 3, - 18, 6, - 18, 21, - } - }, - - // character 86 ('V') - { 5, 18, - { - 1, 21, - 9, 0, - PEN_UP, PEN_UP, - 17, 21, - 9, 0, - } - }, - - // character 87 ('W') - { 11, 24, - { - 2, 21, - 7, 0, - PEN_UP, PEN_UP, - 12, 21, - 7, 0, - PEN_UP, PEN_UP, - 12, 21, - 17, 0, - PEN_UP, PEN_UP, - 22, 21, - 17, 0, - } - }, - - // character 88 ('X') - { 5, 20, - { - 3, 21, - 17, 0, - PEN_UP, PEN_UP, - 17, 21, - 3, 0, - } - }, - - // character 89 ('Y') - { 6, 18, - { - 1, 21, - 9, 11, - 9, 0, - PEN_UP, PEN_UP, - 17, 21, - 9, 11, - } - }, - - // character 90 ('Z') - { 8, 20, - { - 17, 21, - 3, 0, - PEN_UP, PEN_UP, - 3, 21, - 17, 21, - PEN_UP, PEN_UP, - 3, 0, - 17, 0, - } - }, - - // character 91 ('[') - { 11, 14, - { - 4, 25, - 4, -7, - PEN_UP, PEN_UP, - 5, 25, - 5, -7, - PEN_UP, PEN_UP, - 4, 25, - 11, 25, - PEN_UP, PEN_UP, - 4, -7, - 11, -7, - } - }, - - // character 92 ('\') - { 2, 14, - { - 0, 21, - 14, -3, - } - }, - - // character 93 (']') - { 11, 14, - { - 9, 25, - 9, -7, - PEN_UP, PEN_UP, - 10, 25, - 10, -7, - PEN_UP, PEN_UP, - 3, 25, - 10, 25, - PEN_UP, PEN_UP, - 3, -7, - 10, -7, - } - }, - - // character 94 ('^') - { 10, 16, - { - 6, 15, - 8, 18, - 10, 15, - PEN_UP, PEN_UP, - 3, 12, - 8, 17, - 13, 12, - PEN_UP, PEN_UP, - 8, 17, - 8, 0, - } - }, - - // character 95 ('_') - { 2, 16, - { - 0, -2, - 16, -2, - } - }, - - // character 96 ('`') - { 7, 10, - { - 6, 21, - 5, 20, - 4, 18, - 4, 16, - 5, 15, - 6, 16, - 5, 17, - } - }, - - // character 97 ('a') - { 17, 19, - { - 15, 14, - 15, 0, - PEN_UP, PEN_UP, - 15, 11, - 13, 13, - 11, 14, - 8, 14, - 6, 13, - 4, 11, - 3, 8, - 3, 6, - 4, 3, - 6, 1, - 8, 0, - 11, 0, - 13, 1, - 15, 3, - } - }, - - // character 98 ('b') - { 17, 19, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 11, - 6, 13, - 8, 14, - 11, 14, - 13, 13, - 15, 11, - 16, 8, - 16, 6, - 15, 3, - 13, 1, - 11, 0, - 8, 0, - 6, 1, - 4, 3, - } - }, - - // character 99 ('c') - { 14, 18, - { - 15, 11, - 13, 13, - 11, 14, - 8, 14, - 6, 13, - 4, 11, - 3, 8, - 3, 6, - 4, 3, - 6, 1, - 8, 0, - 11, 0, - 13, 1, - 15, 3, - } - }, - - // character 100 ('d') - { 17, 19, - { - 15, 21, - 15, 0, - PEN_UP, PEN_UP, - 15, 11, - 13, 13, - 11, 14, - 8, 14, - 6, 13, - 4, 11, - 3, 8, - 3, 6, - 4, 3, - 6, 1, - 8, 0, - 11, 0, - 13, 1, - 15, 3, - } - }, - - // character 101 ('e') - { 17, 18, - { - 3, 8, - 15, 8, - 15, 10, - 14, 12, - 13, 13, - 11, 14, - 8, 14, - 6, 13, - 4, 11, - 3, 8, - 3, 6, - 4, 3, - 6, 1, - 8, 0, - 11, 0, - 13, 1, - 15, 3, - } - }, - - // character 102 ('f') - { 8, 12, - { - 10, 21, - 8, 21, - 6, 20, - 5, 17, - 5, 0, - PEN_UP, PEN_UP, - 2, 14, - 9, 14, - } - }, - - // character 103 ('g') - { 22, 19, - { - 15, 14, - 15, -2, - 14, -5, - 13, -6, - 11, -7, - 8, -7, - 6, -6, - PEN_UP, PEN_UP, - 15, 11, - 13, 13, - 11, 14, - 8, 14, - 6, 13, - 4, 11, - 3, 8, - 3, 6, - 4, 3, - 6, 1, - 8, 0, - 11, 0, - 13, 1, - 15, 3, - } - }, - - // character 104 ('h') - { 10, 19, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 4, 10, - 7, 13, - 9, 14, - 12, 14, - 14, 13, - 15, 10, - 15, 0, - } - }, - - // character 105 ('i') - { 8, 8, - { - 3, 21, - 4, 20, - 5, 21, - 4, 22, - 3, 21, - PEN_UP, PEN_UP, - 4, 14, - 4, 0, - } - }, - - // character 106 ('j') - { 11, 10, - { - 5, 21, - 6, 20, - 7, 21, - 6, 22, - 5, 21, - PEN_UP, PEN_UP, - 6, 14, - 6, -3, - 5, -6, - 3, -7, - 1, -7, - } - }, - - // character 107 ('k') - { 8, 17, - { - 4, 21, - 4, 0, - PEN_UP, PEN_UP, - 14, 14, - 4, 4, - PEN_UP, PEN_UP, - 8, 8, - 15, 0, - } - }, - - // character 108 ('l') - { 2, 8, - { - 4, 21, - 4, 0, - } - }, - - // character 109 ('m') - { 18, 30, - { - 4, 14, - 4, 0, - PEN_UP, PEN_UP, - 4, 10, - 7, 13, - 9, 14, - 12, 14, - 14, 13, - 15, 10, - 15, 0, - PEN_UP, PEN_UP, - 15, 10, - 18, 13, - 20, 14, - 23, 14, - 25, 13, - 26, 10, - 26, 0, - } - }, - - // character 110 ('n') - { 10, 19, - { - 4, 14, - 4, 0, - PEN_UP, PEN_UP, - 4, 10, - 7, 13, - 9, 14, - 12, 14, - 14, 13, - 15, 10, - 15, 0, - } - }, - - // character 111 ('o') - { 17, 19, - { - 8, 14, - 6, 13, - 4, 11, - 3, 8, - 3, 6, - 4, 3, - 6, 1, - 8, 0, - 11, 0, - 13, 1, - 15, 3, - 16, 6, - 16, 8, - 15, 11, - 13, 13, - 11, 14, - 8, 14, - } - }, - - // character 112 ('p') - { 17, 19, - { - 4, 14, - 4, -7, - PEN_UP, PEN_UP, - 4, 11, - 6, 13, - 8, 14, - 11, 14, - 13, 13, - 15, 11, - 16, 8, - 16, 6, - 15, 3, - 13, 1, - 11, 0, - 8, 0, - 6, 1, - 4, 3, - } - }, - - // character 113 ('q') - { 17, 19, - { - 15, 14, - 15, -7, - PEN_UP, PEN_UP, - 15, 11, - 13, 13, - 11, 14, - 8, 14, - 6, 13, - 4, 11, - 3, 8, - 3, 6, - 4, 3, - 6, 1, - 8, 0, - 11, 0, - 13, 1, - 15, 3, - } - }, - - // character 114 ('r') - { 8, 13, - { - 4, 14, - 4, 0, - PEN_UP, PEN_UP, - 4, 8, - 5, 11, - 7, 13, - 9, 14, - 12, 14, - } - }, - - // character 115 ('s') - { 17, 17, - { - 14, 11, - 13, 13, - 10, 14, - 7, 14, - 4, 13, - 3, 11, - 4, 9, - 6, 8, - 11, 7, - 13, 6, - 14, 4, - 14, 3, - 13, 1, - 10, 0, - 7, 0, - 4, 1, - 3, 3, - } - }, - - // character 116 ('t') - { 8, 12, - { - 5, 21, - 5, 4, - 6, 1, - 8, 0, - 10, 0, - PEN_UP, PEN_UP, - 2, 14, - 9, 14, - } - }, - - // character 117 ('u') - { 10, 19, - { - 4, 14, - 4, 4, - 5, 1, - 7, 0, - 10, 0, - 12, 1, - 15, 4, - PEN_UP, PEN_UP, - 15, 14, - 15, 0, - } - }, - - // character 118 ('v') - { 5, 16, - { - 2, 14, - 8, 0, - PEN_UP, PEN_UP, - 14, 14, - 8, 0, - } - }, - - // character 119 ('w') - { 11, 22, - { - 3, 14, - 7, 0, - PEN_UP, PEN_UP, - 11, 14, - 7, 0, - PEN_UP, PEN_UP, - 11, 14, - 15, 0, - PEN_UP, PEN_UP, - 19, 14, - 15, 0, - } - }, - - // character 120 ('x') - { 5, 17, - { - 3, 14, - 14, 0, - PEN_UP, PEN_UP, - 14, 14, - 3, 0, - } - }, - - // character 121 ('y') - { 9, 16, - { - 2, 14, - 8, 0, - PEN_UP, PEN_UP, - 14, 14, - 8, 0, - 6, -4, - 4, -6, - 2, -7, - 1, -7, - } - }, - - // character 122 ('z') - { 8, 17, - { - 14, 14, - 3, 0, - PEN_UP, PEN_UP, - 3, 14, - 14, 14, - PEN_UP, PEN_UP, - 3, 0, - 14, 0, - } - }, - - // character 123 ('{') - { 39, 14, - { - 9, 25, - 7, 24, - 6, 23, - 5, 21, - 5, 19, - 6, 17, - 7, 16, - 8, 14, - 8, 12, - 6, 10, - PEN_UP, PEN_UP, - 7, 24, - 6, 22, - 6, 20, - 7, 18, - 8, 17, - 9, 15, - 9, 13, - 8, 11, - 4, 9, - 8, 7, - 9, 5, - 9, 3, - 8, 1, - 7, 0, - 6, -2, - 6, -4, - 7, -6, - PEN_UP, PEN_UP, - 6, 8, - 8, 6, - 8, 4, - 7, 2, - 6, 1, - 5, -1, - 5, -3, - 6, -5, - 7, -6, - 9, -7, - } - }, - - // character 124 ('|') - { 2, 8, - { - 4, 25, - 4, -7, - } - }, - - // character 125 ('}') - { 39, 14, - { - 5, 25, - 7, 24, - 8, 23, - 9, 21, - 9, 19, - 8, 17, - 7, 16, - 6, 14, - 6, 12, - 8, 10, - PEN_UP, PEN_UP, - 7, 24, - 8, 22, - 8, 20, - 7, 18, - 6, 17, - 5, 15, - 5, 13, - 6, 11, - 10, 9, - 6, 7, - 5, 5, - 5, 3, - 6, 1, - 7, 0, - 8, -2, - 8, -4, - 7, -6, - PEN_UP, PEN_UP, - 8, 8, - 6, 6, - 6, 4, - 7, 2, - 8, 1, - 9, -1, - 9, -3, - 8, -5, - 7, -6, - 5, -7, - } - }, - - // character 126 ('~') - { 23, 24, - { - 3, 6, - 3, 8, - 4, 11, - 6, 12, - 8, 12, - 10, 11, - 14, 8, - 16, 7, - 18, 7, - 20, 8, - 21, 10, - PEN_UP, PEN_UP, - 3, 8, - 4, 10, - 6, 11, - 8, 11, - 10, 10, - 14, 7, - 16, 6, - 18, 6, - 20, 7, - 21, 10, - 21, 12, - } - }, -}; - diff --git a/generate.cpp b/generate.cpp deleted file mode 100644 index 815df5e..0000000 --- a/generate.cpp +++ /dev/null @@ -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 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; -} - diff --git a/glhelper.cpp b/glhelper.cpp deleted file mode 100644 index f7fc6c1..0000000 --- a/glhelper.cpp +++ /dev/null @@ -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); -} - diff --git a/graphicswin.cpp b/graphicswin.cpp deleted file mode 100644 index f38f7bd..0000000 --- a/graphicswin.cpp +++ /dev/null @@ -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 *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 *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; -} - diff --git a/group.cpp b/group.cpp deleted file mode 100644 index 9fc16fa..0000000 --- a/group.cpp +++ /dev/null @@ -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 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, - IdList *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 *l, Expr *expr, int index) { - Equation eq; - eq.e = expr; - eq.h = h.equation(index); - l->Add(&eq); -} - -void Group::GenerateEquations(IdList *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 *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 *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 *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); -} - diff --git a/groupmesh.cpp b/groupmesh.cpp deleted file mode 100644 index 1396b2b..0000000 --- a/groupmesh.cpp +++ /dev/null @@ -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 -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 -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(&(srcg->thisShell), &thisShell); - GenerateForStepAndRepeat (&(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(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(&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); - } - } - } -} - diff --git a/icon.ico b/icon.ico deleted file mode 100644 index e545e2f..0000000 Binary files a/icon.ico and /dev/null differ diff --git a/icons/angle.png b/icons/angle.png deleted file mode 100644 index 7001a9d..0000000 Binary files a/icons/angle.png and /dev/null differ diff --git a/icons/arc.png b/icons/arc.png deleted file mode 100644 index 8c09e1a..0000000 Binary files a/icons/arc.png and /dev/null differ diff --git a/icons/assemble.png b/icons/assemble.png deleted file mode 100644 index 5db2d6b..0000000 Binary files a/icons/assemble.png and /dev/null differ diff --git a/icons/bezier.png b/icons/bezier.png deleted file mode 100644 index 73620cd..0000000 Binary files a/icons/bezier.png and /dev/null differ diff --git a/icons/char-0-check-false.png b/icons/char-0-check-false.png deleted file mode 100644 index b9020c3..0000000 Binary files a/icons/char-0-check-false.png and /dev/null differ diff --git a/icons/char-1-check-true.png b/icons/char-1-check-true.png deleted file mode 100644 index 7f6a42c..0000000 Binary files a/icons/char-1-check-true.png and /dev/null differ diff --git a/icons/char-2-radio-false.png b/icons/char-2-radio-false.png deleted file mode 100644 index f35d6ee..0000000 Binary files a/icons/char-2-radio-false.png and /dev/null differ diff --git a/icons/char-3-radio-true.png b/icons/char-3-radio-true.png deleted file mode 100644 index 5322f58..0000000 Binary files a/icons/char-3-radio-true.png and /dev/null differ diff --git a/icons/circle.png b/icons/circle.png deleted file mode 100644 index aec46a3..0000000 Binary files a/icons/circle.png and /dev/null differ diff --git a/icons/constraint.png b/icons/constraint.png deleted file mode 100644 index a0e1889..0000000 Binary files a/icons/constraint.png and /dev/null differ diff --git a/icons/construction.png b/icons/construction.png deleted file mode 100644 index fe3149a..0000000 Binary files a/icons/construction.png and /dev/null differ diff --git a/icons/edges.png b/icons/edges.png deleted file mode 100644 index d89c046..0000000 Binary files a/icons/edges.png and /dev/null differ diff --git a/icons/equal.png b/icons/equal.png deleted file mode 100644 index f1ae3ff..0000000 Binary files a/icons/equal.png and /dev/null differ diff --git a/icons/extrude.png b/icons/extrude.png deleted file mode 100644 index 6d4a44a..0000000 Binary files a/icons/extrude.png and /dev/null differ diff --git a/icons/faces.png b/icons/faces.png deleted file mode 100644 index 8fd4e36..0000000 Binary files a/icons/faces.png and /dev/null differ diff --git a/icons/hidden-lines.png b/icons/hidden-lines.png deleted file mode 100644 index cf43afb..0000000 Binary files a/icons/hidden-lines.png and /dev/null differ diff --git a/icons/horiz.png b/icons/horiz.png deleted file mode 100644 index 5706c69..0000000 Binary files a/icons/horiz.png and /dev/null differ diff --git a/icons/in3d.png b/icons/in3d.png deleted file mode 100644 index 0d6be07..0000000 Binary files a/icons/in3d.png and /dev/null differ diff --git a/icons/length.png b/icons/length.png deleted file mode 100644 index 8396bb1..0000000 Binary files a/icons/length.png and /dev/null differ diff --git a/icons/line.png b/icons/line.png deleted file mode 100644 index 8a02224..0000000 Binary files a/icons/line.png and /dev/null differ diff --git a/icons/mesh.png b/icons/mesh.png deleted file mode 100644 index dde867b..0000000 Binary files a/icons/mesh.png and /dev/null differ diff --git a/icons/normal.png b/icons/normal.png deleted file mode 100644 index c88c250..0000000 Binary files a/icons/normal.png and /dev/null differ diff --git a/icons/ontoworkplane.png b/icons/ontoworkplane.png deleted file mode 100644 index b476e8f..0000000 Binary files a/icons/ontoworkplane.png and /dev/null differ diff --git a/icons/other-supp.png b/icons/other-supp.png deleted file mode 100644 index 2bb6368..0000000 Binary files a/icons/other-supp.png and /dev/null differ diff --git a/icons/parallel.png b/icons/parallel.png deleted file mode 100644 index 4b8ef99..0000000 Binary files a/icons/parallel.png and /dev/null differ diff --git a/icons/perpendicular.png b/icons/perpendicular.png deleted file mode 100644 index b93d8b3..0000000 Binary files a/icons/perpendicular.png and /dev/null differ diff --git a/icons/point.png b/icons/point.png deleted file mode 100644 index e8a14d5..0000000 Binary files a/icons/point.png and /dev/null differ diff --git a/icons/pointonx.png b/icons/pointonx.png deleted file mode 100644 index ad15c56..0000000 Binary files a/icons/pointonx.png and /dev/null differ diff --git a/icons/rectangle.png b/icons/rectangle.png deleted file mode 100644 index 272dbfa..0000000 Binary files a/icons/rectangle.png and /dev/null differ diff --git a/icons/ref.png b/icons/ref.png deleted file mode 100644 index afd64c3..0000000 Binary files a/icons/ref.png and /dev/null differ diff --git a/icons/same-orientation.png b/icons/same-orientation.png deleted file mode 100644 index c612ee5..0000000 Binary files a/icons/same-orientation.png and /dev/null differ diff --git a/icons/shaded.png b/icons/shaded.png deleted file mode 100644 index 5fb452d..0000000 Binary files a/icons/shaded.png and /dev/null differ diff --git a/icons/sketch-in-3d.png b/icons/sketch-in-3d.png deleted file mode 100644 index 204fd8c..0000000 Binary files a/icons/sketch-in-3d.png and /dev/null differ diff --git a/icons/sketch-in-plane.png b/icons/sketch-in-plane.png deleted file mode 100644 index 86811e8..0000000 Binary files a/icons/sketch-in-plane.png and /dev/null differ diff --git a/icons/step-rotate.png b/icons/step-rotate.png deleted file mode 100644 index 6c38a16..0000000 Binary files a/icons/step-rotate.png and /dev/null differ diff --git a/icons/step-translate.png b/icons/step-translate.png deleted file mode 100644 index faaeff0..0000000 Binary files a/icons/step-translate.png and /dev/null differ diff --git a/icons/symmetric.png b/icons/symmetric.png deleted file mode 100644 index 6eb9cad..0000000 Binary files a/icons/symmetric.png and /dev/null differ diff --git a/icons/tangent-arc.png b/icons/tangent-arc.png deleted file mode 100644 index 0db8ac3..0000000 Binary files a/icons/tangent-arc.png and /dev/null differ diff --git a/icons/text.png b/icons/text.png deleted file mode 100644 index 01eb028..0000000 Binary files a/icons/text.png and /dev/null differ diff --git a/icons/trim.png b/icons/trim.png deleted file mode 100644 index daa0878..0000000 Binary files a/icons/trim.png and /dev/null differ diff --git a/icons/vert.png b/icons/vert.png deleted file mode 100644 index 41eeb6e..0000000 Binary files a/icons/vert.png and /dev/null differ diff --git a/icons/workplane.png b/icons/workplane.png deleted file mode 100644 index c451453..0000000 Binary files a/icons/workplane.png and /dev/null differ diff --git a/mesh.cpp b/mesh.cpp deleted file mode 100644 index d8450fc..0000000 --- a/mesh.cpp +++ /dev/null @@ -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(); -} - diff --git a/modify.cpp b/modify.cpp deleted file mode 100644 index 0e8fcf6..0000000 --- a/modify.cpp +++ /dev/null @@ -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 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; -} - diff --git a/mouse.cpp b/mouse.cpp deleted file mode 100644 index ba9f398..0000000 --- a/mouse.cpp +++ /dev/null @@ -1,1331 +0,0 @@ -//----------------------------------------------------------------------------- -// Anything relating to mouse, keyboard, or 6-DOF mouse input. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -void GraphicsWindow::UpdateDraggedPoint(hEntity hp, double mx, double my) { - Entity *p = SK.GetEntity(hp); - Vector pos = p->PointGetNum(); - UpdateDraggedNum(&pos, mx, my); - p->PointForceTo(pos); -} - -void GraphicsWindow::UpdateDraggedNum(Vector *pos, double mx, double my) { - *pos = pos->Plus(projRight.ScaledBy((mx - orig.mouse.x)/scale)); - *pos = pos->Plus(projUp.ScaledBy((my - orig.mouse.y)/scale)); -} - -void GraphicsWindow::AddPointToDraggedList(hEntity hp) { - Entity *p = SK.GetEntity(hp); - // If an entity and its points are both selected, then its points could - // end up in the list twice. This would be bad, because it would move - // twice as far as the mouse pointer... - List *lhe = &(pending.points); - for(hEntity *hee = lhe->First(); hee; hee = lhe->NextAfter(hee)) { - if(hee->v == hp.v) { - // Exact same point. - return; - } - Entity *pe = SK.GetEntity(*hee); - if(pe->type == p->type && - pe->type != Entity::POINT_IN_2D && - pe->type != Entity::POINT_IN_3D && - pe->group.v == p->group.v) - { - // Transform-type point, from the same group. So it handles the - // same unknowns. - return; - } - } - pending.points.Add(&hp); -} - -void GraphicsWindow::StartDraggingByEntity(hEntity he) { - Entity *e = SK.GetEntity(he); - if(e->IsPoint()) { - AddPointToDraggedList(e->h); - } else if(e->type == Entity::LINE_SEGMENT || - e->type == Entity::ARC_OF_CIRCLE || - e->type == Entity::CUBIC || - e->type == Entity::CUBIC_PERIODIC || - e->type == Entity::CIRCLE || - e->type == Entity::TTF_TEXT) - { - int pts; - EntReqTable::GetEntityInfo(e->type, e->extraPoints, - NULL, &pts, NULL, NULL); - for(int i = 0; i < pts; i++) { - AddPointToDraggedList(e->point[i]); - } - } -} - -void GraphicsWindow::StartDraggingBySelection(void) { - List *ls = &(selection); - for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) { - if(!s->entity.v) continue; - - StartDraggingByEntity(s->entity); - } - // The user might select a point, and then click it again to start - // dragging; but the point just got unselected by that click. So drag - // the hovered item too, and they'll always have it. - if(hover.entity.v) StartDraggingByEntity(hover.entity); -} - -void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, - bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown) -{ - if(GraphicsEditControlIsVisible()) return; - if(context.active) return; - - SS.extraLine.draw = false; - - if(!orig.mouseDown) { - // If someone drags the mouse into our window with the left button - // already depressed, then we don't have our starting point; so - // don't try. - leftDown = false; - } - - if(rightDown) { - middleDown = true; - shiftDown = !shiftDown; - } - - if(SS.showToolbar) { - if(ToolbarMouseMoved((int)x, (int)y)) { - hover.Clear(); - return; - } - } - - if(!leftDown && (pending.operation == DRAGGING_POINTS || - pending.operation == DRAGGING_MARQUEE)) - { - ClearPending(); - InvalidateGraphics(); - } - - Point2d mp = Point2d::From(x, y); - currentMousePosition = mp; - - if(rightDown && orig.mouse.DistanceTo(mp) < 5 && !orig.startedMoving) { - // Avoid accidentally panning (or rotating if shift is down) if the - // user wants a context menu. - return; - } - orig.startedMoving = true; - - // If the middle button is down, then mouse movement is used to pan and - // rotate our view. This wins over everything else. - if(middleDown) { - hover.Clear(); - - double dx = (x - orig.mouse.x) / scale; - double dy = (y - orig.mouse.y) / scale; - - if(!(shiftDown || ctrlDown)) { - double s = 0.3*(PI/180)*scale; // degrees per pixel - projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx); - projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy); - - NormalizeProjectionVectors(); - } else if(ctrlDown) { - double theta = atan2(orig.mouse.y, orig.mouse.x); - theta -= atan2(y, x); - SS.extraLine.draw = true; - SS.extraLine.ptA = UnProjectPoint(Point2d::From(0, 0)); - SS.extraLine.ptB = UnProjectPoint(mp); - - Vector normal = orig.projRight.Cross(orig.projUp); - projRight = orig.projRight.RotatedAbout(normal, theta); - projUp = orig.projUp.RotatedAbout(normal, theta); - - NormalizeProjectionVectors(); - } else { - offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x; - offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y; - offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z; - } - - orig.projRight = projRight; - orig.projUp = projUp; - orig.offset = offset; - orig.mouse.x = x; - orig.mouse.y = y; - - if(SS.TW.shown.screen == TextWindow::SCREEN_EDIT_VIEW) { - if(havePainted) { - SS.later.showTW = true; - } - } - InvalidateGraphics(); - havePainted = false; - return; - } - - if(pending.operation == 0) { - double dm = orig.mouse.DistanceTo(mp); - // If we're currently not doing anything, then see if we should - // start dragging something. - if(leftDown && dm > 3) { - Entity *e = NULL; - if(hover.entity.v) e = SK.GetEntity(hover.entity); - if(e && e->type != Entity::WORKPLANE) { - Entity *e = SK.GetEntity(hover.entity); - if(e->type == Entity::CIRCLE && selection.n <= 1) { - // Drag the radius. - ClearSelection(); - pending.circle = hover.entity; - pending.operation = DRAGGING_RADIUS; - } else if(e->IsNormal()) { - ClearSelection(); - pending.normal = hover.entity; - pending.operation = DRAGGING_NORMAL; - } else { - if(!hoverWasSelectedOnMousedown) { - // The user clicked an unselected entity, which - // means they're dragging just the hovered thing, - // not the full selection. So clear all the selection - // except that entity. - ClearSelection(); - MakeSelected(e->h); - } - StartDraggingBySelection(); - if(!hoverWasSelectedOnMousedown) { - // And then clear the selection again, since they - // probably didn't want that selected if they just - // were dragging it. - ClearSelection(); - } - hover.Clear(); - pending.operation = DRAGGING_POINTS; - } - } else if(hover.constraint.v && - SK.GetConstraint(hover.constraint)->HasLabel()) - { - ClearSelection(); - pending.constraint = hover.constraint; - pending.operation = DRAGGING_CONSTRAINT; - } - if(pending.operation != 0) { - // We just started a drag, so remember for the undo before - // the drag changes anything. - SS.UndoRemember(); - } else { - if(!hover.constraint.v) { - // That's just marquee selection, which should not cause - // an undo remember. - if(dm > 10) { - if(hover.entity.v) { - // Avoid accidentally selecting workplanes when - // starting drags. - MakeUnselected(hover.entity, false); - hover.Clear(); - } - pending.operation = DRAGGING_MARQUEE; - orig.marqueePoint = - UnProjectPoint(orig.mouseOnButtonDown); - } - } - } - } else { - // Otherwise, just hit test and give up; but don't hit test - // if the mouse is down, because then the user could hover - // a point, mouse down (thus selecting it), and drag, in an - // effort to drag the point, but instead hover a different - // entity before we move far enough to start the drag. - if(!leftDown) HitTestMakeSelection(mp); - } - return; - } - - // If the user has started an operation from the menu, but not - // completed it, then just do the selection. - if(pending.operation < FIRST_PENDING) { - HitTestMakeSelection(mp); - return; - } - - // We're currently dragging something; so do that. But if we haven't - // painted since the last time we solved, do nothing, because there's - // no sense solving a frame and not displaying it. - if(!havePainted) { - if(pending.operation == DRAGGING_POINTS && ctrlDown) { - SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown); - SS.extraLine.ptB = UnProjectPoint(mp); - SS.extraLine.draw = true; - } - return; - } - switch(pending.operation) { - case DRAGGING_CONSTRAINT: { - Constraint *c = SK.constraint.FindById(pending.constraint); - UpdateDraggedNum(&(c->disp.offset), x, y); - orig.mouse = mp; - InvalidateGraphics(); - break; - } - - case DRAGGING_NEW_LINE_POINT: - case DRAGGING_NEW_POINT: - UpdateDraggedPoint(pending.point, x, y); - HitTestMakeSelection(mp); - SS.MarkGroupDirtyByEntity(pending.point); - orig.mouse = mp; - InvalidateGraphics(); - break; - - case DRAGGING_POINTS: - if(shiftDown || ctrlDown) { - // Edit the rotation associated with a POINT_N_ROT_TRANS, - // either within (ctrlDown) or out of (shiftDown) the plane - // of the screen. So first get the rotation to apply, in qt. - Quaternion qt; - if(ctrlDown) { - double d = mp.DistanceTo(orig.mouseOnButtonDown); - if(d < 25) { - // Don't start dragging the position about the normal - // until we're a little ways out, to get a reasonable - // reference pos - orig.mouse = mp; - break; - } - double theta = atan2(orig.mouse.y-orig.mouseOnButtonDown.y, - orig.mouse.x-orig.mouseOnButtonDown.x); - theta -= atan2(y-orig.mouseOnButtonDown.y, - x-orig.mouseOnButtonDown.x); - - Vector gn = projRight.Cross(projUp); - qt = Quaternion::From(gn, -theta); - - SS.extraLine.draw = true; - SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown); - SS.extraLine.ptB = UnProjectPoint(mp); - } else { - double dx = -(x - orig.mouse.x); - double dy = -(y - orig.mouse.y); - double s = 0.3*(PI/180); // degrees per pixel - qt = Quaternion::From(projUp, -s*dx).Times( - Quaternion::From(projRight, s*dy)); - } - orig.mouse = mp; - - // Now apply this rotation to the points being dragged. - List *lhe = &(pending.points); - for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) { - Entity *e = SK.GetEntity(*he); - if(e->type != Entity::POINT_N_ROT_TRANS) { - if(ctrlDown) { - Vector p = e->PointGetNum(); - p = p.Minus(SS.extraLine.ptA); - p = qt.Rotate(p); - p = p.Plus(SS.extraLine.ptA); - e->PointForceTo(p); - SS.MarkGroupDirtyByEntity(e->h); - } - continue; - } - - Quaternion q = e->PointGetQuaternion(); - Vector p = e->PointGetNum(); - q = qt.Times(q); - e->PointForceQuaternionTo(q); - // Let's rotate about the selected point; so fix up the - // translation so that that point didn't move. - e->PointForceTo(p); - SS.MarkGroupDirtyByEntity(e->h); - } - } else { - List *lhe = &(pending.points); - for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) { - UpdateDraggedPoint(*he, x, y); - SS.MarkGroupDirtyByEntity(*he); - } - orig.mouse = mp; - } - break; - - case DRAGGING_NEW_CUBIC_POINT: { - UpdateDraggedPoint(pending.point, x, y); - HitTestMakeSelection(mp); - - hRequest hr = pending.point.request(); - if(pending.point.v == hr.entity(4).v) { - // The very first segment; dragging final point drags both - // tangent points. - Vector p0 = SK.GetEntity(hr.entity(1))->PointGetNum(), - p3 = SK.GetEntity(hr.entity(4))->PointGetNum(), - p1 = p0.ScaledBy(2.0/3).Plus(p3.ScaledBy(1.0/3)), - p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3)); - SK.GetEntity(hr.entity(1+1))->PointForceTo(p1); - SK.GetEntity(hr.entity(1+2))->PointForceTo(p2); - } else { - // A subsequent segment; dragging point drags only final - // tangent point. - int i = SK.GetEntity(hr.entity(0))->extraPoints; - Vector pn = SK.GetEntity(hr.entity(4+i))->PointGetNum(), - pnm2 = SK.GetEntity(hr.entity(2+i))->PointGetNum(), - pnm1 = (pn.Plus(pnm2)).ScaledBy(0.5); - SK.GetEntity(hr.entity(3+i))->PointForceTo(pnm1); - } - - orig.mouse = mp; - SS.MarkGroupDirtyByEntity(pending.point); - break; - } - case DRAGGING_NEW_ARC_POINT: { - UpdateDraggedPoint(pending.point, x, y); - HitTestMakeSelection(mp); - - hRequest hr = pending.point.request(); - Vector ona = SK.GetEntity(hr.entity(2))->PointGetNum(); - Vector onb = SK.GetEntity(hr.entity(3))->PointGetNum(); - Vector center = (ona.Plus(onb)).ScaledBy(0.5); - - SK.GetEntity(hr.entity(1))->PointForceTo(center); - - orig.mouse = mp; - SS.MarkGroupDirtyByEntity(pending.point); - break; - } - case DRAGGING_NEW_RADIUS: - case DRAGGING_RADIUS: { - Entity *circle = SK.GetEntity(pending.circle); - Vector center = SK.GetEntity(circle->point[0])->PointGetNum(); - Point2d c2 = ProjectPoint(center); - double r = c2.DistanceTo(mp)/scale; - SK.GetEntity(circle->distance)->DistanceForceTo(r); - - SS.MarkGroupDirtyByEntity(pending.circle); - break; - } - - case DRAGGING_NORMAL: { - Entity *normal = SK.GetEntity(pending.normal); - Vector p = SK.GetEntity(normal->point[0])->PointGetNum(); - Point2d p2 = ProjectPoint(p); - - Quaternion q = normal->NormalGetNum(); - Vector u = q.RotationU(), v = q.RotationV(); - - if(ctrlDown) { - double theta = atan2(orig.mouse.y-p2.y, orig.mouse.x-p2.x); - theta -= atan2(y-p2.y, x-p2.x); - - Vector normal = projRight.Cross(projUp); - u = u.RotatedAbout(normal, -theta); - v = v.RotatedAbout(normal, -theta); - } else { - double dx = -(x - orig.mouse.x); - double dy = -(y - orig.mouse.y); - double s = 0.3*(PI/180); // degrees per pixel - u = u.RotatedAbout(projUp, -s*dx); - u = u.RotatedAbout(projRight, s*dy); - v = v.RotatedAbout(projUp, -s*dx); - v = v.RotatedAbout(projRight, s*dy); - } - orig.mouse = mp; - normal->NormalForceTo(Quaternion::From(u, v)); - - SS.MarkGroupDirtyByEntity(pending.normal); - break; - } - - case DRAGGING_MARQUEE: - orig.mouse = mp; - InvalidateGraphics(); - break; - - default: oops(); - } - - if(pending.operation != 0 && - pending.operation != DRAGGING_CONSTRAINT && - pending.operation != DRAGGING_MARQUEE) - { - SS.GenerateAll(); - } - havePainted = false; -} - -void GraphicsWindow::ClearPending(void) { - pending.points.Clear(); - ZERO(&pending); - SS.later.showTW = true; -} - -void GraphicsWindow::MouseMiddleOrRightDown(double x, double y) { - if(GraphicsEditControlIsVisible()) return; - - orig.offset = offset; - orig.projUp = projUp; - orig.projRight = projRight; - orig.mouse.x = x; - orig.mouse.y = y; - orig.startedMoving = false; -} - -void GraphicsWindow::ContextMenuListStyles(void) { - CreateContextSubmenu(); - Style *s; - bool empty = true; - for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) { - if(s->h.v < Style::FIRST_CUSTOM) continue; - - AddContextMenuItem(s->DescriptionString(), CMNU_FIRST_STYLE + s->h.v); - empty = false; - } - - if(!empty) AddContextMenuItem(NULL, CONTEXT_SEPARATOR); - - AddContextMenuItem("No Style", CMNU_NO_STYLE); - AddContextMenuItem("Newly Created Custom Style...", CMNU_NEW_CUSTOM_STYLE); -} - -void GraphicsWindow::MouseRightUp(double x, double y) { - SS.extraLine.draw = false; - InvalidateGraphics(); - - // Don't show a context menu if the user is right-clicking the toolbar, - // or if they are finishing a pan. - if(ToolbarMouseMoved((int)x, (int)y)) return; - if(orig.startedMoving) return; - - if(context.active) return; - - if(pending.operation == DRAGGING_NEW_LINE_POINT || - pending.operation == DRAGGING_NEW_CUBIC_POINT) - { - // Special case; use a right click to stop drawing lines, since - // a left click would draw another one. This is quicker and more - // intuitive than hitting escape. Likewise for new cubic segments. - ClearPending(); - return; - } - - context.active = true; - - if(!hover.IsEmpty()) { - MakeSelected(&hover); - SS.later.showTW = true; - } - GroupSelection(); - - bool itemsSelected = (gs.n > 0 || gs.constraints > 0); - - if(itemsSelected) { - if(gs.stylables > 0) { - ContextMenuListStyles(); - AddContextMenuItem("Assign to Style", CONTEXT_SUBMENU); - } - if(gs.n + gs.constraints == 1) { - AddContextMenuItem("Group Info", CMNU_GROUP_INFO); - } - if(gs.n + gs.constraints == 1 && gs.stylables == 1) { - AddContextMenuItem("Style Info", CMNU_STYLE_INFO); - } - if(gs.withEndpoints > 0) { - AddContextMenuItem("Select Edge Chain", CMNU_SELECT_CHAIN); - } - if(gs.constraints == 1 && gs.n == 0) { - Constraint *c = SK.GetConstraint(gs.constraint[0]); - if(c->HasLabel() && c->type != Constraint::COMMENT) { - AddContextMenuItem("Toggle Reference Dimension", - CMNU_REFERENCE_DIM); - } - if(c->type == Constraint::ANGLE || - c->type == Constraint::EQUAL_ANGLE) - { - AddContextMenuItem("Other Supplementary Angle", - CMNU_OTHER_ANGLE); - } - } - if(gs.comments > 0 || gs.points > 0) { - AddContextMenuItem("Snap to Grid", CMNU_SNAP_TO_GRID); - } - - if(gs.points == 1) { - Entity *p = SK.GetEntity(gs.point[0]); - Constraint *c; - IdList *lc = &(SK.constraint); - for(c = lc->First(); c; c = lc->NextAfter(c)) { - if(c->type != Constraint::POINTS_COINCIDENT) continue; - if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) { - break; - } - } - if(c) { - AddContextMenuItem("Delete Point-Coincident Constraint", - CMNU_DEL_COINCIDENT); - } - } - AddContextMenuItem(NULL, CONTEXT_SEPARATOR); - if(LockedInWorkplane()) { - AddContextMenuItem("Cut", CMNU_CUT_SEL); - AddContextMenuItem("Copy", CMNU_COPY_SEL); - } - } - - if(SS.clipboard.r.n > 0 && LockedInWorkplane()) { - AddContextMenuItem("Paste", CMNU_PASTE_SEL); - } - - if(itemsSelected) { - AddContextMenuItem("Delete", CMNU_DELETE_SEL); - AddContextMenuItem(NULL, CONTEXT_SEPARATOR); - AddContextMenuItem("Unselect All", CMNU_UNSELECT_ALL); - } - // If only one item is selected, then it must be the one that we just - // selected from the hovered item; in which case unselect all and hovered - // are equivalent. - if(!hover.IsEmpty() && selection.n > 1) { - AddContextMenuItem("Unselect Hovered", CMNU_UNSELECT_HOVERED); - } - - int ret = ShowContextMenu(); - switch(ret) { - case CMNU_UNSELECT_ALL: - MenuEdit(MNU_UNSELECT_ALL); - break; - - case CMNU_UNSELECT_HOVERED: - if(!hover.IsEmpty()) { - MakeUnselected(&hover, true); - } - break; - - case CMNU_SELECT_CHAIN: - MenuEdit(MNU_SELECT_CHAIN); - break; - - case CMNU_CUT_SEL: - MenuClipboard(MNU_CUT); - break; - - case CMNU_COPY_SEL: - MenuClipboard(MNU_COPY); - break; - - case CMNU_PASTE_SEL: - MenuClipboard(MNU_PASTE); - break; - - case CMNU_DELETE_SEL: - MenuClipboard(MNU_DELETE); - break; - - case CMNU_REFERENCE_DIM: - Constraint::MenuConstrain(MNU_REFERENCE); - break; - - case CMNU_OTHER_ANGLE: - Constraint::MenuConstrain(MNU_OTHER_ANGLE); - break; - - case CMNU_DEL_COINCIDENT: { - SS.UndoRemember(); - if(!gs.point[0].v) break; - Entity *p = SK.GetEntity(gs.point[0]); - if(!p->IsPoint()) break; - - SK.constraint.ClearTags(); - Constraint *c; - for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) { - if(c->type != Constraint::POINTS_COINCIDENT) continue; - if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) { - c->tag = 1; - } - } - SK.constraint.RemoveTagged(); - ClearSelection(); - break; - } - - case CMNU_SNAP_TO_GRID: - MenuEdit(MNU_SNAP_TO_GRID); - break; - - case CMNU_GROUP_INFO: { - hGroup hg; - if(gs.entities == 1) { - hg = SK.GetEntity(gs.entity[0])->group; - } else if(gs.points == 1) { - hg = SK.GetEntity(gs.point[0])->group; - } else if(gs.constraints == 1) { - hg = SK.GetConstraint(gs.constraint[0])->group; - } else { - break; - } - ClearSelection(); - - SS.TW.GoToScreen(TextWindow::SCREEN_GROUP_INFO); - SS.TW.shown.group = hg; - SS.later.showTW = true; - break; - } - - case CMNU_STYLE_INFO: { - hStyle hs; - if(gs.entities == 1) { - hs = Style::ForEntity(gs.entity[0]); - } else if(gs.points == 1) { - hs = Style::ForEntity(gs.point[0]); - } else if(gs.constraints == 1) { - hs = SK.GetConstraint(gs.constraint[0])->disp.style; - if(!hs.v) hs.v = Style::CONSTRAINT; - } else { - break; - } - ClearSelection(); - - SS.TW.GoToScreen(TextWindow::SCREEN_STYLE_INFO); - SS.TW.shown.style = hs; - SS.later.showTW = true; - break; - } - - case CMNU_NEW_CUSTOM_STYLE: { - DWORD v = Style::CreateCustomStyle(); - Style::AssignSelectionToStyle(v); - break; - } - - case CMNU_NO_STYLE: - Style::AssignSelectionToStyle(0); - break; - - default: - if(ret >= CMNU_FIRST_STYLE) { - Style::AssignSelectionToStyle(ret - CMNU_FIRST_STYLE); - } else { - // otherwise it was cancelled, so do nothing - contextMenuCancelTime = GetMilliseconds(); - } - break; - } - - context.active = false; - SS.later.showTW = true; -} - -hRequest GraphicsWindow::AddRequest(int type) { - return AddRequest(type, true); -} -hRequest GraphicsWindow::AddRequest(int type, bool rememberForUndo) { - if(rememberForUndo) SS.UndoRemember(); - - Request r; - memset(&r, 0, sizeof(r)); - r.group = activeGroup; - Group *g = SK.GetGroup(activeGroup); - if(g->type == Group::DRAWING_3D || g->type == Group::DRAWING_WORKPLANE) { - r.construction = false; - } else { - r.construction = true; - } - r.workplane = ActiveWorkplane(); - r.type = type; - SK.request.AddAndAssignId(&r); - - // We must regenerate the parameters, so that the code that tries to - // place this request's entities where the mouse is can do so. But - // we mustn't try to solve until reasonable values have been supplied - // for these new parameters, or else we'll get a numerical blowup. - SS.GenerateAll(-1, -1); - SS.MarkGroupDirty(r.group); - return r.h; -} - -bool GraphicsWindow::ConstrainPointByHovered(hEntity pt) { - if(!hover.entity.v) return false; - - Entity *e = SK.GetEntity(hover.entity); - if(e->IsPoint()) { - Constraint::ConstrainCoincident(e->h, pt); - return true; - } - if(e->IsCircle()) { - Constraint::Constrain(Constraint::PT_ON_CIRCLE, - pt, Entity::NO_ENTITY, e->h); - return true; - } - if(e->type == Entity::LINE_SEGMENT) { - Constraint::Constrain(Constraint::PT_ON_LINE, - pt, Entity::NO_ENTITY, e->h); - return true; - } - - return false; -} - -void GraphicsWindow::MouseLeftDown(double mx, double my) { - orig.mouseDown = true; - - if(GraphicsEditControlIsVisible()) { - orig.mouse = Point2d::From(mx, my); - orig.mouseOnButtonDown = orig.mouse; - HideGraphicsEditControl(); - return; - } - SS.TW.HideEditControl(); - - if(SS.showToolbar) { - if(ToolbarMouseDown((int)mx, (int)my)) return; - } - - // Make sure the hover is up to date. - MouseMoved(mx, my, false, false, false, false, false); - orig.mouse.x = mx; - orig.mouse.y = my; - orig.mouseOnButtonDown = orig.mouse; - - // The current mouse location - Vector v = offset.ScaledBy(-1); - v = v.Plus(projRight.ScaledBy(mx/scale)); - v = v.Plus(projUp.ScaledBy(my/scale)); - - hRequest hr; - switch(pending.operation) { - case MNU_DATUM_POINT: - hr = AddRequest(Request::DATUM_POINT); - SK.GetEntity(hr.entity(0))->PointForceTo(v); - ConstrainPointByHovered(hr.entity(0)); - - ClearSuper(); - break; - - case MNU_LINE_SEGMENT: - hr = AddRequest(Request::LINE_SEGMENT); - SK.GetEntity(hr.entity(1))->PointForceTo(v); - ConstrainPointByHovered(hr.entity(1)); - - ClearSuper(); - - pending.operation = DRAGGING_NEW_LINE_POINT; - pending.point = hr.entity(2); - pending.description = "click next point of line, or press Esc"; - SK.GetEntity(pending.point)->PointForceTo(v); - break; - - case MNU_RECTANGLE: { - if(!SS.GW.LockedInWorkplane()) { - Error("Can't draw rectangle in 3d; select a workplane first."); - ClearSuper(); - break; - } - hRequest lns[4]; - int i; - SS.UndoRemember(); - for(i = 0; i < 4; i++) { - lns[i] = AddRequest(Request::LINE_SEGMENT, false); - } - for(i = 0; i < 4; i++) { - Constraint::ConstrainCoincident( - lns[i].entity(1), lns[(i+1)%4].entity(2)); - SK.GetEntity(lns[i].entity(1))->PointForceTo(v); - SK.GetEntity(lns[i].entity(2))->PointForceTo(v); - } - for(i = 0; i < 4; i++) { - Constraint::Constrain( - (i % 2) ? Constraint::HORIZONTAL : Constraint::VERTICAL, - Entity::NO_ENTITY, Entity::NO_ENTITY, - lns[i].entity(0)); - } - ConstrainPointByHovered(lns[2].entity(1)); - - pending.operation = DRAGGING_NEW_POINT; - pending.point = lns[1].entity(2); - pending.description = "click to place other corner of rectangle"; - break; - } - case MNU_CIRCLE: - hr = AddRequest(Request::CIRCLE); - // Centered where we clicked - SK.GetEntity(hr.entity(1))->PointForceTo(v); - // Normal to the screen - SK.GetEntity(hr.entity(32))->NormalForceTo( - Quaternion::From(SS.GW.projRight, SS.GW.projUp)); - // Initial radius zero - SK.GetEntity(hr.entity(64))->DistanceForceTo(0); - - ConstrainPointByHovered(hr.entity(1)); - - ClearSuper(); - - pending.operation = DRAGGING_NEW_RADIUS; - pending.circle = hr.entity(0); - pending.description = "click to set radius"; - break; - - case MNU_ARC: { - if(!SS.GW.LockedInWorkplane()) { - Error("Can't draw arc in 3d; select a workplane first."); - ClearPending(); - break; - } - hr = AddRequest(Request::ARC_OF_CIRCLE); - // This fudge factor stops us from immediately failing to solve - // because of the arc's implicit (equal radius) tangent. - Vector adj = SS.GW.projRight.WithMagnitude(2/SS.GW.scale); - SK.GetEntity(hr.entity(1))->PointForceTo(v.Minus(adj)); - SK.GetEntity(hr.entity(2))->PointForceTo(v); - SK.GetEntity(hr.entity(3))->PointForceTo(v); - ConstrainPointByHovered(hr.entity(2)); - - ClearSuper(); - - pending.operation = DRAGGING_NEW_ARC_POINT; - pending.point = hr.entity(3); - pending.description = "click to place point"; - break; - } - case MNU_CUBIC: - hr = AddRequest(Request::CUBIC); - SK.GetEntity(hr.entity(1))->PointForceTo(v); - SK.GetEntity(hr.entity(2))->PointForceTo(v); - SK.GetEntity(hr.entity(3))->PointForceTo(v); - SK.GetEntity(hr.entity(4))->PointForceTo(v); - ConstrainPointByHovered(hr.entity(1)); - - ClearSuper(); - - pending.operation = DRAGGING_NEW_CUBIC_POINT; - pending.point = hr.entity(4); - pending.description = "click next point of cubic, or press Esc"; - break; - - case MNU_WORKPLANE: - if(LockedInWorkplane()) { - Error("Sketching in a workplane already; sketch in 3d before " - "creating new workplane."); - ClearSuper(); - break; - } - hr = AddRequest(Request::WORKPLANE); - SK.GetEntity(hr.entity(1))->PointForceTo(v); - SK.GetEntity(hr.entity(32))->NormalForceTo( - Quaternion::From(SS.GW.projRight, SS.GW.projUp)); - ConstrainPointByHovered(hr.entity(1)); - - ClearSuper(); - break; - - case MNU_TTF_TEXT: { - if(!SS.GW.LockedInWorkplane()) { - Error("Can't draw text in 3d; select a workplane first."); - ClearSuper(); - break; - } - hr = AddRequest(Request::TTF_TEXT); - Request *r = SK.GetRequest(hr); - r->str.strcpy("Abc"); - r->font.strcpy("arial.ttf"); - - SK.GetEntity(hr.entity(1))->PointForceTo(v); - SK.GetEntity(hr.entity(2))->PointForceTo(v); - - pending.operation = DRAGGING_NEW_POINT; - pending.point = hr.entity(2); - pending.description = "click to place bottom left of text"; - break; - } - - case MNU_COMMENT: { - ClearSuper(); - Constraint c; - ZERO(&c); - c.group = SS.GW.activeGroup; - c.workplane = SS.GW.ActiveWorkplane(); - c.type = Constraint::COMMENT; - c.disp.offset = v; - c.comment.strcpy("NEW COMMENT -- DOUBLE-CLICK TO EDIT"); - Constraint::AddConstraint(&c); - break; - } - - case DRAGGING_RADIUS: - case DRAGGING_NEW_POINT: - // The MouseMoved event has already dragged it as desired. - ClearPending(); - break; - - case DRAGGING_NEW_ARC_POINT: - ConstrainPointByHovered(pending.point); - ClearPending(); - break; - - case DRAGGING_NEW_CUBIC_POINT: { - hRequest hr = pending.point.request(); - Request *r = SK.GetRequest(hr); - - if(hover.entity.v == hr.entity(1).v && r->extraPoints >= 2) { - // They want the endpoints coincident, which means a periodic - // spline instead. - r->type = Request::CUBIC_PERIODIC; - // Remove the off-curve control points, which are no longer - // needed here; so move [2,ep+1] down, skipping first pt. - int i; - for(i = 2; i <= r->extraPoints+1; i++) { - SK.GetEntity(hr.entity((i-1)+1))->PointForceTo( - SK.GetEntity(hr.entity(i+1))->PointGetNum()); - } - // and move ep+3 down by two, skipping both - SK.GetEntity(hr.entity((r->extraPoints+1)+1))->PointForceTo( - SK.GetEntity(hr.entity((r->extraPoints+3)+1))->PointGetNum()); - r->extraPoints -= 2; - // And we're done. - SS.MarkGroupDirty(r->group); - SS.later.generateAll = true; - ClearPending(); - break; - } - - if(ConstrainPointByHovered(pending.point)) { - ClearPending(); - break; - } - - Entity e; - if(r->extraPoints >= arraylen(e.point) - 4) { - ClearPending(); - break; - } - - (SK.GetRequest(hr)->extraPoints)++; - SS.GenerateAll(-1, -1); - - int ep = r->extraPoints; - Vector last = SK.GetEntity(hr.entity(3+ep))->PointGetNum(); - - SK.GetEntity(hr.entity(2+ep))->PointForceTo(last); - SK.GetEntity(hr.entity(3+ep))->PointForceTo(v); - SK.GetEntity(hr.entity(4+ep))->PointForceTo(v); - pending.point = hr.entity(4+ep); - break; - } - - case DRAGGING_NEW_LINE_POINT: { - if(hover.entity.v) { - Entity *e = SK.GetEntity(hover.entity); - if(e->IsPoint()) { - hRequest hrl = pending.point.request(); - Entity *sp = SK.GetEntity(hrl.entity(1)); - if(( e->PointGetNum()).Equals( - (sp->PointGetNum()))) - { - // If we constrained by the hovered point, then we - // would create a zero-length line segment. That's - // not good, so just stop drawing. - ClearPending(); - break; - } - } - } - - if(ConstrainPointByHovered(pending.point)) { - ClearPending(); - break; - } - - // Create a new line segment, so that we continue drawing. - hRequest hr = AddRequest(Request::LINE_SEGMENT); - SK.GetEntity(hr.entity(1))->PointForceTo(v); - // Displace the second point of the new line segment slightly, - // to avoid creating zero-length edge warnings. - SK.GetEntity(hr.entity(2))->PointForceTo( - v.Plus(projRight.ScaledBy(0.5/scale))); - - // Constrain the line segments to share an endpoint - Constraint::ConstrainCoincident(pending.point, hr.entity(1)); - - // And drag an endpoint of the new line segment - pending.operation = DRAGGING_NEW_LINE_POINT; - pending.point = hr.entity(2); - pending.description = "click next point of line, or press Esc"; - - break; - } - - case 0: - default: - ClearPending(); - if(!hover.IsEmpty()) { - hoverWasSelectedOnMousedown = IsSelected(&hover); - MakeSelected(&hover); - } - break; - } - - SS.later.showTW = true; - InvalidateGraphics(); -} - -void GraphicsWindow::MouseLeftUp(double mx, double my) { - orig.mouseDown = false; - hoverWasSelectedOnMousedown = false; - - switch(pending.operation) { - case DRAGGING_POINTS: - SS.extraLine.draw = false; - // fall through - case DRAGGING_CONSTRAINT: - case DRAGGING_NORMAL: - case DRAGGING_RADIUS: - ClearPending(); - InvalidateGraphics(); - break; - - case DRAGGING_MARQUEE: - SelectByMarquee(); - ClearPending(); - InvalidateGraphics(); - break; - - case 0: - // We need to clear the selection here, and not in the mouse down - // event, since a mouse down without anything hovered could also - // be the start of marquee selection. But don't do that on the - // left click to cancel a context menu. The time delay is an ugly - // hack. - if(hover.IsEmpty() && - (contextMenuCancelTime == 0 || - (GetMilliseconds() - contextMenuCancelTime) > 200)) - { - ClearSelection(); - } - break; - - default: - break; // do nothing - } -} - -void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { - if(GraphicsEditControlIsVisible()) return; - SS.TW.HideEditControl(); - - if(hover.constraint.v) { - constraintBeingEdited = hover.constraint; - ClearSuper(); - - Constraint *c = SK.GetConstraint(constraintBeingEdited); - if(!c->HasLabel()) { - // Not meaningful to edit a constraint without a dimension - return; - } - if(c->reference) { - // Not meaningful to edit a reference dimension - return; - } - - Vector p3 = c->GetLabelPos(); - Point2d p2 = ProjectPoint(p3); - - char s[1024]; - switch(c->type) { - case Constraint::COMMENT: - strcpy(s, c->comment.str); - break; - - case Constraint::ANGLE: - case Constraint::LENGTH_RATIO: - sprintf(s, "%.3f", c->valA); - break; - - default: { - double v = fabs(c->valA); - char *def = SS.MmToString(v); - double eps = 1e-12; - if(fabs(SS.StringToMm(def) - v) < eps) { - // Show value with default number of digits after decimal, - // which is at least enough to represent it exactly. - strcpy(s, def); - } else { - // Show value with as many digits after decimal as - // required to represent it exactly, up to 10. - v /= SS.MmPerUnit(); - int i; - for(i = 0; i <= 10; i++) { - sprintf(s, "%.*f", i, v); - if(fabs(atof(s) - v) < eps) break; - } - } - break; - } - } - ShowGraphicsEditControl((int)p2.x, (int)p2.y-4, s); - } -} - -void GraphicsWindow::EditControlDone(char *s) { - HideGraphicsEditControl(); - Constraint *c = SK.GetConstraint(constraintBeingEdited); - - if(c->type == Constraint::COMMENT) { - SS.UndoRemember(); - c->comment.strcpy(s); - return; - } - - Expr *e = Expr::From(s, true); - if(e) { - SS.UndoRemember(); - - switch(c->type) { - case Constraint::PROJ_PT_DISTANCE: - case Constraint::PT_LINE_DISTANCE: - case Constraint::PT_FACE_DISTANCE: - case Constraint::PT_PLANE_DISTANCE: { - // The sign is not displayed to the user, but this is a signed - // distance internally. To flip the sign, the user enters a - // negative distance. - bool wasNeg = (c->valA < 0); - if(wasNeg) { - c->valA = -SS.ExprToMm(e); - } else { - c->valA = SS.ExprToMm(e); - } - break; - } - case Constraint::ANGLE: - case Constraint::LENGTH_RATIO: - // These don't get the units conversion for distance, and - // they're always positive - c->valA = fabs(e->Eval()); - break; - - default: - // These are always positive, and they get the units conversion. - c->valA = fabs(SS.ExprToMm(e)); - break; - } - SS.MarkGroupDirty(c->group); - SS.GenerateAll(); - } -} - -bool GraphicsWindow::KeyDown(int c) { - if(c == ('h' - 'a') + 1) { - // Treat backspace identically to escape. - MenuEdit(MNU_UNSELECT_ALL); - return true; - } - - return false; -} - -void GraphicsWindow::MouseScroll(double x, double y, int delta) { - double offsetRight = offset.Dot(projRight); - double offsetUp = offset.Dot(projUp); - - double righti = x/scale - offsetRight; - double upi = y/scale - offsetUp; - - if(delta > 0) { - scale *= 1.2; - } else { - scale /= 1.2; - } - - double rightf = x/scale - offsetRight; - double upf = y/scale - offsetUp; - - offset = offset.Plus(projRight.ScaledBy(rightf - righti)); - offset = offset.Plus(projUp.ScaledBy(upf - upi)); - - if(SS.TW.shown.screen == TextWindow::SCREEN_EDIT_VIEW) { - if(havePainted) { - SS.later.showTW = true; - } - } - havePainted = false; - InvalidateGraphics(); -} - -void GraphicsWindow::MouseLeave(void) { - // Un-hover everything when the mouse leaves our window, unless there's - // currently a context menu shown. - if(!context.active) { - hover.Clear(); - toolbarTooltipped = 0; - toolbarHovered = 0; - PaintGraphics(); - } - SS.extraLine.draw = false; -} - -void GraphicsWindow::SpaceNavigatorMoved(double tx, double ty, double tz, - double rx, double ry, double rz, - bool shiftDown) -{ - if(!havePainted) return; - Vector out = projRight.Cross(projUp); - - // rotation vector is axis of rotation, and its magnitude is angle - Vector aa = Vector::From(rx, ry, rz); - // but it's given with respect to screen projection frame - aa = aa.ScaleOutOfCsys(projRight, projUp, out); - double aam = aa.Magnitude(); - if(aam != 0.0) aa = aa.WithMagnitude(1); - - // This can either transform our view, or transform an imported part. - GroupSelection(); - Entity *e = NULL; - Group *g = NULL; - if(gs.points == 1 && gs.n == 1) e = SK.GetEntity(gs.point [0]); - if(gs.entities == 1 && gs.n == 1) e = SK.GetEntity(gs.entity[0]); - if(e) g = SK.GetGroup(e->group); - if(g && g->type == Group::IMPORTED && !shiftDown) { - // Apply the transformation to an imported part. Gain down the Z - // axis, since it's hard to see what you're doing on that one since - // it's normal to the screen. - Vector t = projRight.ScaledBy(tx/scale).Plus( - projUp .ScaledBy(ty/scale).Plus( - out .ScaledBy(0.1*tz/scale))); - Quaternion q = Quaternion::From(aa, aam); - - // If we go five seconds without SpaceNavigator input, or if we've - // switched groups, then consider that a new action and save an undo - // point. - SDWORD now = GetMilliseconds(); - if(now - lastSpaceNavigatorTime > 5000 || - lastSpaceNavigatorGroup.v != g->h.v) - { - SS.UndoRemember(); - } - - g->TransformImportedBy(t, q); - - lastSpaceNavigatorTime = now; - lastSpaceNavigatorGroup = g->h; - SS.MarkGroupDirty(g->h); - SS.later.generateAll = true; - } else { - // Apply the transformation to the view of the everything. The - // x and y components are translation; but z component is scale, - // not translation, or else it would do nothing in a parallel - // projection - offset = offset.Plus(projRight.ScaledBy(tx/scale)); - offset = offset.Plus(projUp.ScaledBy(ty/scale)); - scale *= exp(0.001*tz); - - if(aam != 0.0) { - projRight = projRight.RotatedAbout(aa, -aam); - projUp = projUp. RotatedAbout(aa, -aam); - NormalizeProjectionVectors(); - } - } - - havePainted = false; - InvalidateGraphics(); -} - -void GraphicsWindow::SpaceNavigatorButtonUp(void) { - ZoomToFit(false); - InvalidateGraphics(); -} - diff --git a/obj/t b/obj/t deleted file mode 100644 index e69de29..0000000 diff --git a/png2c.pl b/png2c.pl deleted file mode 100644 index b6092ef..0000000 --- a/png2c.pl +++ /dev/null @@ -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 () { - 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"; - -} diff --git a/pngchar2c.pl b/pngchar2c.pl deleted file mode 100644 index 99ce60f..0000000 --- a/pngchar2c.pl +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/perl - -use GD; - -for $file (sort ) { - 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"; -} - diff --git a/polygon.cpp b/polygon.cpp deleted file mode 100644 index 46727f0..0000000 --- a/polygon.cpp +++ /dev/null @@ -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); - } - } - } - } -} - diff --git a/request.cpp b/request.cpp deleted file mode 100644 index daae57b..0000000 --- a/request.cpp +++ /dev/null @@ -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, - IdList *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 hp) { - Param pa; - memset(&pa, 0, sizeof(pa)); - pa.h = hp; - param->Add(&pa); - return hp; -} - diff --git a/solvespace.cpp b/solvespace.cpp deleted file mode 100644 index cae3c31..0000000 --- a/solvespace.cpp +++ /dev/null @@ -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(); - } -} diff --git a/constraint.cpp b/src/constraint.cpp similarity index 100% rename from constraint.cpp rename to src/constraint.cpp diff --git a/constrainteq.cpp b/src/constrainteq.cpp similarity index 100% rename from constrainteq.cpp rename to src/constrainteq.cpp diff --git a/dsc.h b/src/dsc.h similarity index 100% rename from dsc.h rename to src/dsc.h diff --git a/entity.cpp b/src/entity.cpp similarity index 100% rename from entity.cpp rename to src/entity.cpp diff --git a/expr.cpp b/src/expr.cpp similarity index 100% rename from expr.cpp rename to src/expr.cpp diff --git a/expr.h b/src/expr.h similarity index 100% rename from expr.h rename to src/expr.h diff --git a/exposed/lib.cpp b/src/lib.cpp similarity index 100% rename from exposed/lib.cpp rename to src/lib.cpp diff --git a/win32/w32util.cpp b/src/platform/w32util.cpp similarity index 100% rename from win32/w32util.cpp rename to src/platform/w32util.cpp diff --git a/polygon.h b/src/polygon.h similarity index 100% rename from polygon.h rename to src/polygon.h diff --git a/sketch.h b/src/sketch.h similarity index 100% rename from sketch.h rename to src/sketch.h diff --git a/exposed/slvs.h b/src/slvs.h similarity index 100% rename from exposed/slvs.h rename to src/slvs.h diff --git a/solvespace.h b/src/solvespace.h similarity index 100% rename from solvespace.h rename to src/solvespace.h diff --git a/srf/surface.h b/src/srf/surface.h similarity index 100% rename from srf/surface.h rename to src/srf/surface.h diff --git a/system.cpp b/src/system.cpp similarity index 100% rename from system.cpp rename to src/system.cpp diff --git a/ui.h b/src/ui.h similarity index 100% rename from ui.h rename to src/ui.h diff --git a/util.cpp b/src/util.cpp similarity index 100% rename from util.cpp rename to src/util.cpp diff --git a/srf/boolean.cpp b/srf/boolean.cpp deleted file mode 100644 index f43b944..0000000 --- a/srf/boolean.cpp +++ /dev/null @@ -1,981 +0,0 @@ -//----------------------------------------------------------------------------- -// Top-level functions to compute the Boolean union or difference between -// two shells of rational polynomial surfaces. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -int I, N, FLAG; - -void SShell::MakeFromUnionOf(SShell *a, SShell *b) { - MakeFromBoolean(a, b, AS_UNION); -} - -void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) { - MakeFromBoolean(a, b, AS_DIFFERENCE); -} - -//----------------------------------------------------------------------------- -// Take our original pwl curve. Wherever an edge intersects a surface within -// either agnstA or agnstB, split the piecewise linear element. Then refine -// the intersection so that it lies on all three relevant surfaces: the -// intersecting surface, srfA, and srfB. (So the pwl curve should lie at -// the intersection of srfA and srfB.) Return a new pwl curve with everything -// split. -//----------------------------------------------------------------------------- -static Vector LineStart, LineDirection; -static int ByTAlongLine(const void *av, const void *bv) -{ - SInter *a = (SInter *)av, - *b = (SInter *)bv; - - double ta = (a->p.Minus(LineStart)).DivPivoting(LineDirection), - tb = (b->p.Minus(LineStart)).DivPivoting(LineDirection); - - return (ta > tb) ? 1 : -1; -} -SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, - SSurface *srfA, SSurface *srfB) -{ - SCurve ret; - ret = *this; - ZERO(&(ret.pts)); - - SCurvePt *p = pts.First(); - if(!p) oops(); - SCurvePt prev = *p; - ret.pts.Add(p); - p = pts.NextAfter(p); - - for(; p; p = pts.NextAfter(p)) { - List il; - ZERO(&il); - - // Find all the intersections with the two passed shells - if(agnstA) - agnstA->AllPointsIntersecting(prev.p, p->p, &il, true, false, true); - if(agnstB) - agnstB->AllPointsIntersecting(prev.p, p->p, &il, true, false, true); - - if(il.n > 0) { - // The intersections were generated by intersecting the pwl - // edge against a surface; so they must be refined to lie - // exactly on the original curve. - il.ClearTags(); - SInter *pi; - for(pi = il.First(); pi; pi = il.NextAfter(pi)) { - if(pi->srf == srfA || pi->srf == srfB) { - // The edge certainly intersects the surfaces that it - // trims (at its endpoints), but those ones don't count. - // They are culled later, but no sense calculating them - // and they will cause numerical problems (since two - // of the three surfaces they're refined to lie on will - // be identical, so the matrix will be singular). - pi->tag = 1; - continue; - } - - Point2d puv; - (pi->srf)->ClosestPointTo(pi->p, &puv, false); - - // Split the edge if the intersection lies within the surface's - // trim curves, or within the chord tol of the trim curve; want - // some slop if points are close to edge and pwl is too coarse, - // and it doesn't hurt to split unnecessarily. - Point2d dummy = { 0, 0 }; - int c = pi->srf->bsp->ClassifyPoint(puv, dummy, pi->srf); - if(c == SBspUv::OUTSIDE) { - double d; - d = pi->srf->bsp->MinimumDistanceToEdge(puv, pi->srf); - if(d > SS.ChordTolMm()) { - pi->tag = 1; - continue; - } - } - - // We're keeping the intersection, so actually refine it. - (pi->srf)->PointOnSurfaces(srfA, srfB, &(puv.x), &(puv.y)); - pi->p = (pi->srf)->PointAt(puv); - } - il.RemoveTagged(); - - // And now sort them in order along the line. Note that we must - // do that after refining, in case the refining would make two - // points switch places. - LineStart = prev.p; - LineDirection = (p->p).Minus(prev.p); - qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine); - - // And now uses the intersections to generate our split pwl edge(s) - Vector prev = Vector::From(VERY_POSITIVE, 0, 0); - for(pi = il.First(); pi; pi = il.NextAfter(pi)) { - double t = (pi->p.Minus(LineStart)).DivPivoting(LineDirection); - // On-edge intersection will generate same split point for - // both surfaces, so don't create zero-length edge. - if(!prev.Equals(pi->p)) { - SCurvePt scpt; - scpt.tag = 0; - scpt.p = pi->p; - scpt.vertex = true; - ret.pts.Add(&scpt); - } - prev = pi->p; - } - } - - il.Clear(); - ret.pts.Add(p); - prev = *p; - } - return ret; -} - -void SShell::CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into) { - SCurve *sc; - for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { - SCurve scn = sc->MakeCopySplitAgainst(agnst, NULL, - surface.FindById(sc->surfA), - surface.FindById(sc->surfB)); - scn.source = opA ? SCurve::FROM_A : SCurve::FROM_B; - - hSCurve hsc = into->curve.AddAndAssignId(&scn); - // And note the new ID so that we can rewrite the trims appropriately - sc->newH = hsc; - } -} - -void SSurface::TrimFromEdgeList(SEdgeList *el, bool asUv) { - el->l.ClearTags(); - - STrimBy stb; - ZERO(&stb); - for(;;) { - // Find an edge, any edge; we'll start from there. - SEdge *se; - for(se = el->l.First(); se; se = el->l.NextAfter(se)) { - if(se->tag) continue; - break; - } - if(!se) break; - se->tag = 1; - stb.start = se->a; - stb.finish = se->b; - stb.curve.v = se->auxA; - stb.backwards = se->auxB ? true : false; - - // Find adjoining edges from the same curve; those should be - // merged into a single trim. - bool merged; - do { - merged = false; - for(se = el->l.First(); se; se = el->l.NextAfter(se)) { - if(se->tag) continue; - if(se->auxA != stb.curve.v) continue; - if(( se->auxB && !stb.backwards) || - (!se->auxB && stb.backwards)) continue; - - if((se->a).Equals(stb.finish)) { - stb.finish = se->b; - se->tag = 1; - merged = true; - } else if((se->b).Equals(stb.start)) { - stb.start = se->a; - se->tag = 1; - merged = true; - } - } - } while(merged); - - if(asUv) { - stb.start = PointAt(stb.start.x, stb.start.y); - stb.finish = PointAt(stb.finish.x, stb.finish.y); - } - - // And add the merged trim, with xyz (not uv like the polygon) pts - trim.Add(&stb); - } -} - -static bool KeepRegion(int type, bool opA, int shell, int orig) -{ - bool inShell = (shell == SShell::INSIDE), - inSame = (shell == SShell::COINC_SAME), - inOpp = (shell == SShell::COINC_OPP), - inOrig = (orig == SShell::INSIDE); - - bool inFace = inSame || inOpp; - - // If these are correct, then they should be independent of inShell - // if inFace is true. - if(!inOrig) return false; - switch(type) { - case SShell::AS_UNION: - if(opA) { - return (!inShell && !inFace); - } else { - return (!inShell && !inFace) || inSame; - } - break; - - case SShell::AS_DIFFERENCE: - if(opA) { - return (!inShell && !inFace); - } else { - return (inShell && !inFace) || inSame; - } - break; - - default: oops(); - } -} -static bool KeepEdge(int type, bool opA, - int indir_shell, int outdir_shell, - int indir_orig, int outdir_orig) -{ - bool keepIn = KeepRegion(type, opA, indir_shell, indir_orig), - keepOut = KeepRegion(type, opA, outdir_shell, outdir_orig); - - // If the regions to the left and right of this edge are both in or both - // out, then this edge is not useful and should be discarded. - if(keepIn && !keepOut) return true; - return false; -} - -static void TagByClassifiedEdge(int bspclass, int *indir, int *outdir) -{ - switch(bspclass) { - case SBspUv::INSIDE: - *indir = SShell::INSIDE; - *outdir = SShell::INSIDE; - break; - - case SBspUv::OUTSIDE: - *indir = SShell::OUTSIDE; - *outdir = SShell::OUTSIDE; - break; - - case SBspUv::EDGE_PARALLEL: - *indir = SShell::INSIDE; - *outdir = SShell::OUTSIDE; - break; - - case SBspUv::EDGE_ANTIPARALLEL: - *indir = SShell::OUTSIDE; - *outdir = SShell::INSIDE; - break; - - default: - dbp("TagByClassifiedEdge: fail!"); - *indir = SShell::OUTSIDE; - *outdir = SShell::OUTSIDE; - break; - } -} - -void DEBUGEDGELIST(SEdgeList *sel, SSurface *surf) { - dbp("print %d edges", sel->l.n); - SEdge *se; - for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) { - Vector mid = (se->a).Plus(se->b).ScaledBy(0.5); - Vector arrow = (se->b).Minus(se->a); - SWAP(double, arrow.x, arrow.y); - arrow.x *= -1; - arrow = arrow.WithMagnitude(0.01); - arrow = arrow.Plus(mid); - - SS.nakedEdges.AddEdge(surf->PointAt(se->a.x, se->a.y), - surf->PointAt(se->b.x, se->b.y)); - SS.nakedEdges.AddEdge(surf->PointAt(mid.x, mid.y), - surf->PointAt(arrow.x, arrow.y)); - } -} - -static char *REGION(int d) { - switch(d) { - case SShell::INSIDE: return "inside"; - case SShell::OUTSIDE: return "outside"; - case SShell::COINC_SAME: return "same"; - case SShell::COINC_OPP: return "opp"; - default: return "xxx"; - } -} - - -//----------------------------------------------------------------------------- -// We are given src, with at least one edge, and avoid, a list of points to -// avoid. We return a chain of edges (that share endpoints), such that no -// point within the avoid list ever occurs in the middle of a chain. And we -// delete the edges in that chain from our source list. -//----------------------------------------------------------------------------- -void SSurface::FindChainAvoiding(SEdgeList *src, SEdgeList *dest, - SPointList *avoid) -{ - if(src->l.n < 1) oops(); - // Start with an arbitrary edge. - dest->l.Add(&(src->l.elem[0])); - src->l.ClearTags(); - src->l.elem[0].tag = 1; - - bool added; - do { - added = false; - // The start and finish of the current edge chain - Vector s = dest->l.elem[0].a, - f = dest->l.elem[dest->l.n - 1].b; - - // We can attach a new edge at the start or finish, as long as that - // start or finish point isn't in the list of points to avoid. - bool startOkay = !avoid->ContainsPoint(s), - finishOkay = !avoid->ContainsPoint(f); - - // Now look for an unused edge that joins at the start or finish of - // our chain (if permitted by the avoid list). - SEdge *se; - for(se = src->l.First(); se; se = src->l.NextAfter(se)) { - if(se->tag) continue; - if(startOkay && s.Equals(se->b)) { - dest->l.AddToBeginning(se); - s = se->a; - se->tag = 1; - startOkay = !avoid->ContainsPoint(s); - } else if(finishOkay && f.Equals(se->a)) { - dest->l.Add(se); - f = se->b; - se->tag = 1; - finishOkay = !avoid->ContainsPoint(f); - } else { - continue; - } - - added = true; - } - } while(added); - - src->l.RemoveTagged(); -} - -void SSurface::EdgeNormalsWithinSurface(Point2d auv, Point2d buv, - Vector *pt, - Vector *enin, Vector *enout, - Vector *surfn, - DWORD auxA, - SShell *shell, SShell *sha, SShell *shb) -{ - // the midpoint of the edge - Point2d muv = (auv.Plus(buv)).ScaledBy(0.5); - // a vector parallel to the edge - Point2d abuv = buv.Minus(auv).WithMagnitude(0.01); - - *pt = PointAt(muv); - - // If this edge just approximates a curve, then refine our midpoint so - // so that it actually lies on that curve too. Otherwise stuff like - // point-on-face tests will fail, since the point won't actually lie - // on the other face. - hSCurve hc = { auxA }; - SCurve *sc = shell->curve.FindById(hc); - if(sc->isExact && sc->exact.deg != 1) { - double t; - sc->exact.ClosestPointTo(*pt, &t, false); - *pt = sc->exact.PointAt(t); - ClosestPointTo(*pt, &muv); - } else if(!sc->isExact) { - SSurface *trimmedA = sc->GetSurfaceA(sha, shb), - *trimmedB = sc->GetSurfaceB(sha, shb); - *pt = trimmedA->ClosestPointOnThisAndSurface(trimmedB, *pt); - ClosestPointTo(*pt, &muv); - } - - *surfn = NormalAt(muv.x, muv.y); - - // Compute the edge's inner normal in xyz space. - Vector ab = (PointAt(auv)).Minus(PointAt(buv)), - enxyz = (ab.Cross(*surfn)).WithMagnitude(SS.ChordTolMm()); - // And based on that, compute the edge's inner normal in uv space. This - // vector is perpendicular to the edge in xyz, but not necessarily in uv. - Vector tu, tv; - TangentsAt(muv.x, muv.y, &tu, &tv); - Point2d enuv; - enuv.x = enxyz.Dot(tu) / tu.MagSquared(); - enuv.y = enxyz.Dot(tv) / tv.MagSquared(); - - // Compute the inner and outer normals of this edge (within the srf), - // in xyz space. These are not necessarily antiparallel, if the - // surface is curved. - Vector pin = PointAt(muv.Minus(enuv)), - pout = PointAt(muv.Plus(enuv)); - *enin = pin.Minus(*pt), - *enout = pout.Minus(*pt); -} - -//----------------------------------------------------------------------------- -// Trim this surface against the specified shell, in the way that's appropriate -// for the specified Boolean operation type (and which operand we are). We -// also need a pointer to the shell that contains our own surface, since that -// contains our original trim curves. -//----------------------------------------------------------------------------- -SSurface SSurface::MakeCopyTrimAgainst(SShell *parent, - SShell *sha, SShell *shb, - SShell *into, - int type) -{ - bool opA = (parent == sha); - SShell *agnst = opA ? shb : sha; - - SSurface ret; - // The returned surface is identical, just the trim curves change - ret = *this; - ZERO(&(ret.trim)); - - // First, build a list of the existing trim curves; update them to use - // the split curves. - STrimBy *stb; - for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { - STrimBy stn = *stb; - stn.curve = (parent->curve.FindById(stn.curve))->newH; - ret.trim.Add(&stn); - } - - if(type == SShell::AS_DIFFERENCE && !opA) { - // The second operand of a Boolean difference gets turned inside out - ret.Reverse(); - } - - // Build up our original trim polygon; remember the coordinates could - // be changed if we just flipped the surface normal, and we are using - // the split curves (not the original curves). - SEdgeList orig; - ZERO(&orig); - ret.MakeEdgesInto(into, &orig, AS_UV); - ret.trim.Clear(); - // which means that we can't necessarily use the old BSP... - SBspUv *origBsp = SBspUv::From(&orig, &ret); - - // And now intersect the other shell against us - SEdgeList inter; - ZERO(&inter); - - SSurface *ss; - for(ss = agnst->surface.First(); ss; ss = agnst->surface.NextAfter(ss)) { - SCurve *sc; - for(sc = into->curve.First(); sc; sc = into->curve.NextAfter(sc)) { - if(sc->source != SCurve::FROM_INTERSECTION) continue; - if(opA) { - if(sc->surfA.v != h.v || sc->surfB.v != ss->h.v) continue; - } else { - if(sc->surfB.v != h.v || sc->surfA.v != ss->h.v) continue; - } - - int i; - for(i = 1; i < sc->pts.n; i++) { - Vector a = sc->pts.elem[i-1].p, - b = sc->pts.elem[i].p; - - Point2d auv, buv; - ss->ClosestPointTo(a, &(auv.x), &(auv.y)); - ss->ClosestPointTo(b, &(buv.x), &(buv.y)); - - int c = ss->bsp->ClassifyEdge(auv, buv, ss); - if(c != SBspUv::OUTSIDE) { - Vector ta = Vector::From(0, 0, 0); - Vector tb = Vector::From(0, 0, 0); - ret.ClosestPointTo(a, &(ta.x), &(ta.y)); - ret.ClosestPointTo(b, &(tb.x), &(tb.y)); - - Vector tn = ret.NormalAt(ta.x, ta.y); - Vector sn = ss->NormalAt(auv.x, auv.y); - - // We are subtracting the portion of our surface that - // lies in the shell, so the in-plane edge normal should - // point opposite to the surface normal. - bool bkwds = true; - if((tn.Cross(b.Minus(a))).Dot(sn) < 0) bkwds = !bkwds; - if(type == SShell::AS_DIFFERENCE && !opA) bkwds = !bkwds; - if(bkwds) { - inter.AddEdge(tb, ta, sc->h.v, 1); - } else { - inter.AddEdge(ta, tb, sc->h.v, 0); - } - } - } - } - } - - // Record all the points where more than two edges join, which I will call - // the choosing points. If two edges join at a non-choosing point, then - // they must either both be kept or both be discarded (since that would - // otherwise create an open contour). - SPointList choosing; - ZERO(&choosing); - SEdge *se; - for(se = orig.l.First(); se; se = orig.l.NextAfter(se)) { - choosing.IncrementTagFor(se->a); - choosing.IncrementTagFor(se->b); - } - for(se = inter.l.First(); se; se = inter.l.NextAfter(se)) { - choosing.IncrementTagFor(se->a); - choosing.IncrementTagFor(se->b); - } - SPoint *sp; - for(sp = choosing.l.First(); sp; sp = choosing.l.NextAfter(sp)) { - if(sp->tag == 2) { - sp->tag = 1; - } else { - sp->tag = 0; - } - } - choosing.l.RemoveTagged(); - - // The list of edges to trim our new surface, a combination of edges from - // our original and intersecting edge lists. - SEdgeList final; - ZERO(&final); - - while(orig.l.n > 0) { - SEdgeList chain; - ZERO(&chain); - FindChainAvoiding(&orig, &chain, &choosing); - - // Arbitrarily choose an edge within the chain to classify; they - // should all be the same, though. - se = &(chain.l.elem[chain.l.n/2]); - - Point2d auv = (se->a).ProjectXy(), - buv = (se->b).ProjectXy(); - - Vector pt, enin, enout, surfn; - ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn, - se->auxA, into, sha, shb); - - int indir_shell, outdir_shell, indir_orig, outdir_orig; - - indir_orig = SShell::INSIDE; - outdir_orig = SShell::OUTSIDE; - - agnst->ClassifyEdge(&indir_shell, &outdir_shell, - ret.PointAt(auv), ret.PointAt(buv), pt, - enin, enout, surfn); - - if(KeepEdge(type, opA, indir_shell, outdir_shell, - indir_orig, outdir_orig)) - { - for(se = chain.l.First(); se; se = chain.l.NextAfter(se)) { - final.AddEdge(se->a, se->b, se->auxA, se->auxB); - } - } - chain.Clear(); - } - - while(inter.l.n > 0) { - SEdgeList chain; - ZERO(&chain); - FindChainAvoiding(&inter, &chain, &choosing); - - // Any edge in the chain, same as above. - se = &(chain.l.elem[chain.l.n/2]); - - Point2d auv = (se->a).ProjectXy(), - buv = (se->b).ProjectXy(); - - Vector pt, enin, enout, surfn; - ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn, - se->auxA, into, sha, shb); - - int indir_shell, outdir_shell, indir_orig, outdir_orig; - - int c_this = origBsp->ClassifyEdge(auv, buv, &ret); - TagByClassifiedEdge(c_this, &indir_orig, &outdir_orig); - - agnst->ClassifyEdge(&indir_shell, &outdir_shell, - ret.PointAt(auv), ret.PointAt(buv), pt, - enin, enout, surfn); - - if(KeepEdge(type, opA, indir_shell, outdir_shell, - indir_orig, outdir_orig)) - { - for(se = chain.l.First(); se; se = chain.l.NextAfter(se)) { - final.AddEdge(se->a, se->b, se->auxA, se->auxB); - } - } - chain.Clear(); - } - - // Cull extraneous edges; duplicates or anti-parallel pairs. In particular, - // we can get duplicate edges if our surface intersects the other shell - // at an edge, so that both surfaces intersect coincident (and both - // generate an intersection edge). - final.CullExtraneousEdges(); - - // Use our reassembled edges to trim the new surface. - ret.TrimFromEdgeList(&final, true); - - SPolygon poly; - ZERO(&poly); - final.l.ClearTags(); - if(!final.AssemblePolygon(&poly, NULL, true)) { - into->booleanFailed = true; - dbp("failed: I=%d, avoid=%d", I, choosing.l.n); - DEBUGEDGELIST(&final, &ret); - } - poly.Clear(); - - choosing.Clear(); - final.Clear(); - inter.Clear(); - orig.Clear(); - return ret; -} - -void SShell::CopySurfacesTrimAgainst(SShell *sha, SShell *shb, SShell *into, - int type) -{ - SSurface *ss; - for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { - SSurface ssn; - ssn = ss->MakeCopyTrimAgainst(this, sha, shb, into, type); - ss->newH = into->surface.AddAndAssignId(&ssn); - I++; - } -} - -void SShell::MakeIntersectionCurvesAgainst(SShell *agnst, SShell *into) { - SSurface *sa; - for(sa = surface.First(); sa; sa = surface.NextAfter(sa)) { - SSurface *sb; - for(sb = agnst->surface.First(); sb; sb = agnst->surface.NextAfter(sb)){ - // Intersect every surface from our shell against every surface - // from agnst; this will add zero or more curves to the curve - // list for into. - sa->IntersectAgainst(sb, this, agnst, into); - } - } -} - -void SShell::CleanupAfterBoolean(void) { - SSurface *ss; - for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { - ss->edges.Clear(); - } -} - -//----------------------------------------------------------------------------- -// All curves contain handles to the two surfaces that they trim. After a -// Boolean or assembly, we must rewrite those handles to refer to the curves -// by their new IDs. -//----------------------------------------------------------------------------- -void SShell::RewriteSurfaceHandlesForCurves(SShell *a, SShell *b) { - SCurve *sc; - for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { - sc->surfA = sc->GetSurfaceA(a, b)->newH, - sc->surfB = sc->GetSurfaceB(a, b)->newH; - } -} - -//----------------------------------------------------------------------------- -// Copy all the surfaces and curves from two different shells into a single -// shell. The only difficulty is to rewrite all of their handles; we don't -// look for any surface intersections, so if two objects interfere then the -// result is just self-intersecting. This is used for assembly, since it's -// much faster than merging as union. -//----------------------------------------------------------------------------- -void SShell::MakeFromAssemblyOf(SShell *a, SShell *b) { - booleanFailed = false; - - Vector t = Vector::From(0, 0, 0); - Quaternion q = Quaternion::IDENTITY; - int i = 0; - SShell *ab; - - // First, copy over all the curves. Note which shell (a or b) each curve - // came from, but assign it a new ID. - SCurve *c, cn; - for(i = 0; i < 2; i++) { - ab = (i == 0) ? a : b; - for(c = ab->curve.First(); c; c = ab->curve.NextAfter(c)) { - cn = SCurve::FromTransformationOf(c, t, q, 1.0); - cn.source = (i == 0) ? SCurve::FROM_A : SCurve::FROM_B; - // surfA and surfB are wrong now, and we can't fix them until - // we've assigned IDs to the surfaces. So we'll get that later. - c->newH = curve.AddAndAssignId(&cn); - } - } - - // Likewise copy over all the surfaces. - SSurface *s, sn; - for(i = 0; i < 2; i++) { - ab = (i == 0) ? a : b; - for(s = ab->surface.First(); s; s = ab->surface.NextAfter(s)) { - sn = SSurface::FromTransformationOf(s, t, q, 1.0, true); - // All the trim curve IDs get rewritten; we know the new handles - // to the curves since we recorded them in the previous step. - STrimBy *stb; - for(stb = sn.trim.First(); stb; stb = sn.trim.NextAfter(stb)) { - stb->curve = ab->curve.FindById(stb->curve)->newH; - } - s->newH = surface.AddAndAssignId(&sn); - } - } - - // Finally, rewrite the surfaces associated with each curve to use the - // new handles. - RewriteSurfaceHandlesForCurves(a, b); -} - -void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) { - booleanFailed = false; - - a->MakeClassifyingBsps(NULL); - b->MakeClassifyingBsps(NULL); - - // Copy over all the original curves, splitting them so that a - // piecwise linear segment never crosses a surface from the other - // shell. - a->CopyCurvesSplitAgainst(true, b, this); - b->CopyCurvesSplitAgainst(false, a, this); - - // Generate the intersection curves for each surface in A against all - // the surfaces in B (which is all of the intersection curves). - a->MakeIntersectionCurvesAgainst(b, this); - - SCurve *sc; - for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { - SSurface *srfA = sc->GetSurfaceA(a, b), - *srfB = sc->GetSurfaceB(a, b); - - sc->RemoveShortSegments(srfA, srfB); - } - - // And clean up the piecewise linear things we made as a calculation aid - a->CleanupAfterBoolean(); - b->CleanupAfterBoolean(); - // Remake the classifying BSPs with the split (and short-segment-removed) - // curves - a->MakeClassifyingBsps(this); - b->MakeClassifyingBsps(this); - - if(b->surface.n == 0 || a->surface.n == 0) { - I = 1000000; - } else { - I = 0; - } - // Then trim and copy the surfaces - a->CopySurfacesTrimAgainst(a, b, this, type); - b->CopySurfacesTrimAgainst(a, b, this, type); - - // Now that we've copied the surfaces, we know their new hSurfaces, so - // rewrite the curves to refer to the surfaces by their handles in the - // result. - RewriteSurfaceHandlesForCurves(a, b); - - // And clean up the piecewise linear things we made as a calculation aid - a->CleanupAfterBoolean(); - b->CleanupAfterBoolean(); -} - -//----------------------------------------------------------------------------- -// All of the BSP routines that we use to perform and accelerate polygon ops. -//----------------------------------------------------------------------------- -void SShell::MakeClassifyingBsps(SShell *useCurvesFrom) { - SSurface *ss; - for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { - ss->MakeClassifyingBsp(this, useCurvesFrom); - } -} - -void SSurface::MakeClassifyingBsp(SShell *shell, SShell *useCurvesFrom) { - SEdgeList el; - ZERO(&el); - - MakeEdgesInto(shell, &el, AS_UV, useCurvesFrom); - bsp = SBspUv::From(&el, this); - el.Clear(); - - ZERO(&edges); - MakeEdgesInto(shell, &edges, AS_XYZ, useCurvesFrom); -} - -SBspUv *SBspUv::Alloc(void) { - return (SBspUv *)AllocTemporary(sizeof(SBspUv)); -} - -static int ByLength(const void *av, const void *bv) -{ - SEdge *a = (SEdge *)av, - *b = (SEdge *)bv; - - double la = (a->a).Minus(a->b).Magnitude(), - lb = (b->a).Minus(b->b).Magnitude(); - - // Sort in descending order, longest first. This improves numerical - // stability for the normals. - return (la < lb) ? 1 : -1; -} -SBspUv *SBspUv::From(SEdgeList *el, SSurface *srf) { - SEdgeList work; - ZERO(&work); - - SEdge *se; - for(se = el->l.First(); se; se = el->l.NextAfter(se)) { - work.AddEdge(se->a, se->b, se->auxA, se->auxB); - } - qsort(work.l.elem, work.l.n, sizeof(work.l.elem[0]), ByLength); - - SBspUv *bsp = NULL; - for(se = work.l.First(); se; se = work.l.NextAfter(se)) { - bsp = bsp->InsertEdge((se->a).ProjectXy(), (se->b).ProjectXy(), srf); - } - - work.Clear(); - return bsp; -} - -//----------------------------------------------------------------------------- -// The points in this BSP are in uv space, but we want to apply our tolerances -// consistently in xyz (i.e., we want to say a point is on-edge if its xyz -// distance to that edge is less than LENGTH_EPS, irrespective of its distance -// in uv). So we linearize the surface about the point we're considering and -// then do the test. That preserves point-on-line relationships, and the only -// time we care about exact correctness is when we're very close to the line, -// which is when the linearization is accurate. -//----------------------------------------------------------------------------- -void SBspUv::ScalePoints(Point2d *pt, Point2d *a, Point2d *b, SSurface *srf) { - Vector tu, tv; - srf->TangentsAt(pt->x, pt->y, &tu, &tv); - double mu = tu.Magnitude(), mv = tv.Magnitude(); - - pt->x *= mu; pt->y *= mv; - a ->x *= mu; a ->y *= mv; - b ->x *= mu; b ->y *= mv; -} -double SBspUv::ScaledSignedDistanceToLine(Point2d pt, Point2d a, Point2d b, - SSurface *srf) -{ - ScalePoints(&pt, &a, &b, srf); - - Point2d n = ((b.Minus(a)).Normal()).WithMagnitude(1); - double d = a.Dot(n); - - return pt.Dot(n) - d; -} -double SBspUv::ScaledDistanceToLine(Point2d pt, Point2d a, Point2d b, bool seg, - SSurface *srf) -{ - ScalePoints(&pt, &a, &b, srf); - - return pt.DistanceToLine(a, b, seg); -} - -SBspUv *SBspUv::InsertEdge(Point2d ea, Point2d eb, SSurface *srf) { - if(!this) { - SBspUv *ret = Alloc(); - ret->a = ea; - ret->b = eb; - return ret; - } - - double dea = ScaledSignedDistanceToLine(ea, a, b, srf), - deb = ScaledSignedDistanceToLine(eb, a, b, srf); - - if(fabs(dea) < LENGTH_EPS && fabs(deb) < LENGTH_EPS) { - // Line segment is coincident with this one, store in same node - SBspUv *m = Alloc(); - m->a = ea; - m->b = eb; - m->more = more; - more = m; - } else if(fabs(dea) < LENGTH_EPS) { - // Point A lies on this lie, but point B does not - if(deb > 0) { - pos = pos->InsertEdge(ea, eb, srf); - } else { - neg = neg->InsertEdge(ea, eb, srf); - } - } else if(fabs(deb) < LENGTH_EPS) { - // Point B lies on this lie, but point A does not - if(dea > 0) { - pos = pos->InsertEdge(ea, eb, srf); - } else { - neg = neg->InsertEdge(ea, eb, srf); - } - } else if(dea > 0 && deb > 0) { - pos = pos->InsertEdge(ea, eb, srf); - } else if(dea < 0 && deb < 0) { - neg = neg->InsertEdge(ea, eb, srf); - } else { - // New edge crosses this one; we need to split. - Point2d n = ((b.Minus(a)).Normal()).WithMagnitude(1); - double d = a.Dot(n); - double t = (d - n.Dot(ea)) / (n.Dot(eb.Minus(ea))); - Point2d pi = ea.Plus((eb.Minus(ea)).ScaledBy(t)); - if(dea > 0) { - pos = pos->InsertEdge(ea, pi, srf); - neg = neg->InsertEdge(pi, eb, srf); - } else { - neg = neg->InsertEdge(ea, pi, srf); - pos = pos->InsertEdge(pi, eb, srf); - } - } - return this; -} - -int SBspUv::ClassifyPoint(Point2d p, Point2d eb, SSurface *srf) { - if(!this) return OUTSIDE; - - double dp = ScaledSignedDistanceToLine(p, a, b, srf); - - if(fabs(dp) < LENGTH_EPS) { - SBspUv *f = this; - while(f) { - Point2d ba = (f->b).Minus(f->a); - if(ScaledDistanceToLine(p, f->a, ba, true, srf) < LENGTH_EPS) { - if(ScaledDistanceToLine(eb, f->a, ba, false, srf) < LENGTH_EPS){ - if(ba.Dot(eb.Minus(p)) > 0) { - return EDGE_PARALLEL; - } else { - return EDGE_ANTIPARALLEL; - } - } else { - return EDGE_OTHER; - } - } - f = f->more; - } - // Pick arbitrarily which side to send it down, doesn't matter - int c1 = neg ? neg->ClassifyPoint(p, eb, srf) : OUTSIDE; - int c2 = pos ? pos->ClassifyPoint(p, eb, srf) : INSIDE; - if(c1 != c2) { - dbp("MISMATCH: %d %d %08x %08x", c1, c2, neg, pos); - } - return c1; - } else if(dp > 0) { - return pos ? pos->ClassifyPoint(p, eb, srf) : INSIDE; - } else { - return neg ? neg->ClassifyPoint(p, eb, srf) : OUTSIDE; - } -} - -int SBspUv::ClassifyEdge(Point2d ea, Point2d eb, SSurface *srf) { - int ret = ClassifyPoint((ea.Plus(eb)).ScaledBy(0.5), eb, srf); - if(ret == EDGE_OTHER) { - // Perhaps the edge is tangent at its midpoint (and we screwed up - // somewhere earlier and failed to split it); try a different - // point on the edge. - ret = ClassifyPoint(ea.Plus((eb.Minus(ea)).ScaledBy(0.294)), eb, srf); - } - return ret; -} - -double SBspUv::MinimumDistanceToEdge(Point2d p, SSurface *srf) { - if(!this) return VERY_POSITIVE; - - double dn = neg->MinimumDistanceToEdge(p, srf), - dp = pos->MinimumDistanceToEdge(p, srf); - - Point2d as = a, bs = b; - ScalePoints(&p, &as, &bs, srf); - double d = p.DistanceToLine(as, bs.Minus(as), true); - - return min(d, min(dn, dp)); -} - diff --git a/srf/curve.cpp b/srf/curve.cpp deleted file mode 100644 index 551ef96..0000000 --- a/srf/curve.cpp +++ /dev/null @@ -1,860 +0,0 @@ -//----------------------------------------------------------------------------- -// Anything involving curves and sets of curves (except for the real math, -// which is in ratpoly.cpp). -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "../solvespace.h" - -SBezier SBezier::From(Vector4 p0, Vector4 p1) { - SBezier ret; - ZERO(&ret); - ret.deg = 1; - ret.weight[0] = p0.w; - ret.ctrl [0] = p0.PerspectiveProject(); - ret.weight[1] = p1.w; - ret.ctrl [1] = p1.PerspectiveProject(); - return ret; -} - -SBezier SBezier::From(Vector4 p0, Vector4 p1, Vector4 p2) { - SBezier ret; - ZERO(&ret); - ret.deg = 2; - ret.weight[0] = p0.w; - ret.ctrl [0] = p0.PerspectiveProject(); - ret.weight[1] = p1.w; - ret.ctrl [1] = p1.PerspectiveProject(); - ret.weight[2] = p2.w; - ret.ctrl [2] = p2.PerspectiveProject(); - return ret; -} - -SBezier SBezier::From(Vector4 p0, Vector4 p1, Vector4 p2, Vector4 p3) { - SBezier ret; - ZERO(&ret); - ret.deg = 3; - ret.weight[0] = p0.w; - ret.ctrl [0] = p0.PerspectiveProject(); - ret.weight[1] = p1.w; - ret.ctrl [1] = p1.PerspectiveProject(); - ret.weight[2] = p2.w; - ret.ctrl [2] = p2.PerspectiveProject(); - ret.weight[3] = p3.w; - ret.ctrl [3] = p3.PerspectiveProject(); - return ret; -} - -SBezier SBezier::From(Vector p0, Vector p1) { - return SBezier::From(p0.Project4d(), - p1.Project4d()); -} - -SBezier SBezier::From(Vector p0, Vector p1, Vector p2) { - return SBezier::From(p0.Project4d(), - p1.Project4d(), - p2.Project4d()); -} - -SBezier SBezier::From(Vector p0, Vector p1, Vector p2, Vector p3) { - return SBezier::From(p0.Project4d(), - p1.Project4d(), - p2.Project4d(), - p3.Project4d()); -} - -Vector SBezier::Start(void) { - return ctrl[0]; -} - -Vector SBezier::Finish(void) { - return ctrl[deg]; -} - -void SBezier::Reverse(void) { - int i; - for(i = 0; i < (deg+1)/2; i++) { - SWAP(Vector, ctrl[i], ctrl[deg-i]); - SWAP(double, weight[i], weight[deg-i]); - } -} - -void SBezier::ScaleSelfBy(double s) { - int i; - for(i = 0; i <= deg; i++) { - ctrl[i] = ctrl[i].ScaledBy(s); - } -} - -void SBezier::GetBoundingProjd(Vector u, Vector orig, - double *umin, double *umax) -{ - int i; - for(i = 0; i <= deg; i++) { - double ut = ((ctrl[i]).Minus(orig)).Dot(u); - if(ut < *umin) *umin = ut; - if(ut > *umax) *umax = ut; - } -} - -SBezier SBezier::TransformedBy(Vector t, Quaternion q, double scale) { - SBezier ret = *this; - int i; - for(i = 0; i <= deg; i++) { - ret.ctrl[i] = (ret.ctrl[i]).ScaledBy(scale); - ret.ctrl[i] = (q.Rotate(ret.ctrl[i])).Plus(t); - } - return ret; -} - -//----------------------------------------------------------------------------- -// Does this curve lie entirely within the specified plane? It does if all -// the control points lie in that plane. -//----------------------------------------------------------------------------- -bool SBezier::IsInPlane(Vector n, double d) { - int i; - for(i = 0; i <= deg; i++) { - if(fabs((ctrl[i]).Dot(n) - d) > LENGTH_EPS) { - return false; - } - } - return true; -} - -//----------------------------------------------------------------------------- -// Is this Bezier exactly the arc of a circle, projected along the specified -// axis? If yes, return that circle's center and radius. -//----------------------------------------------------------------------------- -bool SBezier::IsCircle(Vector axis, Vector *center, double *r) { - if(deg != 2) return false; - - if(ctrl[1].DistanceToLine(ctrl[0], ctrl[2].Minus(ctrl[0])) < LENGTH_EPS) { - // This is almost a line segment. So it's a circle with very large - // radius, which is likely to make code that tries to handle circles - // blow up. So return false. - return false; - } - - Vector t0 = (ctrl[0]).Minus(ctrl[1]), - t2 = (ctrl[2]).Minus(ctrl[1]), - r0 = axis.Cross(t0), - r2 = axis.Cross(t2); - - *center = Vector::AtIntersectionOfLines(ctrl[0], (ctrl[0]).Plus(r0), - ctrl[2], (ctrl[2]).Plus(r2), - NULL, NULL, NULL); - - double rd0 = center->Minus(ctrl[0]).Magnitude(), - rd2 = center->Minus(ctrl[2]).Magnitude(); - if(fabs(rd0 - rd2) > LENGTH_EPS) { - return false; - } - *r = rd0; - - Vector u = r0.WithMagnitude(1), - v = (axis.Cross(u)).WithMagnitude(1); - Point2d c2 = center->Project2d(u, v), - pa2 = (ctrl[0]).Project2d(u, v).Minus(c2), - pb2 = (ctrl[2]).Project2d(u, v).Minus(c2); - - double thetaa = atan2(pa2.y, pa2.x), // in fact always zero due to csys - thetab = atan2(pb2.y, pb2.x), - dtheta = WRAP_NOT_0(thetab - thetaa, 2*PI); - if(dtheta > PI) { - // Not possible with a second order Bezier arc; so we must have - // the points backwards. - dtheta = 2*PI - dtheta; - } - - if(fabs(weight[1] - cos(dtheta/2)) > LENGTH_EPS) { - return false; - } - - return true; -} - -bool SBezier::IsRational(void) { - int i; - for(i = 0; i <= deg; i++) { - if(fabs(weight[i] - 1) > LENGTH_EPS) return true; - } - return false; -} - -//----------------------------------------------------------------------------- -// Apply a perspective transformation to a rational Bezier curve, calculating -// the new weights as required. -//----------------------------------------------------------------------------- -SBezier SBezier::InPerspective(Vector u, Vector v, Vector n, - Vector origin, double cameraTan) -{ - Quaternion q = Quaternion::From(u, v); - q = q.Inverse(); - // we want Q*(p - o) = Q*p - Q*o - SBezier ret = this->TransformedBy(q.Rotate(origin).ScaledBy(-1), q, 1.0); - int i; - for(i = 0; i <= deg; i++) { - Vector4 ct = Vector4::From(ret.weight[i], ret.ctrl[i]); - // so the desired curve, before perspective, is - // (x/w, y/w, z/w) - // and after perspective is - // ((x/w)/(1 - (z/w)*cameraTan, ... - // = (x/(w - z*cameraTan), ... - // so we want to let w' = w - z*cameraTan - ct.w = ct.w - ct.z*cameraTan; - - ret.ctrl[i] = ct.PerspectiveProject(); - ret.weight[i] = ct.w; - } - return ret; -} - -bool SBezier::Equals(SBezier *b) { - // We just test of identical degree and control points, even though two - // curves could still be coincident (even sharing endpoints). - if(deg != b->deg) return false; - int i; - for(i = 0; i <= deg; i++) { - if(!(ctrl[i]).Equals(b->ctrl[i])) return false; - if(fabs(weight[i] - b->weight[i]) > LENGTH_EPS) return false; - } - return true; -} - -void SBezierList::Clear(void) { - l.Clear(); -} - -void SBezierList::ScaleSelfBy(double s) { - SBezier *sb; - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - sb->ScaleSelfBy(s); - } -} - -//----------------------------------------------------------------------------- -// If our list contains multiple identical Beziers (in either forward or -// reverse order), then cull them. -//----------------------------------------------------------------------------- -void SBezierList::CullIdenticalBeziers(void) { - int i, j; - - l.ClearTags(); - for(i = 0; i < l.n; i++) { - SBezier *bi = &(l.elem[i]), bir; - bir = *bi; - bir.Reverse(); - - for(j = i + 1; j < l.n; j++) { - SBezier *bj = &(l.elem[j]); - if(bj->Equals(bi) || - bj->Equals(&bir)) - { - bi->tag = 1; - bj->tag = 1; - } - } - } - l.RemoveTagged(); -} - -//----------------------------------------------------------------------------- -// Find all the points where a list of Bezier curves intersects another list -// of Bezier curves. We do this by intersecting their piecewise linearizations, -// and then refining any intersections that we find to lie exactly on the -// curves. So this will screw up on tangencies and stuff, but otherwise should -// be fine. -//----------------------------------------------------------------------------- -void SBezierList::AllIntersectionsWith(SBezierList *sblb, SPointList *spl) { - SBezier *sba, *sbb; - for(sba = l.First(); sba; sba = l.NextAfter(sba)) { - for(sbb = sblb->l.First(); sbb; sbb = sblb->l.NextAfter(sbb)) { - sbb->AllIntersectionsWith(sba, spl); - } - } -} -void SBezier::AllIntersectionsWith(SBezier *sbb, SPointList *spl) { - SPointList splRaw; - ZERO(&splRaw); - SEdgeList sea, seb; - ZERO(&sea); - ZERO(&seb); - this->MakePwlInto(&sea); - sbb ->MakePwlInto(&seb); - SEdge *se; - for(se = sea.l.First(); se; se = sea.l.NextAfter(se)) { - // This isn't quite correct, since AnyEdgeCrossings doesn't count - // the case where two pairs of line segments intersect at their - // vertices. So this isn't robust, although that case isn't very - // likely. - seb.AnyEdgeCrossings(se->a, se->b, NULL, &splRaw); - } - SPoint *sp; - for(sp = splRaw.l.First(); sp; sp = splRaw.l.NextAfter(sp)) { - Vector p = sp->p; - if(PointOnThisAndCurve(sbb, &p)) { - if(!spl->ContainsPoint(p)) spl->Add(p); - } - } - sea.Clear(); - seb.Clear(); - splRaw.Clear(); -} - -//----------------------------------------------------------------------------- -// Find a plane that contains all of the curves in this list. If the curves -// are all colinear (or coincident, or empty), then that plane is not exactly -// determined but we choose the additional degree(s) of freedom arbitrarily. -// Returns true if all the curves are coplanar, otherwise false. -//----------------------------------------------------------------------------- -bool SBezierList::GetPlaneContainingBeziers(Vector *p, Vector *u, Vector *v, - Vector *notCoplanarAt) -{ - Vector pt, ptFar, ptOffLine, dp, n; - double farMax, offLineMax; - int i; - SBezier *sb; - - // Get any point on any Bezier; or an arbitrary point if list is empty. - if(l.n > 0) { - pt = l.elem[0].Start(); - } else { - pt = Vector::From(0, 0, 0); - } - ptFar = ptOffLine = pt; - - // Get the point farthest from our arbitrary point. - farMax = VERY_NEGATIVE; - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - for(i = 0; i <= sb->deg; i++) { - double m = (pt.Minus(sb->ctrl[i])).Magnitude(); - if(m > farMax) { - ptFar = sb->ctrl[i]; - farMax = m; - } - } - } - if(ptFar.Equals(pt)) { - // The points are all coincident. So neither basis vector matters. - *p = pt; - *u = Vector::From(1, 0, 0); - *v = Vector::From(0, 1, 0); - return true; - } - - // Get the point farthest from the line between pt and ptFar - dp = ptFar.Minus(pt); - offLineMax = VERY_NEGATIVE; - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - for(i = 0; i <= sb->deg; i++) { - double m = (sb->ctrl[i]).DistanceToLine(pt, dp); - if(m > offLineMax) { - ptOffLine = sb->ctrl[i]; - offLineMax = m; - } - } - } - - *p = pt; - if(offLineMax < LENGTH_EPS) { - // The points are all colinear; so choose the second basis vector - // arbitrarily. - *u = (ptFar.Minus(pt)).WithMagnitude(1); - *v = (u->Normal(0)).WithMagnitude(1); - } else { - // The points actually define a plane. - n = (ptFar.Minus(pt)).Cross(ptOffLine.Minus(pt)); - *u = (n.Normal(0)).WithMagnitude(1); - *v = (n.Normal(1)).WithMagnitude(1); - } - - // So we have a plane; but check whether all of the points lie in that - // plane. - n = u->Cross(*v); - n = n.WithMagnitude(1); - double d = p->Dot(n); - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - for(i = 0; i <= sb->deg; i++) { - if(fabs(n.Dot(sb->ctrl[i]) - d) > LENGTH_EPS) { - if(notCoplanarAt) *notCoplanarAt = sb->ctrl[i]; - return false; - } - } - } - return true; -} - -//----------------------------------------------------------------------------- -// Assemble curves in sbl into a single loop. The curves may appear in any -// direction (start to finish, or finish to start), and will be reversed if -// necessary. The curves in the returned loop are removed from sbl, even if -// the loop cannot be closed. -//----------------------------------------------------------------------------- -SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl, - bool *allClosed, SEdge *errorAt) -{ - SBezierLoop loop; - ZERO(&loop); - - if(sbl->l.n < 1) return loop; - sbl->l.ClearTags(); - - SBezier *first = &(sbl->l.elem[0]); - first->tag = 1; - loop.l.Add(first); - Vector start = first->Start(); - Vector hanging = first->Finish(); - int auxA = first->auxA; - - sbl->l.RemoveTagged(); - - while(sbl->l.n > 0 && !hanging.Equals(start)) { - int i; - bool foundNext = false; - for(i = 0; i < sbl->l.n; i++) { - SBezier *test = &(sbl->l.elem[i]); - - if((test->Finish()).Equals(hanging) && test->auxA == auxA) { - test->Reverse(); - // and let the next test catch it - } - if((test->Start()).Equals(hanging) && test->auxA == auxA) { - test->tag = 1; - loop.l.Add(test); - hanging = test->Finish(); - sbl->l.RemoveTagged(); - foundNext = true; - break; - } - } - if(!foundNext) { - // The loop completed without finding the hanging edge, so - // it's an open loop - errorAt->a = hanging; - errorAt->b = start; - *allClosed = false; - return loop; - } - } - if(hanging.Equals(start)) { - *allClosed = true; - } else { - // We ran out of edges without forming a closed loop. - errorAt->a = hanging; - errorAt->b = start; - *allClosed = false; - } - - return loop; -} - -void SBezierLoop::Reverse(void) { - l.Reverse(); - SBezier *sb; - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - // If we didn't reverse each curve, then the next curve in list would - // share your start, not your finish. - sb->Reverse(); - } -} - -void SBezierLoop::GetBoundingProjd(Vector u, Vector orig, - double *umin, double *umax) -{ - SBezier *sb; - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - sb->GetBoundingProjd(u, orig, umin, umax); - } -} - -void SBezierLoop::MakePwlInto(SContour *sc, double chordTol) { - SBezier *sb; - for(sb = l.First(); sb; sb = l.NextAfter(sb)) { - sb->MakePwlInto(sc, chordTol); - // Avoid double points at join between Beziers; except that - // first and last points should be identical. - if(l.NextAfter(sb) != NULL) { - sc->l.RemoveLast(1); - } - } - // Ensure that it's exactly closed, not just within a numerical tolerance. - if((sc->l.elem[sc->l.n - 1].p).Equals(sc->l.elem[0].p)) { - sc->l.elem[sc->l.n - 1] = sc->l.elem[0]; - } -} - -bool SBezierLoop::IsClosed(void) { - if(l.n < 1) return false; - Vector s = l.elem[0].Start(), - f = l.elem[l.n-1].Finish(); - return s.Equals(f); -} - - -//----------------------------------------------------------------------------- -// Assemble the curves in sbl into multiple loops, and piecewise linearize the -// curves into poly. If we can't close a contour, then we add it to -// openContours (if that isn't NULL) and keep going; so this works even if the -// input contains a mix of open and closed curves. -//----------------------------------------------------------------------------- -SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly, - double chordTol, - bool *allClosed, SEdge *errorAt, - SBezierList *openContours) -{ - SBezierLoopSet ret; - ZERO(&ret); - - *allClosed = true; - while(sbl->l.n > 0) { - bool thisClosed; - SBezierLoop loop; - loop = SBezierLoop::FromCurves(sbl, &thisClosed, errorAt); - if(!thisClosed) { - // Record open loops in a separate list, if requested. - *allClosed = false; - if(openContours) { - SBezier *sb; - for(sb = loop.l.First(); sb; sb = loop.l.NextAfter(sb)) { - openContours->l.Add(sb); - } - } - loop.Clear(); - } else { - ret.l.Add(&loop); - poly->AddEmptyContour(); - loop.MakePwlInto(&(poly->l.elem[poly->l.n-1]), chordTol); - } - } - - poly->normal = poly->ComputeNormal(); - ret.normal = poly->normal; - if(poly->l.n > 0) { - ret.point = poly->AnyPoint(); - } else { - ret.point = Vector::From(0, 0, 0); - } - - return ret; -} - -void SBezierLoopSet::GetBoundingProjd(Vector u, Vector orig, - double *umin, double *umax) -{ - SBezierLoop *sbl; - for(sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) { - sbl->GetBoundingProjd(u, orig, umin, umax); - } -} - -//----------------------------------------------------------------------------- -// Convert all the Beziers into piecewise linear form, and assemble that into -// a polygon, one contour per loop. -//----------------------------------------------------------------------------- -void SBezierLoopSet::MakePwlInto(SPolygon *sp) { - SBezierLoop *sbl; - for(sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) { - sp->AddEmptyContour(); - sbl->MakePwlInto(&(sp->l.elem[sp->l.n - 1])); - } -} - -void SBezierLoopSet::Clear(void) { - int i; - for(i = 0; i < l.n; i++) { - (l.elem[i]).Clear(); - } - l.Clear(); -} - -//----------------------------------------------------------------------------- -// An export helper function. We start with a list of Bezier curves, and -// assemble them into loops. We find the outer loops, and find the outer loops' -// inner loops, and group them accordingly. -//----------------------------------------------------------------------------- -void SBezierLoopSetSet::FindOuterFacesFrom(SBezierList *sbl, SPolygon *spxyz, - SSurface *srfuv, - double chordTol, - bool *allClosed, SEdge *notClosedAt, - bool *allCoplanar, Vector *notCoplanarAt, - SBezierList *openContours) -{ - SSurface srfPlane; - if(!srfuv) { - Vector p, u, v; - *allCoplanar = - sbl->GetPlaneContainingBeziers(&p, &u, &v, notCoplanarAt); - if(!*allCoplanar) { - // Don't even try to assemble them into loops if they're not - // all coplanar. - if(openContours) { - SBezier *sb; - for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { - openContours->l.Add(sb); - } - } - return; - } - // All the curves lie in a plane through p with basis vectors u and v. - srfPlane = SSurface::FromPlane(p, u, v); - srfuv = &srfPlane; - } - - int i, j; - // Assemble the Bezier trim curves into closed loops; we also get the - // piecewise linearization of the curves (in the SPolygon spxyz), as a - // calculation aid for the loop direction. - SBezierLoopSet sbls = SBezierLoopSet::From(sbl, spxyz, chordTol, - allClosed, notClosedAt, - openContours); - if(sbls.l.n != spxyz->l.n) return; - - // Convert the xyz piecewise linear to uv piecewise linear. - SPolygon spuv; - ZERO(&spuv); - SContour *sc; - for(sc = spxyz->l.First(); sc; sc = spxyz->l.NextAfter(sc)) { - spuv.AddEmptyContour(); - SPoint *pt; - for(pt = sc->l.First(); pt; pt = sc->l.NextAfter(pt)) { - double u, v; - srfuv->ClosestPointTo(pt->p, &u, &v); - spuv.l.elem[spuv.l.n - 1].AddPoint(Vector::From(u, v, 0)); - } - } - spuv.normal = Vector::From(0, 0, 1); // must be, since it's in xy plane now - - static const int OUTER_LOOP = 10; - static const int INNER_LOOP = 20; - static const int USED_LOOP = 30; - // Fix the contour directions; we do this properly, in uv space, so it - // works for curved surfaces too (important for STEP export). - spuv.FixContourDirections(); - for(i = 0; i < spuv.l.n; i++) { - SContour *contour = &(spuv.l.elem[i]); - SBezierLoop *bl = &(sbls.l.elem[i]); - if(contour->tag) { - // This contour got reversed in the polygon to make the directions - // consistent, so the same must be necessary for the Bezier loop. - bl->Reverse(); - } - if(contour->IsClockwiseProjdToNormal(spuv.normal)) { - bl->tag = INNER_LOOP; - } else { - bl->tag = OUTER_LOOP; - } - } - - bool loopsRemaining = true; - while(loopsRemaining) { - loopsRemaining = false; - for(i = 0; i < sbls.l.n; i++) { - SBezierLoop *loop = &(sbls.l.elem[i]); - if(loop->tag != OUTER_LOOP) continue; - - // Check if this contour contains any outer loops; if it does, then - // we should do those "inner outer loops" first; otherwise we - // will steal their holes, since their holes also lie inside this - // contour. - for(j = 0; j < sbls.l.n; j++) { - SBezierLoop *outer = &(sbls.l.elem[j]); - if(i == j) continue; - if(outer->tag != OUTER_LOOP) continue; - - Vector p = spuv.l.elem[j].AnyEdgeMidpoint(); - if(spuv.l.elem[i].ContainsPointProjdToNormal(spuv.normal, p)) { - break; - } - } - if(j < sbls.l.n) { - // It does, can't do this one yet. - continue; - } - - SBezierLoopSet outerAndInners; - ZERO(&outerAndInners); - loopsRemaining = true; - loop->tag = USED_LOOP; - outerAndInners.l.Add(loop); - int auxA = 0; - if(loop->l.n > 0) auxA = loop->l.elem[0].auxA; - - for(j = 0; j < sbls.l.n; j++) { - SBezierLoop *inner = &(sbls.l.elem[j]); - if(inner->tag != INNER_LOOP) continue; - if(inner->l.n < 1) continue; - if(inner->l.elem[0].auxA != auxA) continue; - - Vector p = spuv.l.elem[j].AnyEdgeMidpoint(); - if(spuv.l.elem[i].ContainsPointProjdToNormal(spuv.normal, p)) { - outerAndInners.l.Add(inner); - inner->tag = USED_LOOP; - } - } - - outerAndInners.point = srfuv->PointAt(0, 0); - outerAndInners.normal = srfuv->NormalAt(0, 0); - l.Add(&outerAndInners); - } - } - - // If we have poorly-formed loops--for example, overlapping zero-area - // stuff--then we can end up with leftovers. We use this function to - // group stuff into closed paths for export when possible, so it's bad - // to screw up on that stuff. So just add them onto the open curve list. - // Very ugly, but better than losing curves. - for(i = 0; i < sbls.l.n; i++) { - SBezierLoop *loop = &(sbls.l.elem[i]); - if(loop->tag == USED_LOOP) continue; - - if(openContours) { - SBezier *sb; - for(sb = loop->l.First(); sb; sb = loop->l.NextAfter(sb)) { - openContours->l.Add(sb); - } - } - loop->Clear(); - // but don't free the used loops, since we shallow-copied them to - // ourself - } - - sbls.l.Clear(); // not sbls.Clear(), since that would deep-clear - spuv.Clear(); -} - -void SBezierLoopSetSet::AddOpenPath(SBezier *sb) { - SBezierLoop sbl; - ZERO(&sbl); - sbl.l.Add(sb); - - SBezierLoopSet sbls; - ZERO(&sbls); - sbls.l.Add(&sbl); - - l.Add(&sbls); -} - -void SBezierLoopSetSet::Clear(void) { - SBezierLoopSet *sbls; - for(sbls = l.First(); sbls; sbls = l.NextAfter(sbls)) { - sbls->Clear(); - } - l.Clear(); -} - -SCurve SCurve::FromTransformationOf(SCurve *a, - Vector t, Quaternion q, double scale) -{ - SCurve ret; - ZERO(&ret); - - ret.h = a->h; - ret.isExact = a->isExact; - ret.exact = (a->exact).TransformedBy(t, q, scale); - ret.surfA = a->surfA; - ret.surfB = a->surfB; - - SCurvePt *p; - for(p = a->pts.First(); p; p = a->pts.NextAfter(p)) { - SCurvePt pp = *p; - pp.p = (pp.p).ScaledBy(scale); - pp.p = (q.Rotate(pp.p)).Plus(t); - ret.pts.Add(&pp); - } - return ret; -} - -void SCurve::Clear(void) { - pts.Clear(); -} - -SSurface *SCurve::GetSurfaceA(SShell *a, SShell *b) { - if(source == FROM_A) { - return a->surface.FindById(surfA); - } else if(source == FROM_B) { - return b->surface.FindById(surfA); - } else if(source == FROM_INTERSECTION) { - return a->surface.FindById(surfA); - } else oops(); -} - -SSurface *SCurve::GetSurfaceB(SShell *a, SShell *b) { - if(source == FROM_A) { - return a->surface.FindById(surfB); - } else if(source == FROM_B) { - return b->surface.FindById(surfB); - } else if(source == FROM_INTERSECTION) { - return b->surface.FindById(surfB); - } else oops(); -} - -//----------------------------------------------------------------------------- -// When we split line segments wherever they intersect a surface, we introduce -// extra pwl points. This may create very short edges that could be removed -// without violating the chord tolerance. Those are ugly, and also break -// stuff in the Booleans. So remove them. -//----------------------------------------------------------------------------- -void SCurve::RemoveShortSegments(SSurface *srfA, SSurface *srfB) { - // Three, not two; curves are pwl'd to at least two edges (three points) - // even if not necessary, to avoid square holes. - if(pts.n <= 3) return; - pts.ClearTags(); - - Vector prev = pts.elem[0].p; - int i, a; - for(i = 1; i < pts.n - 1; i++) { - SCurvePt *sct = &(pts.elem[i]), - *scn = &(pts.elem[i+1]); - if(sct->vertex) { - prev = sct->p; - continue; - } - bool mustKeep = false; - - // We must check against both surfaces; the piecewise linear edge - // may have a different chord tolerance in the two surfaces. (For - // example, a circle in the surface of a cylinder is just a straight - // line, so it always has perfect chord tol, but that circle in - // a plane is a circle so it doesn't). - for(a = 0; a < 2; a++) { - SSurface *srf = (a == 0) ? srfA : srfB; - Vector puv, nuv; - srf->ClosestPointTo(prev, &(puv.x), &(puv.y)); - srf->ClosestPointTo(scn->p, &(nuv.x), &(nuv.y)); - - if(srf->ChordToleranceForEdge(nuv, puv) > SS.ChordTolMm()) { - mustKeep = true; - } - } - - if(mustKeep) { - prev = sct->p; - } else { - sct->tag = 1; - // and prev is unchanged, since there's no longer any point - // in between - } - } - - pts.RemoveTagged(); -} - -STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) { - STrimBy stb; - ZERO(&stb); - stb.curve = hsc; - SCurve *sc = shell->curve.FindById(hsc); - - if(backwards) { - stb.finish = sc->pts.elem[0].p; - stb.start = sc->pts.elem[sc->pts.n - 1].p; - stb.backwards = true; - } else { - stb.start = sc->pts.elem[0].p; - stb.finish = sc->pts.elem[sc->pts.n - 1].p; - stb.backwards = false; - } - - return stb; -} - diff --git a/srf/merge.cpp b/srf/merge.cpp deleted file mode 100644 index eacd6d6..0000000 --- a/srf/merge.cpp +++ /dev/null @@ -1,129 +0,0 @@ -//----------------------------------------------------------------------------- -// Routines to merge multiple coincident surfaces (each with their own trim -// curves) into a single surface, with all of the trim curves. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "../solvespace.h" - -void SShell::MergeCoincidentSurfaces(void) { - surface.ClearTags(); - - int i, j; - SSurface *si, *sj; - - for(i = 0; i < surface.n; i++) { - si = &(surface.elem[i]); - if(si->tag) continue; - // Let someone else clean up the empty surfaces; we can certainly merge - // them, but we don't know how to calculate a reasonable bounding box. - if(si->trim.n == 0) continue; - // And for now we handle only coincident planes, so no sense wasting - // time on other surfaces. - if(si->degm != 1 || si->degn != 1) continue; - - SEdgeList sel; - ZERO(&sel); - si->MakeEdgesInto(this, &sel, SSurface::AS_XYZ); - - bool mergedThisTime, merged = false; - do { - mergedThisTime = false; - - for(j = i + 1; j < surface.n; j++) { - sj = &(surface.elem[j]); - if(sj->tag) continue; - if(!sj->CoincidentWith(si, true)) continue; - if(sj->color != si->color) continue; - // But we do merge surfaces with different face entities, since - // otherwise we'd hardly ever merge anything. - - // This surface is coincident. But let's not merge coincident - // surfaces if they contain disjoint contours; that just makes - // the bounding box tests less effective, and possibly things - // less robust. - SEdgeList tel; - ZERO(&tel); - sj->MakeEdgesInto(this, &tel, SSurface::AS_XYZ); - if(!sel.ContainsEdgeFrom(&tel)) { - tel.Clear(); - continue; - } - tel.Clear(); - - sj->tag = 1; - merged = true; - mergedThisTime = true; - sj->MakeEdgesInto(this, &sel, SSurface::AS_XYZ); - sj->trim.Clear(); - - // All the references to this surface get replaced with the - // new srf - SCurve *sc; - for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { - if(sc->surfA.v == sj->h.v) sc->surfA = si->h; - if(sc->surfB.v == sj->h.v) sc->surfB = si->h; - } - } - - // If this iteration merged a contour onto ours, then we have to - // go through the surfaces again; that might have made a new - // surface touch us. - } while(mergedThisTime); - - if(merged) { - sel.CullExtraneousEdges(); - si->trim.Clear(); - si->TrimFromEdgeList(&sel, false); - - // And we must choose control points such that all the trims lie - // with u and v in [0, 1], so that the bbox tests work. - Vector u, v, n; - si->TangentsAt(0.5, 0.5, &u, &v); - u = u.WithMagnitude(1); - v = v.WithMagnitude(1); - n = si->NormalAt(0.5, 0.5).WithMagnitude(1); - v = (n.Cross(u)).WithMagnitude(1); - - double umax = VERY_NEGATIVE, umin = VERY_POSITIVE, - vmax = VERY_NEGATIVE, vmin = VERY_POSITIVE; - SEdge *se; - for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) { - double ut = (se->a).Dot(u), vt = (se->a).Dot(v); - umax = max(umax, ut); - vmax = max(vmax, vt); - umin = min(umin, ut); - vmin = min(vmin, vt); - } - - // An interesting problem here; the real curve could extend - // slightly beyond the bounding box of the piecewise linear - // bits. Not a problem for us, but some apps won't import STEP - // in that case. So give a bit of extra room; in theory just - // a chord tolerance, but more can't hurt. - double muv = max((umax - umin), (vmax - vmin)); - double tol = muv/50 + 3*SS.ChordTolMm(); - umax += tol; - vmax += tol; - umin -= tol; - vmin -= tol; - - // We move in the +v direction as v goes from 0 to 1, and in the - // +u direction as u goes from 0 to 1. So our normal ends up - // pointed the same direction. - double nt = (si->ctrl[0][0]).Dot(n); - si->ctrl[0][0] = - Vector::From(umin, vmin, nt).ScaleOutOfCsys(u, v, n); - si->ctrl[0][1] = - Vector::From(umin, vmax, nt).ScaleOutOfCsys(u, v, n); - si->ctrl[1][1] = - Vector::From(umax, vmax, nt).ScaleOutOfCsys(u, v, n); - si->ctrl[1][0] = - Vector::From(umax, vmin, nt).ScaleOutOfCsys(u, v, n); - } - sel.Clear(); - } - - surface.RemoveTagged(); -} - diff --git a/srf/ratpoly.cpp b/srf/ratpoly.cpp deleted file mode 100644 index 578b205..0000000 --- a/srf/ratpoly.cpp +++ /dev/null @@ -1,598 +0,0 @@ -//----------------------------------------------------------------------------- -// Math on rational polynomial surfaces and curves, typically in Bezier -// form. Evaluate, root-find (by Newton's methods), evaluate derivatives, -// and so on. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "../solvespace.h" - -// Converge it to better than LENGTH_EPS; we want two points, each -// independently projected into uv and back, to end up equal with the -// LENGTH_EPS. Best case that requires LENGTH_EPS/2, but more is better -// and convergence should be fast by now. -#define RATPOLY_EPS (LENGTH_EPS/(1e2)) - -static double Bernstein(int k, int deg, double t) -{ - if(k > deg || k < 0) return 0; - - switch(deg) { - case 0: - return 1; - break; - - case 1: - if(k == 0) { - return (1 - t); - } else if(k = 1) { - return t; - } - break; - - case 2: - if(k == 0) { - return (1 - t)*(1 - t); - } else if(k == 1) { - return 2*(1 - t)*t; - } else if(k == 2) { - return t*t; - } - break; - - case 3: - if(k == 0) { - return (1 - t)*(1 - t)*(1 - t); - } else if(k == 1) { - return 3*(1 - t)*(1 - t)*t; - } else if(k == 2) { - return 3*(1 - t)*t*t; - } else if(k == 3) { - return t*t*t; - } - break; - } - oops(); -} - -double BernsteinDerivative(int k, int deg, double t) -{ - switch(deg) { - case 0: - return 0; - break; - - case 1: - if(k == 0) { - return -1; - } else if(k = 1) { - return 1; - } - break; - - case 2: - if(k == 0) { - return -2 + 2*t; - } else if(k == 1) { - return 2 - 4*t; - } else if(k == 2) { - return 2*t; - } - break; - - case 3: - if(k == 0) { - return -3 + 6*t - 3*t*t; - } else if(k == 1) { - return 3 - 12*t + 9*t*t; - } else if(k == 2) { - return 6*t - 9*t*t; - } else if(k == 3) { - return 3*t*t; - } - break; - } - oops(); -} - -Vector SBezier::PointAt(double t) { - Vector pt = Vector::From(0, 0, 0); - double d = 0; - - int i; - for(i = 0; i <= deg; i++) { - double B = Bernstein(i, deg, t); - pt = pt.Plus(ctrl[i].ScaledBy(B*weight[i])); - d += weight[i]*B; - } - pt = pt.ScaledBy(1.0/d); - return pt; -} - -Vector SBezier::TangentAt(double t) { - Vector pt = Vector::From(0, 0, 0), pt_p = Vector::From(0, 0, 0); - double d = 0, d_p = 0; - - int i; - for(i = 0; i <= deg; i++) { - double B = Bernstein(i, deg, t), - Bp = BernsteinDerivative(i, deg, t); - - pt = pt.Plus(ctrl[i].ScaledBy(B*weight[i])); - d += weight[i]*B; - - pt_p = pt_p.Plus(ctrl[i].ScaledBy(Bp*weight[i])); - d_p += weight[i]*Bp; - } - - // quotient rule; f(t) = n(t)/d(t), so f' = (n'*d - n*d')/(d^2) - Vector ret; - ret = (pt_p.ScaledBy(d)).Minus(pt.ScaledBy(d_p)); - ret = ret.ScaledBy(1.0/(d*d)); - return ret; -} - -void SBezier::ClosestPointTo(Vector p, double *t, bool converge) { - int i; - double minDist = VERY_POSITIVE; - *t = 0; - double res = (deg <= 2) ? 7.0 : 20.0; - for(i = 0; i < (int)res; i++) { - double tryt = (i/res); - - Vector tryp = PointAt(tryt); - double d = (tryp.Minus(p)).Magnitude(); - if(d < minDist) { - *t = tryt; - minDist = d; - } - } - - Vector p0; - for(i = 0; i < (converge ? 15 : 5); i++) { - p0 = PointAt(*t); - if(p0.Equals(p, RATPOLY_EPS)) { - return; - } - - Vector dp = TangentAt(*t); - Vector pc = p.ClosestPointOnLine(p0, dp); - *t += (pc.Minus(p0)).DivPivoting(dp); - } - if(converge) { - dbp("didn't converge (closest point on bezier curve)"); - } -} - -bool SBezier::PointOnThisAndCurve(SBezier *sbb, Vector *p) { - double ta, tb; - this->ClosestPointTo(*p, &ta, false); - sbb ->ClosestPointTo(*p, &tb, false); - - int i; - for(i = 0; i < 20; i++) { - Vector pa = this->PointAt(ta), - pb = sbb ->PointAt(tb), - da = this->TangentAt(ta), - db = sbb ->TangentAt(tb); - - if(pa.Equals(pb, RATPOLY_EPS)) { - *p = pa; - return true; - } - - double tta, ttb; - Vector::ClosestPointBetweenLines(pa, da, pb, db, &tta, &ttb); - ta += tta; - tb += ttb; - } - return false; -} - -void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) { - Vector4 ct[4]; - int i; - for(i = 0; i <= deg; i++) { - ct[i] = Vector4::From(weight[i], ctrl[i]); - } - - switch(deg) { - case 1: { - Vector4 cts = Vector4::Blend(ct[0], ct[1], t); - *bef = SBezier::From(ct[0], cts); - *aft = SBezier::From(cts, ct[1]); - break; - } - case 2: { - Vector4 ct01 = Vector4::Blend(ct[0], ct[1], t), - ct12 = Vector4::Blend(ct[1], ct[2], t), - cts = Vector4::Blend(ct01, ct12, t); - - *bef = SBezier::From(ct[0], ct01, cts); - *aft = SBezier::From(cts, ct12, ct[2]); - break; - } - case 3: { - Vector4 ct01 = Vector4::Blend(ct[0], ct[1], t), - ct12 = Vector4::Blend(ct[1], ct[2], t), - ct23 = Vector4::Blend(ct[2], ct[3], t), - ct01_12 = Vector4::Blend(ct01, ct12, t), - ct12_23 = Vector4::Blend(ct12, ct23, t), - cts = Vector4::Blend(ct01_12, ct12_23, t); - - *bef = SBezier::From(ct[0], ct01, ct01_12, cts); - *aft = SBezier::From(cts, ct12_23, ct23, ct[3]); - break; - } - default: oops(); - } -} - -void SBezier::MakePwlInto(SEdgeList *sel, double chordTol) { - List lv; - ZERO(&lv); - MakePwlInto(&lv, chordTol); - int i; - for(i = 1; i < lv.n; i++) { - sel->AddEdge(lv.elem[i-1], lv.elem[i]); - } - lv.Clear(); -} -void SBezier::MakePwlInto(List *l, double chordTol) { - List lv; - ZERO(&lv); - MakePwlInto(&lv, chordTol); - int i; - for(i = 0; i < lv.n; i++) { - SCurvePt scpt; - scpt.tag = 0; - scpt.p = lv.elem[i]; - scpt.vertex = (i == 0) || (i == (lv.n - 1)); - l->Add(&scpt); - } - lv.Clear(); -} -void SBezier::MakePwlInto(SContour *sc, double chordTol) { - List lv; - ZERO(&lv); - MakePwlInto(&lv, chordTol); - int i; - for(i = 0; i < lv.n; i++) { - sc->AddPoint(lv.elem[i]); - } - lv.Clear(); -} -void SBezier::MakePwlInto(List *l, double chordTol) { - if(chordTol == 0) { - // Use the default chord tolerance. - chordTol = SS.ChordTolMm(); - } - l->Add(&(ctrl[0])); - if(deg == 1) { - l->Add(&(ctrl[1])); - } else { - // Never do fewer than one intermediate point; people seem to get - // unhappy when their circles turn into squares, but maybe less - // unhappy with octagons. - MakePwlWorker(l, 0.0, 0.5, chordTol); - MakePwlWorker(l, 0.5, 1.0, chordTol); - } -} -void SBezier::MakePwlWorker(List *l, double ta, double tb, - double chordTol) -{ - Vector pa = PointAt(ta); - Vector pb = PointAt(tb); - - // Can't test in the middle, or certain cubics would break. - double tm1 = (2*ta + tb) / 3; - double tm2 = (ta + 2*tb) / 3; - - Vector pm1 = PointAt(tm1); - Vector pm2 = PointAt(tm2); - - double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)), - pm2.DistanceToLine(pa, pb.Minus(pa))); - - double step = 1.0/SS.maxSegments; - if((tb - ta) < step || d < chordTol) { - // A previous call has already added the beginning of our interval. - l->Add(&pb); - } else { - double tm = (ta + tb) / 2; - MakePwlWorker(l, ta, tm, chordTol); - MakePwlWorker(l, tm, tb, chordTol); - } -} - -Vector SSurface::PointAt(Point2d puv) { - return PointAt(puv.x, puv.y); -} -Vector SSurface::PointAt(double u, double v) { - Vector num = Vector::From(0, 0, 0); - double den = 0; - - int i, j; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - double Bi = Bernstein(i, degm, u), - Bj = Bernstein(j, degn, v); - - num = num.Plus(ctrl[i][j].ScaledBy(Bi*Bj*weight[i][j])); - den += weight[i][j]*Bi*Bj; - } - } - num = num.ScaledBy(1.0/den); - return num; -} - -void SSurface::TangentsAt(double u, double v, Vector *tu, Vector *tv) { - Vector num = Vector::From(0, 0, 0), - num_u = Vector::From(0, 0, 0), - num_v = Vector::From(0, 0, 0); - double den = 0, - den_u = 0, - den_v = 0; - - int i, j; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - double Bi = Bernstein(i, degm, u), - Bj = Bernstein(j, degn, v), - Bip = BernsteinDerivative(i, degm, u), - Bjp = BernsteinDerivative(j, degn, v); - - num = num.Plus(ctrl[i][j].ScaledBy(Bi*Bj*weight[i][j])); - den += weight[i][j]*Bi*Bj; - - num_u = num_u.Plus(ctrl[i][j].ScaledBy(Bip*Bj*weight[i][j])); - den_u += weight[i][j]*Bip*Bj; - - num_v = num_v.Plus(ctrl[i][j].ScaledBy(Bi*Bjp*weight[i][j])); - den_v += weight[i][j]*Bi*Bjp; - } - } - // quotient rule; f(t) = n(t)/d(t), so f' = (n'*d - n*d')/(d^2) - *tu = ((num_u.ScaledBy(den)).Minus(num.ScaledBy(den_u))); - *tu = tu->ScaledBy(1.0/(den*den)); - - *tv = ((num_v.ScaledBy(den)).Minus(num.ScaledBy(den_v))); - *tv = tv->ScaledBy(1.0/(den*den)); -} - -Vector SSurface::NormalAt(Point2d puv) { - return NormalAt(puv.x, puv.y); -} -Vector SSurface::NormalAt(double u, double v) { - Vector tu, tv; - TangentsAt(u, v, &tu, &tv); - return tu.Cross(tv); -} - -void SSurface::ClosestPointTo(Vector p, Point2d *puv, bool converge) { - ClosestPointTo(p, &(puv->x), &(puv->y), converge); -} -void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool converge) { - // A few special cases first; when control points are coincident the - // derivative goes to zero at the conrol points, and would result in - // nonconvergence. We avoid that here, and also guarantee a consistent - // (u, v) (of the infinitely many possible in one parameter). - if(p.Equals(ctrl[0] [0] )) { *u = 0; *v = 0; return; } - if(p.Equals(ctrl[degm][0] )) { *u = 1; *v = 0; return; } - if(p.Equals(ctrl[degm][degn])) { *u = 1; *v = 1; return; } - if(p.Equals(ctrl[0] [degn])) { *u = 0; *v = 1; return; } - - // And planes are trivial, so don't waste time iterating over those. - if(degm == 1 && degn == 1) { - Vector orig = ctrl[0][0], - bu = (ctrl[1][0]).Minus(orig), - bv = (ctrl[0][1]).Minus(orig); - if((ctrl[1][1]).Equals(orig.Plus(bu).Plus(bv))) { - Vector dp = p.Minus(orig); - *u = dp.Dot(bu) / bu.MagSquared(); - *v = dp.Dot(bv) / bv.MagSquared(); - return; - } - } - - // Try whatever the previous guess was. This is likely to do something - // good if we're working our way along a curve or something else where - // we project successive points that are close to each other; something - // like a 20% speedup empirically. - if(converge) { - double ut = cached.x, vt = cached.y; - if(ClosestPointNewton(p, &ut, &vt, converge)) { - cached.x = *u = ut; - cached.y = *v = vt; - return; - } - } - - // Search for a reasonable initial guess - int i, j; - double minDist = VERY_POSITIVE; - int res = (max(degm, degn) == 2) ? 7 : 20; - for(i = 0; i < res; i++) { - for(j = 0; j < res; j++) { - double tryu = (i + 0.5)/res, tryv = (j + 0.5)/res; - - Vector tryp = PointAt(tryu, tryv); - double d = (tryp.Minus(p)).Magnitude(); - if(d < minDist) { - *u = tryu; - *v = tryv; - minDist = d; - } - } - } - - if(ClosestPointNewton(p, u, v, converge)) { - cached.x = *u; - cached.y = *v; - return; - } - - // If we failed to converge, then at least don't return NaN. - if(isnan(*u) || isnan(*v)) { - *u = *v = 0; - } -} - -bool SSurface::ClosestPointNewton(Vector p, double *u, double *v, bool converge) -{ - // Initial guess is in u, v; refine by Newton iteration. - Vector p0; - for(int i = 0; i < (converge ? 25 : 5); i++) { - p0 = PointAt(*u, *v); - if(converge) { - if(p0.Equals(p, RATPOLY_EPS)) { - return true; - } - } - - Vector tu, tv; - TangentsAt(*u, *v, &tu, &tv); - - // Project the point into a plane through p0, with basis tu, tv; a - // second-order thing would converge faster but needs second - // derivatives. - Vector dp = p.Minus(p0); - double du = dp.Dot(tu), dv = dp.Dot(tv); - *u += du / (tu.MagSquared()); - *v += dv / (tv.MagSquared()); - } - - if(converge) { - dbp("didn't converge"); - dbp("have %.3f %.3f %.3f", CO(p0)); - dbp("want %.3f %.3f %.3f", CO(p)); - dbp("distance = %g", (p.Minus(p0)).Magnitude()); - } - return false; -} - -bool SSurface::PointIntersectingLine(Vector p0, Vector p1, double *u, double *v) -{ - int i; - for(i = 0; i < 15; i++) { - Vector pi, p, tu, tv; - p = PointAt(*u, *v); - TangentsAt(*u, *v, &tu, &tv); - - Vector n = (tu.Cross(tv)).WithMagnitude(1); - double d = p.Dot(n); - - bool parallel; - pi = Vector::AtIntersectionOfPlaneAndLine(n, d, p0, p1, ¶llel); - if(parallel) break; - - // Check for convergence - if(pi.Equals(p, RATPOLY_EPS)) return true; - - // Adjust our guess and iterate - Vector dp = pi.Minus(p); - double du = dp.Dot(tu), dv = dp.Dot(tv); - *u += du / (tu.MagSquared()); - *v += dv / (tv.MagSquared()); - } -// dbp("didn't converge (surface intersecting line)"); - return false; -} - -Vector SSurface::ClosestPointOnThisAndSurface(SSurface *srf2, Vector p) { - // This is untested. - int i, j; - Point2d puv[2]; - SSurface *srf[2] = { this, srf2 }; - - for(j = 0; j < 2; j++) { - (srf[j])->ClosestPointTo(p, &(puv[j]), false); - } - - for(i = 0; i < 10; i++) { - Vector tu[2], tv[2], cp[2], n[2]; - double d[2]; - - for(j = 0; j < 2; j++) { - (srf[j])->TangentsAt(puv[j].x, puv[j].y, &(tu[j]), &(tv[j])); - - cp[j] = (srf[j])->PointAt(puv[j]); - - n[j] = ((tu[j]).Cross(tv[j])).WithMagnitude(1); - d[j] = (n[j]).Dot(cp[j]); - } - - if((cp[0]).Equals(cp[1], RATPOLY_EPS)) break; - - Vector p0 = Vector::AtIntersectionOfPlanes(n[0], d[0], n[1], d[1]), - dp = (n[0]).Cross(n[1]); - - Vector pc = p.ClosestPointOnLine(p0, dp); - - // Adjust our guess and iterate - for(j = 0; j < 2; j++) { - Vector dc = pc.Minus(cp[j]); - double du = dc.Dot(tu[j]), dv = dc.Dot(tv[j]); - puv[j].x += du / ((tu[j]).MagSquared()); - puv[j].y += dv / ((tv[j]).MagSquared()); - } - } - if(i >= 10) { - dbp("this and srf, didn't converge, d=%g", - (puv[0].Minus(puv[1])).Magnitude()); - } - - // If this converged, then the two points are actually equal. - return ((srf[0])->PointAt(puv[0])).Plus( - ((srf[1])->PointAt(puv[1]))).ScaledBy(0.5); -} - -void SSurface::PointOnSurfaces(SSurface *s1, SSurface *s2, - double *up, double *vp) -{ - double u[3] = { *up, 0, 0 }, v[3] = { *vp, 0, 0 }; - SSurface *srf[3] = { this, s1, s2 }; - - // Get initial guesses for (u, v) in the other surfaces - Vector p = PointAt(*u, *v); - (srf[1])->ClosestPointTo(p, &(u[1]), &(v[1]), false); - (srf[2])->ClosestPointTo(p, &(u[2]), &(v[2]), false); - - int i, j; - for(i = 0; i < 20; i++) { - // Approximate each surface by a plane - Vector p[3], tu[3], tv[3], n[3]; - double d[3]; - for(j = 0; j < 3; j++) { - p[j] = (srf[j])->PointAt(u[j], v[j]); - (srf[j])->TangentsAt(u[j], v[j], &(tu[j]), &(tv[j])); - n[j] = ((tu[j]).Cross(tv[j])).WithMagnitude(1); - d[j] = (n[j]).Dot(p[j]); - } - - // If a = b and b = c, then does a = c? No, it doesn't. - if((p[0]).Equals(p[1], RATPOLY_EPS) && - (p[1]).Equals(p[2], RATPOLY_EPS) && - (p[2]).Equals(p[0], RATPOLY_EPS)) - { - *up = u[0]; - *vp = v[0]; - return; - } - - bool parallel; - Vector pi = Vector::AtIntersectionOfPlanes(n[0], d[0], - n[1], d[1], - n[2], d[2], ¶llel); - if(parallel) break; - - for(j = 0; j < 3; j++) { - Vector dp = pi.Minus(p[j]); - double du = dp.Dot(tu[j]), dv = dp.Dot(tv[j]); - u[j] += du / (tu[j]).MagSquared(); - v[j] += dv / (tv[j]).MagSquared(); - } - } - dbp("didn't converge (three surfaces intersecting)"); -} - diff --git a/srf/raycast.cpp b/srf/raycast.cpp deleted file mode 100644 index aa85b19..0000000 --- a/srf/raycast.cpp +++ /dev/null @@ -1,613 +0,0 @@ -//----------------------------------------------------------------------------- -// Routines for ray-casting: intersecting a line segment or an infinite line -// with a surface or shell. Ray-casting against a shell is used for point-in- -// shell testing, and the intersection of edge line segments against surfaces -// is used to get rough surface-curve intersections, which are later refined -// numerically. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -// Dot product tolerance for perpendicular; this is on the direction cosine, -// so it's about 0.001 degrees. -const double SShell::DOTP_TOL = 1e-5; - -extern int FLAG; - - -double SSurface::DepartureFromCoplanar(void) { - int i, j; - int ia, ja, ib, jb, ic, jc; - double best; - - // Grab three points to define a plane; first choose (0, 0) arbitrarily. - ia = ja = 0; - // Then the point farthest from pt a. - best = VERY_NEGATIVE; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - if(i == ia && j == ja) continue; - - double dist = (ctrl[i][j]).Minus(ctrl[ia][ja]).Magnitude(); - if(dist > best) { - best = dist; - ib = i; - jb = j; - } - } - } - // Then biggest magnitude of ab cross ac. - best = VERY_NEGATIVE; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - if(i == ia && j == ja) continue; - if(i == ib && j == jb) continue; - - double mag = - ((ctrl[ia][ja].Minus(ctrl[ib][jb]))).Cross( - (ctrl[ia][ja].Minus(ctrl[i ][j ]))).Magnitude(); - if(mag > best) { - best = mag; - ic = i; - jc = j; - } - } - } - - Vector n = ((ctrl[ia][ja].Minus(ctrl[ib][jb]))).Cross( - (ctrl[ia][ja].Minus(ctrl[ic][jc]))); - n = n.WithMagnitude(1); - double d = (ctrl[ia][ja]).Dot(n); - - // Finally, calculate the deviation from each point to the plane. - double farthest = VERY_NEGATIVE; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - double dist = fabs(n.Dot(ctrl[i][j]) - d); - if(dist > farthest) { - farthest = dist; - } - } - } - return farthest; -} - -void SSurface::WeightControlPoints(void) { - int i, j; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - ctrl[i][j] = (ctrl[i][j]).ScaledBy(weight[i][j]); - } - } -} -void SSurface::UnWeightControlPoints(void) { - int i, j; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - ctrl[i][j] = (ctrl[i][j]).ScaledBy(1.0/weight[i][j]); - } - } -} -void SSurface::CopyRowOrCol(bool row, int this_ij, SSurface *src, int src_ij) { - if(row) { - int j; - for(j = 0; j <= degn; j++) { - ctrl [this_ij][j] = src->ctrl [src_ij][j]; - weight[this_ij][j] = src->weight[src_ij][j]; - } - } else { - int i; - for(i = 0; i <= degm; i++) { - ctrl [i][this_ij] = src->ctrl [i][src_ij]; - weight[i][this_ij] = src->weight[i][src_ij]; - } - } -} -void SSurface::BlendRowOrCol(bool row, int this_ij, SSurface *a, int a_ij, - SSurface *b, int b_ij) -{ - if(row) { - int j; - for(j = 0; j <= degn; j++) { - Vector c = (a->ctrl [a_ij][j]).Plus(b->ctrl [b_ij][j]); - double w = (a->weight[a_ij][j] + b->weight[b_ij][j]); - ctrl [this_ij][j] = c.ScaledBy(0.5); - weight[this_ij][j] = w / 2; - } - } else { - int i; - for(i = 0; i <= degm; i++) { - Vector c = (a->ctrl [i][a_ij]).Plus(b->ctrl [i][b_ij]); - double w = (a->weight[i][a_ij] + b->weight[i][b_ij]); - ctrl [i][this_ij] = c.ScaledBy(0.5); - weight[i][this_ij] = w / 2; - } - } -} -void SSurface::SplitInHalf(bool byU, SSurface *sa, SSurface *sb) { - sa->degm = sb->degm = degm; - sa->degn = sb->degn = degn; - - // by de Casteljau's algorithm in a projective space; so we must work - // on points (w*x, w*y, w*z, w) - WeightControlPoints(); - - switch(byU ? degm : degn) { - case 1: - sa->CopyRowOrCol (byU, 0, this, 0); - sb->CopyRowOrCol (byU, 1, this, 1); - - sa->BlendRowOrCol(byU, 1, this, 0, this, 1); - sb->BlendRowOrCol(byU, 0, this, 0, this, 1); - break; - - case 2: - sa->CopyRowOrCol (byU, 0, this, 0); - sb->CopyRowOrCol (byU, 2, this, 2); - - sa->BlendRowOrCol(byU, 1, this, 0, this, 1); - sb->BlendRowOrCol(byU, 1, this, 1, this, 2); - - sa->BlendRowOrCol(byU, 2, sa, 1, sb, 1); - sb->BlendRowOrCol(byU, 0, sa, 1, sb, 1); - break; - - case 3: { - SSurface st; - st.degm = degm; st.degn = degn; - - sa->CopyRowOrCol (byU, 0, this, 0); - sb->CopyRowOrCol (byU, 3, this, 3); - - sa->BlendRowOrCol(byU, 1, this, 0, this, 1); - sb->BlendRowOrCol(byU, 2, this, 2, this, 3); - st. BlendRowOrCol(byU, 0, this, 1, this, 2); // scratch var - - sa->BlendRowOrCol(byU, 2, sa, 1, &st, 0); - sb->BlendRowOrCol(byU, 1, sb, 2, &st, 0); - - sa->BlendRowOrCol(byU, 3, sa, 2, sb, 1); - sb->BlendRowOrCol(byU, 0, sa, 2, sb, 1); - break; - } - - default: oops(); - } - - sa->UnWeightControlPoints(); - sb->UnWeightControlPoints(); - UnWeightControlPoints(); -} - -//----------------------------------------------------------------------------- -// Find all points where the indicated finite (if segment) or infinite (if not -// segment) line intersects our surface. Report them in uv space in the list. -// We first do a bounding box check; if the line doesn't intersect, then we're -// done. If it does, then we check how small our surface is. If it's big, -// then we subdivide into quarters and recurse. If it's small, then we refine -// by Newton's method and record the point. -//----------------------------------------------------------------------------- -void SSurface::AllPointsIntersectingUntrimmed(Vector a, Vector b, - int *cnt, int *level, - List *l, bool segment, - SSurface *sorig) -{ - // Test if the line intersects our axis-aligned bounding box; if no, then - // no possibility of an intersection - if(LineEntirelyOutsideBbox(a, b, segment)) return; - - if(*cnt > 2000) { - dbp("!!! too many subdivisions (level=%d)!", *level); - dbp("degm = %d degn = %d", degm, degn); - return; - } - (*cnt)++; - - // If we might intersect, and the surface is small, then switch to Newton - // iterations. - if(DepartureFromCoplanar() < 0.2*SS.ChordTolMm()) { - Vector p = (ctrl[0 ][0 ]).Plus( - ctrl[0 ][degn]).Plus( - ctrl[degm][0 ]).Plus( - ctrl[degm][degn]).ScaledBy(0.25); - Inter inter; - sorig->ClosestPointTo(p, &(inter.p.x), &(inter.p.y), false); - if(sorig->PointIntersectingLine(a, b, &(inter.p.x), &(inter.p.y))) { - Vector p = sorig->PointAt(inter.p.x, inter.p.y); - // Debug check, verify that the point lies in both surfaces - // (which it ought to, since the surfaces should be coincident) - double u, v; - ClosestPointTo(p, &u, &v); - l->Add(&inter); - } else { - // Might not converge if line is almost tangent to surface... - } - return; - } - - // But the surface is big, so split it, alternating by u and v - SSurface surf0, surf1; - SplitInHalf((*level & 1) == 0, &surf0, &surf1); - - int nextLevel = (*level) + 1; - (*level) = nextLevel; - surf0.AllPointsIntersectingUntrimmed(a, b, cnt, level, l, segment, sorig); - (*level) = nextLevel; - surf1.AllPointsIntersectingUntrimmed(a, b, cnt, level, l, segment, sorig); -} - -//----------------------------------------------------------------------------- -// Find all points where a line through a and b intersects our surface, and -// add them to the list. If seg is true then report only intersections that -// lie within the finite line segment (not including the endpoints); otherwise -// we work along the infinite line. And we report either just intersections -// inside the trim curve, or any intersection with u, v in [0, 1]. And we -// either disregard or report tangent points. -//----------------------------------------------------------------------------- -void SSurface::AllPointsIntersecting(Vector a, Vector b, - List *l, - bool seg, bool trimmed, bool inclTangent) -{ - if(LineEntirelyOutsideBbox(a, b, seg)) return; - - Vector ba = b.Minus(a); - double bam = ba.Magnitude(); - - List inters; - ZERO(&inters); - - // All the intersections between the line and the surface; either special - // cases that we can quickly solve in closed form, or general numerical. - Vector center, axis, start, finish; - double radius; - if(degm == 1 && degn == 1) { - // Against a plane, easy. - Vector n = NormalAt(0, 0).WithMagnitude(1); - double d = n.Dot(PointAt(0, 0)); - // Trim to line segment now if requested, don't generate points that - // would just get discarded later. - if(!seg || - (n.Dot(a) > d + LENGTH_EPS && n.Dot(b) < d - LENGTH_EPS) || - (n.Dot(b) > d + LENGTH_EPS && n.Dot(a) < d - LENGTH_EPS)) - { - Vector p = Vector::AtIntersectionOfPlaneAndLine(n, d, a, b, NULL); - Inter inter; - ClosestPointTo(p, &(inter.p.x), &(inter.p.y)); - inters.Add(&inter); - } - } else if(IsCylinder(&axis, ¢er, &radius, &start, &finish)) { - // This one can be solved in closed form too. - Vector ab = b.Minus(a); - if(axis.Cross(ab).Magnitude() < LENGTH_EPS) { - // edge is parallel to axis of cylinder, no intersection points - return; - } - // A coordinate system centered at the center of the circle, with - // the edge under test horizontal - Vector u, v, n = axis.WithMagnitude(1); - u = (ab.Minus(n.ScaledBy(ab.Dot(n)))).WithMagnitude(1); - v = n.Cross(u); - Point2d ap = (a.Minus(center)).DotInToCsys(u, v, n).ProjectXy(), - bp = (b.Minus(center)).DotInToCsys(u, v, n).ProjectXy(), - sp = (start. Minus(center)).DotInToCsys(u, v, n).ProjectXy(), - fp = (finish.Minus(center)).DotInToCsys(u, v, n).ProjectXy(); - - double thetas = atan2(sp.y, sp.x), thetaf = atan2(fp.y, fp.x); - - Point2d ip[2]; - int ip_n = 0; - if(fabs(fabs(ap.y) - radius) < LENGTH_EPS) { - // tangent - if(inclTangent) { - ip[0] = Point2d::From(0, ap.y); - ip_n = 1; - } - } else if(fabs(ap.y) < radius) { - // two intersections - double xint = sqrt(radius*radius - ap.y*ap.y); - ip[0] = Point2d::From(-xint, ap.y); - ip[1] = Point2d::From( xint, ap.y); - ip_n = 2; - } - int i; - for(i = 0; i < ip_n; i++) { - double t = (ip[i].Minus(ap)).DivPivoting(bp.Minus(ap)); - // This is a point on the circle; but is it on the arc? - Point2d pp = ap.Plus((bp.Minus(ap)).ScaledBy(t)); - double theta = atan2(pp.y, pp.x); - double dp = WRAP_SYMMETRIC(theta - thetas, 2*PI), - df = WRAP_SYMMETRIC(thetaf - thetas, 2*PI); - double tol = LENGTH_EPS/radius; - - if((df > 0 && ((dp < -tol) || (dp > df + tol))) || - (df < 0 && ((dp > tol) || (dp < df - tol)))) - { - continue; - } - - Vector p = a.Plus((b.Minus(a)).ScaledBy(t)); - - Inter inter; - ClosestPointTo(p, &(inter.p.x), &(inter.p.y)); - inters.Add(&inter); - } - } else { - // General numerical solution by subdivision, fallback - int cnt = 0, level = 0; - AllPointsIntersectingUntrimmed(a, b, &cnt, &level, &inters, seg, this); - } - - // Remove duplicate intersection points - inters.ClearTags(); - int i, j; - for(i = 0; i < inters.n; i++) { - for(j = i + 1; j < inters.n; j++) { - if(inters.elem[i].p.Equals(inters.elem[j].p)) { - inters.elem[j].tag = 1; - } - } - } - inters.RemoveTagged(); - - for(i = 0; i < inters.n; i++) { - Point2d puv = inters.elem[i].p; - - // Make sure the point lies within the finite line segment - Vector pxyz = PointAt(puv.x, puv.y); - double t = (pxyz.Minus(a)).DivPivoting(ba); - if(seg && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) { - continue; - } - - // And that it lies inside our trim region - Point2d dummy = { 0, 0 }; - int c = bsp->ClassifyPoint(puv, dummy, this); - if(trimmed && c == SBspUv::OUTSIDE) { - continue; - } - - // It does, so generate the intersection - SInter si; - si.p = pxyz; - si.surfNormal = NormalAt(puv.x, puv.y); - si.pinter = puv; - si.srf = this; - si.onEdge = (c != SBspUv::INSIDE); - l->Add(&si); - } - - inters.Clear(); -} - -void SShell::AllPointsIntersecting(Vector a, Vector b, - List *il, - bool seg, bool trimmed, bool inclTangent) -{ - SSurface *ss; - for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { - ss->AllPointsIntersecting(a, b, il, seg, trimmed, inclTangent); - } -} - - - -int SShell::ClassifyRegion(Vector edge_n, Vector inter_surf_n, - Vector edge_surf_n) -{ - double dot = inter_surf_n.DirectionCosineWith(edge_n); - if(fabs(dot) < DOTP_TOL) { - // The edge's surface and the edge-on-face surface - // are coincident. Test the edge's surface normal - // to see if it's with same or opposite normals. - if(inter_surf_n.Dot(edge_surf_n) > 0) { - return COINC_SAME; - } else { - return COINC_OPP; - } - } else if(dot > 0) { - return OUTSIDE; - } else { - return INSIDE; - } -} - -//----------------------------------------------------------------------------- -// Does the given point lie on our shell? There are many cases; inside and -// outside are obvious, but then there's all the edge-on-edge and edge-on-face -// possibilities. -// -// To calculate, we intersect a ray through p with our shell, and classify -// using the closest intersection point. If the ray hits a surface on edge, -// then just reattempt in a different random direction. -//----------------------------------------------------------------------------- -bool SShell::ClassifyEdge(int *indir, int *outdir, - Vector ea, Vector eb, - Vector p, - Vector edge_n_in, Vector edge_n_out, Vector surf_n) -{ - List l; - ZERO(&l); - - srand(0); - - // First, check for edge-on-edge - int edge_inters = 0; - Vector inter_surf_n[2], inter_edge_n[2]; - SSurface *srf; - for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) { - if(srf->LineEntirelyOutsideBbox(ea, eb, true)) continue; - - SEdgeList *sel = &(srf->edges); - SEdge *se; - for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) { - if((ea.Equals(se->a) && eb.Equals(se->b)) || - (eb.Equals(se->a) && ea.Equals(se->b)) || - p.OnLineSegment(se->a, se->b)) - { - if(edge_inters < 2) { - // Edge-on-edge case - Point2d pm; - srf->ClosestPointTo(p, &pm, false); - // A vector normal to the surface, at the intersection point - inter_surf_n[edge_inters] = srf->NormalAt(pm); - // A vector normal to the intersecting edge (but within the - // intersecting surface) at the intersection point, pointing - // out. - inter_edge_n[edge_inters] = - (inter_surf_n[edge_inters]).Cross((se->b).Minus((se->a))); - } - - edge_inters++; - } - } - } - - if(edge_inters == 2) { - // TODO, make this use the appropriate curved normals - double dotp[2]; - for(int i = 0; i < 2; i++) { - dotp[i] = edge_n_out.DirectionCosineWith(inter_surf_n[i]); - } - - if(fabs(dotp[1]) < DOTP_TOL) { - SWAP(double, dotp[0], dotp[1]); - SWAP(Vector, inter_surf_n[0], inter_surf_n[1]); - SWAP(Vector, inter_edge_n[0], inter_edge_n[1]); - } - - int coinc = (surf_n.Dot(inter_surf_n[0])) > 0 ? COINC_SAME : COINC_OPP; - - if(fabs(dotp[0]) < DOTP_TOL && fabs(dotp[1]) < DOTP_TOL) { - // This is actually an edge on face case, just that the face - // is split into two pieces joining at our edge. - *indir = coinc; - *outdir = coinc; - } else if(fabs(dotp[0]) < DOTP_TOL && dotp[1] > DOTP_TOL) { - if(edge_n_out.Dot(inter_edge_n[0]) > 0) { - *indir = coinc; - *outdir = OUTSIDE; - } else { - *indir = INSIDE; - *outdir = coinc; - } - } else if(fabs(dotp[0]) < DOTP_TOL && dotp[1] < -DOTP_TOL) { - if(edge_n_out.Dot(inter_edge_n[0]) > 0) { - *indir = coinc; - *outdir = INSIDE; - } else { - *indir = OUTSIDE; - *outdir = coinc; - } - } else if(dotp[0] > DOTP_TOL && dotp[1] > DOTP_TOL) { - *indir = INSIDE; - *outdir = OUTSIDE; - } else if(dotp[0] < -DOTP_TOL && dotp[1] < -DOTP_TOL) { - *indir = OUTSIDE; - *outdir = INSIDE; - } else { - // Edge is tangent to the shell at shell's edge, so can't be - // a boundary of the surface. - return false; - } - return true; - } - - if(edge_inters != 0) dbp("bad, edge_inters=%d", edge_inters); - - // Next, check for edge-on-surface. The ray-casting for edge-inside-shell - // would catch this too, but test separately, for speed (since many edges - // are on surface) and for numerical stability, so we don't pick up - // the additional error from the line intersection. - - for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) { - if(srf->LineEntirelyOutsideBbox(ea, eb, true)) continue; - - Point2d puv; - srf->ClosestPointTo(p, &(puv.x), &(puv.y), false); - Vector pp = srf->PointAt(puv); - - if((pp.Minus(p)).Magnitude() > LENGTH_EPS) continue; - Point2d dummy = { 0, 0 }; - int c = srf->bsp->ClassifyPoint(puv, dummy, srf); - if(c == SBspUv::OUTSIDE) continue; - - // Edge-on-face (unless edge-on-edge above superceded) - Point2d pin, pout; - srf->ClosestPointTo(p.Plus(edge_n_in), &pin, false); - srf->ClosestPointTo(p.Plus(edge_n_out), &pout, false); - - Vector surf_n_in = srf->NormalAt(pin), - surf_n_out = srf->NormalAt(pout); - - *indir = ClassifyRegion(edge_n_in, surf_n_in, surf_n); - *outdir = ClassifyRegion(edge_n_out, surf_n_out, surf_n); - return true; - } - - // Edge is not on face or on edge; so it's either inside or outside - // the shell, and we'll determine which by raycasting. - int cnt = 0; - for(;;) { - // Cast a ray in a random direction (two-sided so that we test if - // the point lies on a surface, but use only one side for in/out - // testing) - Vector ray = Vector::From(Random(1), Random(1), Random(1)); - - AllPointsIntersecting( - p.Minus(ray), p.Plus(ray), &l, false, true, false); - - // no intersections means it's outside - *indir = OUTSIDE; - *outdir = OUTSIDE; - double dmin = VERY_POSITIVE; - bool onEdge = false; - edge_inters = 0; - - SInter *si; - for(si = l.First(); si; si = l.NextAfter(si)) { - double t = ((si->p).Minus(p)).DivPivoting(ray); - if(t*ray.Magnitude() < -LENGTH_EPS) { - // wrong side, doesn't count - continue; - } - - double d = ((si->p).Minus(p)).Magnitude(); - - // We actually should never hit this case; it should have been - // handled above. - if(d < LENGTH_EPS && si->onEdge) { - edge_inters++; - } - - if(d < dmin) { - dmin = d; - // Edge does not lie on surface; either strictly inside - // or strictly outside - if((si->surfNormal).Dot(ray) > 0) { - *indir = INSIDE; - *outdir = INSIDE; - } else { - *indir = OUTSIDE; - *outdir = OUTSIDE; - } - onEdge = si->onEdge; - } - } - l.Clear(); - - // If the point being tested lies exactly on an edge of the shell, - // then our ray always lies on edge, and that's okay. Otherwise - // try again in a different random direction. - if(!onEdge) break; - if(cnt++ > 5) { - dbp("can't find a ray that doesn't hit on edge!"); - dbp("on edge = %d, edge_inters = %d", onEdge, edge_inters); - SS.nakedEdges.AddEdge(ea, eb); - break; - } - } - - return true; -} - diff --git a/srf/surface.cpp b/srf/surface.cpp deleted file mode 100644 index 92bbe9d..0000000 --- a/srf/surface.cpp +++ /dev/null @@ -1,884 +0,0 @@ -//----------------------------------------------------------------------------- -// Anything involving surfaces and sets of surfaces (i.e., shells); except -// for the real math, which is in ratpoly.cpp. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "../solvespace.h" - -SSurface SSurface::FromExtrusionOf(SBezier *sb, Vector t0, Vector t1) { - SSurface ret; - ZERO(&ret); - - ret.degm = sb->deg; - ret.degn = 1; - - int i; - for(i = 0; i <= ret.degm; i++) { - ret.ctrl[i][0] = (sb->ctrl[i]).Plus(t0); - ret.weight[i][0] = sb->weight[i]; - - ret.ctrl[i][1] = (sb->ctrl[i]).Plus(t1); - ret.weight[i][1] = sb->weight[i]; - } - - return ret; -} - -bool SSurface::IsExtrusion(SBezier *of, Vector *alongp) { - int i; - - if(degn != 1) return false; - - Vector along = (ctrl[0][1]).Minus(ctrl[0][0]); - for(i = 0; i <= degm; i++) { - if((fabs(weight[i][1] - weight[i][0]) < LENGTH_EPS) && - ((ctrl[i][1]).Minus(ctrl[i][0])).Equals(along)) - { - continue; - } - return false; - } - - // yes, we are a surface of extrusion; copy the original curve and return - if(of) { - for(i = 0; i <= degm; i++) { - of->weight[i] = weight[i][0]; - of->ctrl[i] = ctrl[i][0]; - } - of->deg = degm; - *alongp = along; - } - return true; -} - -bool SSurface::IsCylinder(Vector *axis, Vector *center, double *r, - Vector *start, Vector *finish) -{ - SBezier sb; - if(!IsExtrusion(&sb, axis)) return false; - if(!sb.IsCircle(*axis, center, r)) return false; - - *start = sb.ctrl[0]; - *finish = sb.ctrl[2]; - return true; -} - -SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis, - double thetas, double thetaf) -{ - SSurface ret; - ZERO(&ret); - - - ret.degm = sb->deg; - ret.degn = 2; - - double dtheta = fabs(WRAP_SYMMETRIC(thetaf - thetas, 2*PI)); - - // We now wish to revolve the curve about the z axis - int i; - for(i = 0; i <= ret.degm; i++) { - Vector p = sb->ctrl[i]; - - Vector ps = p.RotatedAbout(pt, axis, thetas), - pf = p.RotatedAbout(pt, axis, thetaf); - - Vector ct; - if(ps.Equals(pf)) { - // Degenerate case: a control point lies on the axis of revolution, - // so we get three coincident control points. - ct = ps; - } else { - // Normal case, the control point sweeps out a circle. - Vector c = ps.ClosestPointOnLine(pt, axis); - - Vector rs = ps.Minus(c), - rf = pf.Minus(c); - - Vector ts = axis.Cross(rs), - tf = axis.Cross(rf); - - ct = Vector::AtIntersectionOfLines(ps, ps.Plus(ts), - pf, pf.Plus(tf), - NULL, NULL, NULL); - } - - ret.ctrl[i][0] = ps; - ret.ctrl[i][1] = ct; - ret.ctrl[i][2] = pf; - - ret.weight[i][0] = sb->weight[i]; - ret.weight[i][1] = sb->weight[i]*cos(dtheta/2); - ret.weight[i][2] = sb->weight[i]; - } - - return ret; -} - -SSurface SSurface::FromPlane(Vector pt, Vector u, Vector v) { - SSurface ret; - ZERO(&ret); - - ret.degm = 1; - ret.degn = 1; - - ret.weight[0][0] = ret.weight[0][1] = 1; - ret.weight[1][0] = ret.weight[1][1] = 1; - - ret.ctrl[0][0] = pt; - ret.ctrl[0][1] = pt.Plus(u); - ret.ctrl[1][0] = pt.Plus(v); - ret.ctrl[1][1] = pt.Plus(v).Plus(u); - - return ret; -} - -SSurface SSurface::FromTransformationOf(SSurface *a, - Vector t, Quaternion q, double scale, - bool includingTrims) -{ - SSurface ret; - ZERO(&ret); - - ret.h = a->h; - ret.color = a->color; - ret.face = a->face; - - ret.degm = a->degm; - ret.degn = a->degn; - int i, j; - for(i = 0; i <= 3; i++) { - for(j = 0; j <= 3; j++) { - ret.ctrl[i][j] = a->ctrl[i][j]; - ret.ctrl[i][j] = (ret.ctrl[i][j]).ScaledBy(scale); - ret.ctrl[i][j] = (q.Rotate(ret.ctrl[i][j])).Plus(t); - - ret.weight[i][j] = a->weight[i][j]; - } - } - - if(includingTrims) { - STrimBy *stb; - for(stb = a->trim.First(); stb; stb = a->trim.NextAfter(stb)) { - STrimBy n = *stb; - n.start = n.start.ScaledBy(scale); - n.finish = n.finish.ScaledBy(scale); - n.start = (q.Rotate(n.start)) .Plus(t); - n.finish = (q.Rotate(n.finish)).Plus(t); - ret.trim.Add(&n); - } - } - - if(scale < 0) { - // If we mirror every surface of a shell, then it will end up inside - // out. So fix that here. - ret.Reverse(); - } - - return ret; -} - -void SSurface::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) { - *ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE); - *ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE); - - int i, j; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - (ctrl[i][j]).MakeMaxMin(ptMax, ptMin); - } - } -} - -bool SSurface::LineEntirelyOutsideBbox(Vector a, Vector b, bool segment) { - Vector amax, amin; - GetAxisAlignedBounding(&amax, &amin); - if(!Vector::BoundingBoxIntersectsLine(amax, amin, a, b, segment)) { - // The line segment could fail to intersect the bbox, but lie entirely - // within it and intersect the surface. - if(a.OutsideAndNotOn(amax, amin) && b.OutsideAndNotOn(amax, amin)) { - return true; - } - } - return false; -} - -//----------------------------------------------------------------------------- -// Generate the piecewise linear approximation of the trim stb, which applies -// to the curve sc. -//----------------------------------------------------------------------------- -void SSurface::MakeTrimEdgesInto(SEdgeList *sel, int flags, - SCurve *sc, STrimBy *stb) -{ - Vector prev; - bool inCurve = false, empty = true; - double u = 0, v = 0; - - int i, first, last, increment; - if(stb->backwards) { - first = sc->pts.n - 1; - last = 0; - increment = -1; - } else { - first = 0; - last = sc->pts.n - 1; - increment = 1; - } - for(i = first; i != (last + increment); i += increment) { - Vector tpt, *pt = &(sc->pts.elem[i].p); - - if(flags & AS_UV) { - ClosestPointTo(*pt, &u, &v); - tpt = Vector::From(u, v, 0); - } else { - tpt = *pt; - } - - if(inCurve) { - sel->AddEdge(prev, tpt, sc->h.v, stb->backwards); - empty = false; - } - - prev = tpt; // either uv or xyz, depending on flags - - if(pt->Equals(stb->start)) inCurve = true; - if(pt->Equals(stb->finish)) inCurve = false; - } - if(inCurve) dbp("trim was unterminated"); - if(empty) dbp("trim was empty"); -} - -//----------------------------------------------------------------------------- -// Generate all of our trim curves, in piecewise linear form. We can do -// so in either uv or xyz coordinates. And if requested, then we can use -// the split curves from useCurvesFrom instead of the curves in our own -// shell. -//----------------------------------------------------------------------------- -void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, int flags, - SShell *useCurvesFrom) -{ - STrimBy *stb; - for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { - SCurve *sc = shell->curve.FindById(stb->curve); - - // We have the option to use the curves from another shell; this - // is relevant when generating the coincident edges while doing the - // Booleans, since the curves from the output shell will be split - // against any intersecting surfaces (and the originals aren't). - if(useCurvesFrom) { - sc = useCurvesFrom->curve.FindById(sc->newH); - } - - MakeTrimEdgesInto(sel, flags, sc, stb); - } -} - -//----------------------------------------------------------------------------- -// Compute the exact tangent to the intersection curve between two surfaces, -// by taking the cross product of the surface normals. We choose the direction -// of this tangent so that its dot product with dir is positive. -//----------------------------------------------------------------------------- -Vector SSurface::ExactSurfaceTangentAt(Vector p, SSurface *srfA, SSurface *srfB, - Vector dir) -{ - Point2d puva, puvb; - srfA->ClosestPointTo(p, &puva); - srfB->ClosestPointTo(p, &puvb); - Vector ts = (srfA->NormalAt(puva)).Cross( - (srfB->NormalAt(puvb))); - ts = ts.WithMagnitude(1); - if(ts.Dot(dir) < 0) { - ts = ts.ScaledBy(-1); - } - return ts; -} - -//----------------------------------------------------------------------------- -// Report our trim curves. If a trim curve is exact and sbl is not null, then -// add its exact form to sbl. Otherwise, add its piecewise linearization to -// sel. -//----------------------------------------------------------------------------- -void SSurface::MakeSectionEdgesInto(SShell *shell, - SEdgeList *sel, SBezierList *sbl) -{ - STrimBy *stb; - for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { - SCurve *sc = shell->curve.FindById(stb->curve); - SBezier *sb = &(sc->exact); - - if(sbl && sc->isExact && (sb->deg != 1 || !sel)) { - double ts, tf; - if(stb->backwards) { - sb->ClosestPointTo(stb->start, &tf); - sb->ClosestPointTo(stb->finish, &ts); - } else { - sb->ClosestPointTo(stb->start, &ts); - sb->ClosestPointTo(stb->finish, &tf); - } - SBezier junk_bef, keep_aft; - sb->SplitAt(ts, &junk_bef, &keep_aft); - // In the kept piece, the range that used to go from ts to 1 - // now goes from 0 to 1; so rescale tf appropriately. - tf = (tf - ts)/(1 - ts); - - SBezier keep_bef, junk_aft; - keep_aft.SplitAt(tf, &keep_bef, &junk_aft); - - sbl->l.Add(&keep_bef); - } else if(sbl && !sel && !sc->isExact) { - // We must approximate this trim curve, as piecewise cubic sections. - SSurface *srfA = shell->surface.FindById(sc->surfA), - *srfB = shell->surface.FindById(sc->surfB); - - Vector s = stb->backwards ? stb->finish : stb->start, - f = stb->backwards ? stb->start : stb->finish; - - int sp, fp; - for(sp = 0; sp < sc->pts.n; sp++) { - if(s.Equals(sc->pts.elem[sp].p)) break; - } - if(sp >= sc->pts.n) return; - for(fp = sp; fp < sc->pts.n; fp++) { - if(f.Equals(sc->pts.elem[fp].p)) break; - } - if(fp >= sc->pts.n) return; - // So now the curve we want goes from elem[sp] to elem[fp] - - while(sp < fp) { - // Initially, we'll try approximating the entire trim curve - // as a single Bezier segment - int fpt = fp; - - for(;;) { - // So construct a cubic Bezier with the correct endpoints - // and tangents for the current span. - Vector st = sc->pts.elem[sp].p, - ft = sc->pts.elem[fpt].p, - sf = ft.Minus(st); - double m = sf.Magnitude() / 3; - - Vector stan = ExactSurfaceTangentAt(st, srfA, srfB, sf), - ftan = ExactSurfaceTangentAt(ft, srfA, srfB, sf); - - SBezier sb = SBezier::From(st, - st.Plus (stan.WithMagnitude(m)), - ft.Minus(ftan.WithMagnitude(m)), - ft); - - // And test how much this curve deviates from the - // intermediate points (if any). - int i; - bool tooFar = false; - for(i = sp + 1; i <= (fpt - 1); i++) { - Vector p = sc->pts.elem[i].p; - double t; - sb.ClosestPointTo(p, &t, false); - Vector pp = sb.PointAt(t); - if((pp.Minus(p)).Magnitude() > SS.ChordTolMm()/2) { - tooFar = true; - break; - } - } - - if(tooFar) { - // Deviates by too much, so try a shorter span - fpt--; - continue; - } else { - // Okay, so use this piece and break. - sbl->l.Add(&sb); - break; - } - } - - // And continue interpolating, starting wherever the curve - // we just generated finishes. - sp = fpt; - } - } else { - if(sel) MakeTrimEdgesInto(sel, AS_XYZ, sc, stb); - } - } -} - -void SSurface::TriangulateInto(SShell *shell, SMesh *sm) { - SEdgeList el; - ZERO(&el); - - MakeEdgesInto(shell, &el, AS_UV); - - SPolygon poly; - ZERO(&poly); - if(el.AssemblePolygon(&poly, NULL, true)) { - int i, start = sm->l.n; - if(degm == 1 && degn == 1) { - // A surface with curvature along one direction only; so - // choose the triangulation with chords that lie as much - // as possible within the surface. And since the trim curves - // have been pwl'd to within the desired chord tol, that will - // produce a surface good to within roughly that tol. - // - // If this is just a plane (degree (1, 1)) then the triangulation - // code will notice that, and not bother checking chord tols. - poly.UvTriangulateInto(sm, this); - } else { - // A surface with compound curvature. So we must overlay a - // two-dimensional grid, and triangulate around that. - poly.UvGridTriangulateInto(sm, this); - } - - STriMeta meta = { face, color }; - for(i = start; i < sm->l.n; i++) { - STriangle *st = &(sm->l.elem[i]); - st->meta = meta; - st->an = NormalAt(st->a.x, st->a.y); - st->bn = NormalAt(st->b.x, st->b.y); - st->cn = NormalAt(st->c.x, st->c.y); - st->a = PointAt(st->a.x, st->a.y); - st->b = PointAt(st->b.x, st->b.y); - st->c = PointAt(st->c.x, st->c.y); - // Works out that my chosen contour direction is inconsistent with - // the triangle direction, sigh. - st->FlipNormal(); - } - } else { - dbp("failed to assemble polygon to trim nurbs surface in uv space"); - } - - el.Clear(); - poly.Clear(); -} - -//----------------------------------------------------------------------------- -// Reverse the parametrisation of one of our dimensions, which flips the -// normal. We therefore must reverse all our trim curves too. The uv -// coordinates change, but trim curves are stored as xyz so nothing happens -//----------------------------------------------------------------------------- -void SSurface::Reverse(void) { - int i, j; - for(i = 0; i < (degm+1)/2; i++) { - for(j = 0; j <= degn; j++) { - SWAP(Vector, ctrl[i][j], ctrl[degm-i][j]); - SWAP(double, weight[i][j], weight[degm-i][j]); - } - } - - STrimBy *stb; - for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) { - stb->backwards = !stb->backwards; - SWAP(Vector, stb->start, stb->finish); - } -} - -void SSurface::ScaleSelfBy(double s) { - int i, j; - for(i = 0; i <= degm; i++) { - for(j = 0; j <= degn; j++) { - ctrl[i][j] = ctrl[i][j].ScaledBy(s); - } - } -} - -void SSurface::Clear(void) { - trim.Clear(); -} - -void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1, - int color) -{ - // Make the extrusion direction consistent with respect to the normal - // of the sketch we're extruding. - if((t0.Minus(t1)).Dot(sbls->normal) < 0) { - SWAP(Vector, t0, t1); - } - - // Define a coordinate system to contain the original sketch, and get - // a bounding box in that csys - Vector n = sbls->normal.ScaledBy(-1); - Vector u = n.Normal(0), v = n.Normal(1); - Vector orig = sbls->point; - double umax = 1e-10, umin = 1e10; - sbls->GetBoundingProjd(u, orig, &umin, &umax); - double vmax = 1e-10, vmin = 1e10; - sbls->GetBoundingProjd(v, orig, &vmin, &vmax); - // and now fix things up so that all u and v lie between 0 and 1 - orig = orig.Plus(u.ScaledBy(umin)); - orig = orig.Plus(v.ScaledBy(vmin)); - u = u.ScaledBy(umax - umin); - v = v.ScaledBy(vmax - vmin); - - // So we can now generate the top and bottom surfaces of the extrusion, - // planes within a translated (and maybe mirrored) version of that csys. - SSurface s0, s1; - s0 = SSurface::FromPlane(orig.Plus(t0), u, v); - s0.color = color; - s1 = SSurface::FromPlane(orig.Plus(t1).Plus(u), u.ScaledBy(-1), v); - s1.color = color; - hSSurface hs0 = surface.AddAndAssignId(&s0), - hs1 = surface.AddAndAssignId(&s1); - - // Now go through the input curves. For each one, generate its surface - // of extrusion, its two translated trim curves, and one trim line. We - // go through by loops so that we can assign the lines correctly. - SBezierLoop *sbl; - for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) { - SBezier *sb; - - typedef struct { - hSCurve hc; - hSSurface hs; - } TrimLine; - List trimLines; - ZERO(&trimLines); - - for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { - // Generate the surface of extrusion of this curve, and add - // it to the list - SSurface ss = SSurface::FromExtrusionOf(sb, t0, t1); - ss.color = color; - hSSurface hsext = surface.AddAndAssignId(&ss); - - // Translate the curve by t0 and t1 to produce two trim curves - SCurve sc; - ZERO(&sc); - sc.isExact = true; - sc.exact = sb->TransformedBy(t0, Quaternion::IDENTITY, 1.0); - (sc.exact).MakePwlInto(&(sc.pts)); - sc.surfA = hs0; - sc.surfB = hsext; - hSCurve hc0 = curve.AddAndAssignId(&sc); - - ZERO(&sc); - sc.isExact = true; - sc.exact = sb->TransformedBy(t1, Quaternion::IDENTITY, 1.0); - (sc.exact).MakePwlInto(&(sc.pts)); - sc.surfA = hs1; - sc.surfB = hsext; - hSCurve hc1 = curve.AddAndAssignId(&sc); - - STrimBy stb0, stb1; - // The translated curves trim the flat top and bottom surfaces. - stb0 = STrimBy::EntireCurve(this, hc0, false); - stb1 = STrimBy::EntireCurve(this, hc1, true); - (surface.FindById(hs0))->trim.Add(&stb0); - (surface.FindById(hs1))->trim.Add(&stb1); - - // The translated curves also trim the surface of extrusion. - stb0 = STrimBy::EntireCurve(this, hc0, true); - stb1 = STrimBy::EntireCurve(this, hc1, false); - (surface.FindById(hsext))->trim.Add(&stb0); - (surface.FindById(hsext))->trim.Add(&stb1); - - // And form the trim line - Vector pt = sb->Finish(); - ZERO(&sc); - sc.isExact = true; - sc.exact = SBezier::From(pt.Plus(t0), pt.Plus(t1)); - (sc.exact).MakePwlInto(&(sc.pts)); - hSCurve hl = curve.AddAndAssignId(&sc); - // save this for later - TrimLine tl; - tl.hc = hl; - tl.hs = hsext; - trimLines.Add(&tl); - } - - int i; - for(i = 0; i < trimLines.n; i++) { - TrimLine *tl = &(trimLines.elem[i]); - SSurface *ss = surface.FindById(tl->hs); - - TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]); - - STrimBy stb; - stb = STrimBy::EntireCurve(this, tl->hc, true); - ss->trim.Add(&stb); - stb = STrimBy::EntireCurve(this, tlp->hc, false); - ss->trim.Add(&stb); - - (curve.FindById(tl->hc))->surfA = ss->h; - (curve.FindById(tlp->hc))->surfB = ss->h; - } - trimLines.Clear(); - } -} - - -void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis, - int color) -{ - SBezierLoop *sbl; - - int i0 = surface.n, i; - - // Normalize the axis direction so that the direction of revolution - // ends up parallel to the normal of the sketch, on the side of the - // axis where the sketch is. - Vector pto; - double md = VERY_NEGATIVE; - for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) { - SBezier *sb; - for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { - // Choose the point farthest from the axis; we'll get garbage - // if we choose a point that lies on the axis, for example. - // (And our surface will be self-intersecting if the sketch - // spans the axis, so don't worry about that.) - Vector p = sb->Start(); - double d = p.DistanceToLine(pt, axis); - if(d > md) { - md = d; - pto = p; - } - } - } - Vector ptc = pto.ClosestPointOnLine(pt, axis), - up = (pto.Minus(ptc)).WithMagnitude(1), - vp = (sbls->normal).Cross(up); - if(vp.Dot(axis) < 0) { - axis = axis.ScaledBy(-1); - } - - // Now we actually build and trim the surfaces. - for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) { - int i, j; - SBezier *sb, *prev; - - typedef struct { - hSSurface d[4]; - } Revolved; - List hsl; - ZERO(&hsl); - - for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { - Revolved revs; - for(j = 0; j < 4; j++) { - if(sb->deg == 1 && - (sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS && - (sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS) - { - // This is a line on the axis of revolution; it does - // not contribute a surface. - revs.d[j].v = 0; - } else { - SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis, - (PI/2)*j, - (PI/2)*(j+1)); - ss.color = color; - revs.d[j] = surface.AddAndAssignId(&ss); - } - } - hsl.Add(&revs); - } - - for(i = 0; i < sbl->l.n; i++) { - Revolved revs = hsl.elem[i], - revsp = hsl.elem[WRAP(i-1, sbl->l.n)]; - - sb = &(sbl->l.elem[i]); - prev = &(sbl->l.elem[WRAP(i-1, sbl->l.n)]); - - for(j = 0; j < 4; j++) { - SCurve sc; - Quaternion qs = Quaternion::From(axis, (PI/2)*j); - // we want Q*(x - p) + p = Q*x + (p - Q*p) - Vector ts = pt.Minus(qs.Rotate(pt)); - - // If this input curve generate a surface, then trim that - // surface with the rotated version of the input curve. - if(revs.d[j].v) { - ZERO(&sc); - sc.isExact = true; - sc.exact = sb->TransformedBy(ts, qs, 1.0); - (sc.exact).MakePwlInto(&(sc.pts)); - sc.surfA = revs.d[j]; - sc.surfB = revs.d[WRAP(j-1, 4)]; - - hSCurve hcb = curve.AddAndAssignId(&sc); - - STrimBy stb; - stb = STrimBy::EntireCurve(this, hcb, true); - (surface.FindById(sc.surfA))->trim.Add(&stb); - stb = STrimBy::EntireCurve(this, hcb, false); - (surface.FindById(sc.surfB))->trim.Add(&stb); - } - - // And if this input curve and the one after it both generated - // surfaces, then trim both of those by the appropriate - // circle. - if(revs.d[j].v && revsp.d[j].v) { - SSurface *ss = surface.FindById(revs.d[j]); - - ZERO(&sc); - sc.isExact = true; - sc.exact = SBezier::From(ss->ctrl[0][0], - ss->ctrl[0][1], - ss->ctrl[0][2]); - sc.exact.weight[1] = ss->weight[0][1]; - (sc.exact).MakePwlInto(&(sc.pts)); - sc.surfA = revs.d[j]; - sc.surfB = revsp.d[j]; - - hSCurve hcc = curve.AddAndAssignId(&sc); - - STrimBy stb; - stb = STrimBy::EntireCurve(this, hcc, false); - (surface.FindById(sc.surfA))->trim.Add(&stb); - stb = STrimBy::EntireCurve(this, hcc, true); - (surface.FindById(sc.surfB))->trim.Add(&stb); - } - } - } - - hsl.Clear(); - } - - for(i = i0; i < surface.n; i++) { - SSurface *srf = &(surface.elem[i]); - - // Revolution of a line; this is potentially a plane, which we can - // rewrite to have degree (1, 1). - if(srf->degm == 1 && srf->degn == 2) { - // close start, far start, far finish - Vector cs, fs, ff; - double d0, d1; - d0 = (srf->ctrl[0][0]).DistanceToLine(pt, axis); - d1 = (srf->ctrl[1][0]).DistanceToLine(pt, axis); - - if(d0 > d1) { - cs = srf->ctrl[1][0]; - fs = srf->ctrl[0][0]; - ff = srf->ctrl[0][2]; - } else { - cs = srf->ctrl[0][0]; - fs = srf->ctrl[1][0]; - ff = srf->ctrl[1][2]; - } - - // origin close, origin far - Vector oc = cs.ClosestPointOnLine(pt, axis), - of = fs.ClosestPointOnLine(pt, axis); - - if(oc.Equals(of)) { - // This is a plane, not a (non-degenerate) cone. - Vector oldn = srf->NormalAt(0.5, 0.5); - - Vector u = fs.Minus(of), v; - - v = (axis.Cross(u)).WithMagnitude(1); - - double vm = (ff.Minus(of)).Dot(v); - v = v.ScaledBy(vm); - - srf->degm = 1; - srf->degn = 1; - srf->ctrl[0][0] = of; - srf->ctrl[0][1] = of.Plus(u); - srf->ctrl[1][0] = of.Plus(v); - srf->ctrl[1][1] = of.Plus(u).Plus(v); - srf->weight[0][0] = 1; - srf->weight[0][1] = 1; - srf->weight[1][0] = 1; - srf->weight[1][1] = 1; - - if(oldn.Dot(srf->NormalAt(0.5, 0.5)) < 0) { - SWAP(Vector, srf->ctrl[0][0], srf->ctrl[1][0]); - SWAP(Vector, srf->ctrl[0][1], srf->ctrl[1][1]); - } - continue; - } - - if(fabs(d0 - d1) < LENGTH_EPS) { - // This is a cylinder; so transpose it so that we'll recognize - // it as a surface of extrusion. - SSurface sn = *srf; - - // Transposing u and v flips the normal, so reverse u to - // flip it again and put it back where we started. - sn.degm = 2; - sn.degn = 1; - int dm, dn; - for(dm = 0; dm <= 1; dm++) { - for(dn = 0; dn <= 2; dn++) { - sn.ctrl [dn][dm] = srf->ctrl [1-dm][dn]; - sn.weight[dn][dm] = srf->weight[1-dm][dn]; - } - } - - *srf = sn; - continue; - } - } - - } - -} - -void SShell::MakeFromCopyOf(SShell *a) { - MakeFromTransformationOf(a, - Vector::From(0, 0, 0), Quaternion::IDENTITY, 1.0); -} - -void SShell::MakeFromTransformationOf(SShell *a, - Vector t, Quaternion q, double scale) -{ - booleanFailed = false; - - SSurface *s; - for(s = a->surface.First(); s; s = a->surface.NextAfter(s)) { - SSurface n; - n = SSurface::FromTransformationOf(s, t, q, scale, true); - surface.Add(&n); // keeping the old ID - } - - SCurve *c; - for(c = a->curve.First(); c; c = a->curve.NextAfter(c)) { - SCurve n; - n = SCurve::FromTransformationOf(c, t, q, scale); - curve.Add(&n); // keeping the old ID - } -} - -void SShell::MakeEdgesInto(SEdgeList *sel) { - SSurface *s; - for(s = surface.First(); s; s = surface.NextAfter(s)) { - s->MakeEdgesInto(this, sel, SSurface::AS_XYZ); - } -} - -void SShell::MakeSectionEdgesInto(Vector n, double d, - SEdgeList *sel, SBezierList *sbl) -{ - SSurface *s; - for(s = surface.First(); s; s = surface.NextAfter(s)) { - if(s->CoincidentWithPlane(n, d)) { - s->MakeSectionEdgesInto(this, sel, sbl); - } - } -} - -void SShell::TriangulateInto(SMesh *sm) { - SSurface *s; - for(s = surface.First(); s; s = surface.NextAfter(s)) { - s->TriangulateInto(this, sm); - } -} - -bool SShell::IsEmpty(void) { - return (surface.n == 0); -} - -void SShell::Clear(void) { - SSurface *s; - for(s = surface.First(); s; s = surface.NextAfter(s)) { - s->Clear(); - } - surface.Clear(); - - SCurve *c; - for(c = curve.First(); c; c = curve.NextAfter(c)) { - c->Clear(); - } - curve.Clear(); -} - diff --git a/srf/surfinter.cpp b/srf/surfinter.cpp deleted file mode 100644 index 171b7ec..0000000 --- a/srf/surfinter.cpp +++ /dev/null @@ -1,533 +0,0 @@ -//----------------------------------------------------------------------------- -// How to intersect two surfaces, to get some type of curve. This is either -// an exact special case (e.g., two planes to make a line), or a numerical -// thing. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -extern int FLAG; - -void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB, - SShell *agnstA, SShell *agnstB, SShell *into) -{ - SCurve sc; - ZERO(&sc); - // Important to keep the order of (surfA, surfB) consistent; when we later - // rewrite the identifiers, we rewrite surfA from A and surfB from B. - sc.surfA = h; - sc.surfB = srfB->h; - sc.exact = *sb; - sc.isExact = true; - - // Now we have to piecewise linearize the curve. If there's already an - // identical curve in the shell, then follow that pwl exactly, otherwise - // calculate from scratch. - SCurve split, *existing = NULL, *se; - SBezier sbrev = *sb; - sbrev.Reverse(); - bool backwards = false; - for(se = into->curve.First(); se; se = into->curve.NextAfter(se)) { - if(se->isExact) { - if(sb->Equals(&(se->exact))) { - existing = se; - break; - } - if(sbrev.Equals(&(se->exact))) { - existing = se; - backwards = true; - break; - } - } - } - if(existing) { - SCurvePt *v; - for(v = existing->pts.First(); v; v = existing->pts.NextAfter(v)) { - sc.pts.Add(v); - } - if(backwards) sc.pts.Reverse(); - split = sc; - ZERO(&sc); - } else { - sb->MakePwlInto(&(sc.pts)); - // and split the line where it intersects our existing surfaces - split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, srfB); - sc.Clear(); - } - - // Test if the curve lies entirely outside one of the - SCurvePt *scpt; - bool withinA = false, withinB = false; - for(scpt = split.pts.First(); scpt; scpt = split.pts.NextAfter(scpt)) { - double tol = 0.01; - Point2d puv; - ClosestPointTo(scpt->p, &puv); - if(puv.x > -tol && puv.x < 1 + tol && - puv.y > -tol && puv.y < 1 + tol) - { - withinA = true; - } - srfB->ClosestPointTo(scpt->p, &puv); - if(puv.x > -tol && puv.x < 1 + tol && - puv.y > -tol && puv.y < 1 + tol) - { - withinB = true; - } - // Break out early, no sense wasting time if we already have the answer. - if(withinA && withinB) break; - } - if(!(withinA && withinB)) { - // Intersection curve lies entirely outside one of the surfaces, so - // it's fake. - split.Clear(); - return; - } - - if(sb->deg == 2 && 0) { - dbp(" "); - SCurvePt *prev = NULL, *v; - dbp("split.pts.n = %d", split.pts.n); - for(v = split.pts.First(); v; v = split.pts.NextAfter(v)) { - if(prev) { - Vector e = (prev->p).Minus(v->p).WithMagnitude(0); - SS.nakedEdges.AddEdge((prev->p).Plus(e), (v->p).Minus(e)); - } - prev = v; - } - } - // Nothing should be generating zero-len edges. - if((sb->Start()).Equals(sb->Finish())) oops(); - - split.source = SCurve::FROM_INTERSECTION; - into->curve.AddAndAssignId(&split); -} - -void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, - SShell *into) -{ - Vector amax, amin, bmax, bmin; - GetAxisAlignedBounding(&amax, &amin); - b->GetAxisAlignedBounding(&bmax, &bmin); - - if(Vector::BoundingBoxesDisjoint(amax, amin, bmax, bmin)) { - // They cannot possibly intersect, no curves to generate - return; - } - - Vector alongt, alongb; - SBezier oft, ofb; - bool isExtdt = this->IsExtrusion(&oft, &alongt), - isExtdb = b->IsExtrusion(&ofb, &alongb); - - if(degm == 1 && degn == 1 && b->degm == 1 && b->degn == 1) { - // Line-line intersection; it's a plane or nothing. - Vector na = NormalAt(0, 0).WithMagnitude(1), - nb = b->NormalAt(0, 0).WithMagnitude(1); - double da = na.Dot(PointAt(0, 0)), - db = nb.Dot(b->PointAt(0, 0)); - - Vector dl = na.Cross(nb); - if(dl.Magnitude() < LENGTH_EPS) return; // parallel planes - dl = dl.WithMagnitude(1); - Vector p = Vector::AtIntersectionOfPlanes(na, da, nb, db); - - // Trim it to the region 0 <= {u,v} <= 1 for each plane; not strictly - // necessary, since line will be split and excess edges culled, but - // this improves speed and robustness. - int i; - double tmax = VERY_POSITIVE, tmin = VERY_NEGATIVE; - for(i = 0; i < 2; i++) { - SSurface *s = (i == 0) ? this : b; - Vector tu, tv; - s->TangentsAt(0, 0, &tu, &tv); - - double up, vp, ud, vd; - s->ClosestPointTo(p, &up, &vp); - ud = (dl.Dot(tu)) / tu.MagSquared(); - vd = (dl.Dot(tv)) / tv.MagSquared(); - - // so u = up + t*ud - // v = vp + t*vd - if(ud > LENGTH_EPS) { - tmin = max(tmin, -up/ud); - tmax = min(tmax, (1 - up)/ud); - } else if(ud < -LENGTH_EPS) { - tmax = min(tmax, -up/ud); - tmin = max(tmin, (1 - up)/ud); - } else { - if(up < -LENGTH_EPS || up > 1 + LENGTH_EPS) { - // u is constant, and outside [0, 1] - tmax = VERY_NEGATIVE; - } - } - if(vd > LENGTH_EPS) { - tmin = max(tmin, -vp/vd); - tmax = min(tmax, (1 - vp)/vd); - } else if(vd < -LENGTH_EPS) { - tmax = min(tmax, -vp/vd); - tmin = max(tmin, (1 - vp)/vd); - } else { - if(vp < -LENGTH_EPS || vp > 1 + LENGTH_EPS) { - // v is constant, and outside [0, 1] - tmax = VERY_NEGATIVE; - } - } - } - - if(tmax > tmin + LENGTH_EPS) { - SBezier bezier = SBezier::From(p.Plus(dl.ScaledBy(tmin)), - p.Plus(dl.ScaledBy(tmax))); - AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into); - } - } else if((degm == 1 && degn == 1 && isExtdb) || - (b->degm == 1 && b->degn == 1 && isExtdt)) - { - // The intersection between a plane and a surface of extrusion - SSurface *splane, *sext; - if(degm == 1 && degn == 1) { - splane = this; - sext = b; - } else { - splane = b; - sext = this; - } - - Vector n = splane->NormalAt(0, 0).WithMagnitude(1), along; - double d = n.Dot(splane->PointAt(0, 0)); - SBezier bezier; - (void)sext->IsExtrusion(&bezier, &along); - - if(fabs(n.Dot(along)) < LENGTH_EPS) { - // Direction of extrusion is parallel to plane; so intersection - // is zero or more lines. Build a line within the plane, and - // normal to the direction of extrusion, and intersect that line - // against the surface; each intersection point corresponds to - // a line. - Vector pm, alu, p0, dp; - // a point halfway along the extrusion - pm = ((sext->ctrl[0][0]).Plus(sext->ctrl[0][1])).ScaledBy(0.5); - alu = along.WithMagnitude(1); - dp = (n.Cross(along)).WithMagnitude(1); - // n, alu, and dp form an orthogonal csys; set n component to - // place it on the plane, alu component to lie halfway along - // extrusion, and dp component doesn't matter so zero - p0 = n.ScaledBy(d).Plus(alu.ScaledBy(pm.Dot(alu))); - - List inters; - ZERO(&inters); - sext->AllPointsIntersecting( - p0, p0.Plus(dp), &inters, false, false, true); - - SInter *si; - for(si = inters.First(); si; si = inters.NextAfter(si)) { - Vector al = along.ScaledBy(0.5); - SBezier bezier; - bezier = SBezier::From((si->p).Minus(al), (si->p).Plus(al)); - AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into); - } - - inters.Clear(); - } else { - // Direction of extrusion is not parallel to plane; so - // intersection is projection of extruded curve into our plane. - int i; - for(i = 0; i <= bezier.deg; i++) { - Vector p0 = bezier.ctrl[i], - p1 = p0.Plus(along); - - bezier.ctrl[i] = - Vector::AtIntersectionOfPlaneAndLine(n, d, p0, p1, NULL); - } - - AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into); - } - } else if(isExtdt && isExtdb && - sqrt(fabs(alongt.Dot(alongb))) > - sqrt(alongt.Magnitude() * alongb.Magnitude()) - LENGTH_EPS) - { - // Two surfaces of extrusion along the same axis. So they might - // intersect along some number of lines parallel to the axis. - Vector axis = alongt.WithMagnitude(1); - - List inters; - ZERO(&inters); - List lv; - ZERO(&lv); - - double a_axis0 = ( ctrl[0][0]).Dot(axis), - a_axis1 = ( ctrl[0][1]).Dot(axis), - b_axis0 = (b->ctrl[0][0]).Dot(axis), - b_axis1 = (b->ctrl[0][1]).Dot(axis); - - if(a_axis0 > a_axis1) SWAP(double, a_axis0, a_axis1); - if(b_axis0 > b_axis1) SWAP(double, b_axis0, b_axis1); - - double ab_axis0 = max(a_axis0, b_axis0), - ab_axis1 = min(a_axis1, b_axis1); - - if(fabs(ab_axis0 - ab_axis1) < LENGTH_EPS) { - // The line would be zero-length - return; - } - - Vector axis0 = axis.ScaledBy(ab_axis0), - axis1 = axis.ScaledBy(ab_axis1), - axisc = (axis0.Plus(axis1)).ScaledBy(0.5); - - oft.MakePwlInto(&lv); - - int i; - for(i = 0; i < lv.n - 1; i++) { - Vector pa = lv.elem[i], pb = lv.elem[i+1]; - pa = pa.Minus(axis.ScaledBy(pa.Dot(axis))); - pb = pb.Minus(axis.ScaledBy(pb.Dot(axis))); - pa = pa.Plus(axisc); - pb = pb.Plus(axisc); - - b->AllPointsIntersecting(pa, pb, &inters, true, false, false); - } - - SInter *si; - for(si = inters.First(); si; si = inters.NextAfter(si)) { - Vector p = (si->p).Minus(axis.ScaledBy((si->p).Dot(axis))); - double ub, vb; - b->ClosestPointTo(p, &ub, &vb, true); - SSurface plane; - plane = SSurface::FromPlane(p, axis.Normal(0), axis.Normal(1)); - - b->PointOnSurfaces(this, &plane, &ub, &vb); - - p = b->PointAt(ub, vb); - - SBezier bezier; - bezier = SBezier::From(p.Plus(axis0), p.Plus(axis1)); - AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into); - } - - inters.Clear(); - lv.Clear(); - } else { - // Try intersecting the surfaces numerically, by a marching algorithm. - // First, we find all the intersections between a surface and the - // boundary of the other surface. - SPointList spl; - ZERO(&spl); - int a; - for(a = 0; a < 2; a++) { - SShell *shA = (a == 0) ? agnstA : agnstB, - *shB = (a == 0) ? agnstB : agnstA; - SSurface *srfA = (a == 0) ? this : b, - *srfB = (a == 0) ? b : this; - - SEdgeList el; - ZERO(&el); - srfA->MakeEdgesInto(shA, &el, AS_XYZ, NULL); - - SEdge *se; - for(se = el.l.First(); se; se = el.l.NextAfter(se)) { - List lsi; - ZERO(&lsi); - - srfB->AllPointsIntersecting(se->a, se->b, &lsi, - true, true, false); - if(lsi.n == 0) continue; - - // Find the other surface that this curve trims. - hSCurve hsc = { se->auxA }; - SCurve *sc = shA->curve.FindById(hsc); - hSSurface hother = (sc->surfA.v == srfA->h.v) ? - sc->surfB : sc->surfA; - SSurface *other = shA->surface.FindById(hother); - - SInter *si; - for(si = lsi.First(); si; si = lsi.NextAfter(si)) { - Vector p = si->p; - double u, v; - srfB->ClosestPointTo(p, &u, &v); - srfB->PointOnSurfaces(srfA, other, &u, &v); - p = srfB->PointAt(u, v); - if(!spl.ContainsPoint(p)) { - SPoint sp; - sp.p = p; - // We also need the edge normal, so that we know in - // which direction to march. - srfA->ClosestPointTo(p, &u, &v); - Vector n = srfA->NormalAt(u, v); - sp.auxv = n.Cross((se->b).Minus(se->a)); - sp.auxv = (sp.auxv).WithMagnitude(1); - - spl.l.Add(&sp); - } - } - lsi.Clear(); - } - - el.Clear(); - } - - while(spl.l.n >= 2) { - SCurve sc; - ZERO(&sc); - sc.surfA = h; - sc.surfB = b->h; - sc.isExact = false; - sc.source = SCurve::FROM_INTERSECTION; - - Vector start = spl.l.elem[0].p, - startv = spl.l.elem[0].auxv; - spl.l.ClearTags(); - spl.l.elem[0].tag = 1; - spl.l.RemoveTagged(); - - // Our chord tolerance is whatever the user specified - double maxtol = SS.ChordTolMm(); - int maxsteps = max(300, SS.maxSegments*3); - - // The curve starts at our starting point. - SCurvePt padd; - ZERO(&padd); - padd.vertex = true; - padd.p = start; - sc.pts.Add(&padd); - - Point2d pa, pb; - Vector np, npc; - bool fwd; - // Better to start with a too-small step, so that we don't miss - // features of the curve entirely. - double tol, step = maxtol; - for(a = 0; a < maxsteps; a++) { - ClosestPointTo(start, &pa); - b->ClosestPointTo(start, &pb); - - Vector na = NormalAt(pa).WithMagnitude(1), - nb = b->NormalAt(pb).WithMagnitude(1); - - if(a == 0) { - Vector dp = nb.Cross(na); - if(dp.Dot(startv) < 0) { - // We want to march in the more inward direction. - fwd = true; - } else { - fwd = false; - } - } - - int i; - for(i = 0; i < 20; i++) { - Vector dp = nb.Cross(na); - if(!fwd) dp = dp.ScaledBy(-1); - dp = dp.WithMagnitude(step); - - np = start.Plus(dp); - npc = ClosestPointOnThisAndSurface(b, np); - tol = (npc.Minus(np)).Magnitude(); - - if(tol > maxtol*0.8) { - step *= 0.90; - } else { - step /= 0.90; - } - - if((tol < maxtol) && (tol > maxtol/2)) { - // If we meet the chord tolerance test, and we're - // not too fine, then we break out. - break; - } - } - - SPoint *sp; - for(sp = spl.l.First(); sp; sp = spl.l.NextAfter(sp)) { - if((sp->p).OnLineSegment(start, npc, 2*SS.ChordTolMm())) { - sp->tag = 1; - a = maxsteps; - npc = sp->p; - } - } - - padd.p = npc; - padd.vertex = (a == maxsteps); - sc.pts.Add(&padd); - - start = npc; - } - - spl.l.RemoveTagged(); - - // And now we split and insert the curve - SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, b); - sc.Clear(); - into->curve.AddAndAssignId(&split); - } - spl.Clear(); - } -} - -//----------------------------------------------------------------------------- -// Are two surfaces coincident, with the same (or with opposite) normals? -// Currently handles planes only. -//----------------------------------------------------------------------------- -bool SSurface::CoincidentWith(SSurface *ss, bool sameNormal) { - if(degm != 1 || degn != 1) return false; - if(ss->degm != 1 || ss->degn != 1) return false; - - Vector p = ctrl[0][0]; - Vector n = NormalAt(0, 0).WithMagnitude(1); - double d = n.Dot(p); - - if(!ss->CoincidentWithPlane(n, d)) return false; - - Vector n2 = ss->NormalAt(0, 0); - if(sameNormal) { - if(n2.Dot(n) < 0) return false; - } else { - if(n2.Dot(n) > 0) return false; - } - - return true; -} - -bool SSurface::CoincidentWithPlane(Vector n, double d) { - if(degm != 1 || degn != 1) return false; - if(fabs(n.Dot(ctrl[0][0]) - d) > LENGTH_EPS) return false; - if(fabs(n.Dot(ctrl[0][1]) - d) > LENGTH_EPS) return false; - if(fabs(n.Dot(ctrl[1][0]) - d) > LENGTH_EPS) return false; - if(fabs(n.Dot(ctrl[1][1]) - d) > LENGTH_EPS) return false; - - return true; -} - -//----------------------------------------------------------------------------- -// In our shell, find all surfaces that are coincident with the prototype -// surface (with same or opposite normal, as specified), and copy all of -// their trim polygons into el. The edges are returned in uv coordinates for -// the prototype surface. -//----------------------------------------------------------------------------- -void SShell::MakeCoincidentEdgesInto(SSurface *proto, bool sameNormal, - SEdgeList *el, SShell *useCurvesFrom) -{ - SSurface *ss; - for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { - if(proto->CoincidentWith(ss, sameNormal)) { - ss->MakeEdgesInto(this, el, SSurface::AS_XYZ, useCurvesFrom); - } - } - - SEdge *se; - for(se = el->l.First(); se; se = el->l.NextAfter(se)) { - double ua, va, ub, vb; - proto->ClosestPointTo(se->a, &ua, &va); - proto->ClosestPointTo(se->b, &ub, &vb); - - if(sameNormal) { - se->a = Vector::From(ua, va, 0); - se->b = Vector::From(ub, vb, 0); - } else { - // Flip normal, so flip all edge directions - se->b = Vector::From(ua, va, 0); - se->a = Vector::From(ub, vb, 0); - } - } -} - diff --git a/srf/triangulate.cpp b/srf/triangulate.cpp deleted file mode 100644 index 30e5881..0000000 --- a/srf/triangulate.cpp +++ /dev/null @@ -1,515 +0,0 @@ -//----------------------------------------------------------------------------- -// Triangulate a surface. If the surface is curved, then we first superimpose -// a grid of quads, with spacing to achieve our chord tolerance. We then -// proceed by ear-clipping; the resulting mesh should be watertight and not -// awful numerically, but has no special properties (Delaunay, etc.). -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "../solvespace.h" - -void SPolygon::UvTriangulateInto(SMesh *m, SSurface *srf) { - if(l.n <= 0) return; - - SDWORD in = GetMilliseconds(); - - normal = Vector::From(0, 0, 1); - - while(l.n > 0) { - FixContourDirections(); - l.ClearTags(); - - // Find a top-level contour, and start with that. Then build bridges - // in order to merge all its islands into a single contour. - SContour *top; - for(top = l.First(); top; top = l.NextAfter(top)) { - if(top->timesEnclosed == 0) { - break; - } - } - if(!top) { - dbp("polygon has no top-level contours?"); - return; - } - - // Start with the outer contour - SContour merged; - ZERO(&merged); - top->tag = 1; - top->CopyInto(&merged); - (merged.l.n)--; - - // List all of the edges, for testing whether bridges work. - SEdgeList el; - ZERO(&el); - top->MakeEdgesInto(&el); - List vl; - ZERO(&vl); - - // And now find all of its holes. Note that we will also find any - // outer contours that lie entirely within this contour, and any - // holes for those contours. But that's okay, because we can merge - // those too. - SContour *sc; - for(sc = l.First(); sc; sc = l.NextAfter(sc)) { - if(sc->timesEnclosed != 1) continue; - if(sc->l.n < 2) continue; - - // Test the midpoint of an edge. Our polygon may not be self- - // intersecting, but two contours may share a vertex; so a - // vertex could be on the edge of another polygon, in which - // case ContainsPointProjdToNormal returns indeterminate. - Vector tp = sc->AnyEdgeMidpoint(); - if(top->ContainsPointProjdToNormal(normal, tp)) { - sc->tag = 2; - sc->MakeEdgesInto(&el); - sc->FindPointWithMinX(); - } - } - -// dbp("finished finding holes: %d ms", GetMilliseconds() - in); - for(;;) { - double xmin = 1e10; - SContour *scmin = NULL; - - for(sc = l.First(); sc; sc = l.NextAfter(sc)) { - if(sc->tag != 2) continue; - - if(sc->xminPt.x < xmin) { - xmin = sc->xminPt.x; - scmin = sc; - } - } - if(!scmin) break; - - if(!merged.BridgeToContour(scmin, &el, &vl)) { - dbp("couldn't merge our hole"); - return; - } -// dbp(" bridged to contour: %d ms", GetMilliseconds() - in); - scmin->tag = 3; - } -// dbp("finished merging holes: %d ms", GetMilliseconds() - in); - - merged.UvTriangulateInto(m, srf); -// dbp("finished ear clippping: %d ms", GetMilliseconds() - in); - merged.l.Clear(); - el.Clear(); - vl.Clear(); - - // Careful, need to free the points within the contours, and not just - // the contours themselves. This was a tricky memory leak. - for(sc = l.First(); sc; sc = l.NextAfter(sc)) { - if(sc->tag) { - sc->l.Clear(); - } - } - l.RemoveTagged(); - } -} - -bool SContour::BridgeToContour(SContour *sc, - SEdgeList *avoidEdges, List *avoidPts) -{ - int i, j; - - // Start looking for a bridge on our new hole near its leftmost (min x) - // point. - int sco = 0; - for(i = 0; i < (sc->l.n - 1); i++) { - if((sc->l.elem[i].p).EqualsExactly(sc->xminPt)) { - sco = i; - } - } - - // And start looking on our merged contour at whichever point is nearest - // to the leftmost point of the new segment. - int thiso = 0; - double dmin = 1e10; - for(i = 0; i < l.n; i++) { - Vector p = l.elem[i].p; - double d = (p.Minus(sc->xminPt)).MagSquared(); - if(d < dmin) { - dmin = d; - thiso = i; - } - } - - int thisp, scp; - - Vector a, b, *f; - - // First check if the contours share a point; in that case we should - // merge them there, without a bridge. - for(i = 0; i < l.n; i++) { - thisp = WRAP(i+thiso, l.n); - a = l.elem[thisp].p; - - for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) { - if(f->Equals(a)) break; - } - if(f) continue; - - for(j = 0; j < (sc->l.n - 1); j++) { - scp = WRAP(j+sco, (sc->l.n - 1)); - b = sc->l.elem[scp].p; - - if(a.Equals(b)) { - goto haveEdge; - } - } - } - - // If that fails, look for a bridge that does not intersect any edges. - for(i = 0; i < l.n; i++) { - thisp = WRAP(i+thiso, l.n); - a = l.elem[thisp].p; - - for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) { - if(f->Equals(a)) break; - } - if(f) continue; - - for(j = 0; j < (sc->l.n - 1); j++) { - scp = WRAP(j+sco, (sc->l.n - 1)); - b = sc->l.elem[scp].p; - - for(f = avoidPts->First(); f; f = avoidPts->NextAfter(f)) { - if(f->Equals(b)) break; - } - if(f) continue; - - if(avoidEdges->AnyEdgeCrossings(a, b) > 0) { - // doesn't work, bridge crosses an existing edge - } else { - goto haveEdge; - } - } - } - - // Tried all the possibilities, didn't find an edge - return false; - -haveEdge: - SContour merged; - ZERO(&merged); - for(i = 0; i < l.n; i++) { - merged.AddPoint(l.elem[i].p); - if(i == thisp) { - // less than or equal; need to duplicate the join point - for(j = 0; j <= (sc->l.n - 1); j++) { - int jp = WRAP(j + scp, (sc->l.n - 1)); - merged.AddPoint((sc->l.elem[jp]).p); - } - // and likewise duplicate join point for the outer curve - merged.AddPoint(l.elem[i].p); - } - } - - // and future bridges mustn't cross our bridge, and it's tricky to get - // things right if two bridges come from the same point - avoidEdges->AddEdge(a, b); - avoidPts->Add(&a); - avoidPts->Add(&b); - - l.Clear(); - l = merged.l; - return true; -} - -bool SContour::IsEar(int bp, double scaledEps) { - int ap = WRAP(bp-1, l.n), - cp = WRAP(bp+1, l.n); - - STriangle tr; - ZERO(&tr); - tr.a = l.elem[ap].p; - tr.b = l.elem[bp].p; - tr.c = l.elem[cp].p; - - if((tr.a).Equals(tr.c)) { - // This is two coincident and anti-parallel edges. Zero-area, so - // won't generate a real triangle, but we certainly can clip it. - return true; - } - - Vector n = Vector::From(0, 0, -1); - if((tr.Normal()).Dot(n) < scaledEps) { - // This vertex is reflex, or between two collinear edges; either way, - // it's not an ear. - return false; - } - - // Accelerate with an axis-aligned bounding box test - Vector maxv = tr.a, minv = tr.a; - (tr.b).MakeMaxMin(&maxv, &minv); - (tr.c).MakeMaxMin(&maxv, &minv); - - int i; - for(i = 0; i < l.n; i++) { - if(i == ap || i == bp || i == cp) continue; - - Vector p = l.elem[i].p; - if(p.OutsideAndNotOn(maxv, minv)) continue; - - // A point on the edge of the triangle is considered to be inside, - // and therefore makes it a non-ear; but a point on the vertex is - // "outside", since that's necessary to make bridges work. - if(p.EqualsExactly(tr.a)) continue; - if(p.EqualsExactly(tr.b)) continue; - if(p.EqualsExactly(tr.c)) continue; - - if(tr.ContainsPointProjd(n, p)) { - return false; - } - } - return true; -} - -void SContour::ClipEarInto(SMesh *m, int bp, double scaledEps) { - int ap = WRAP(bp-1, l.n), - cp = WRAP(bp+1, l.n); - - STriangle tr; - ZERO(&tr); - tr.a = l.elem[ap].p; - tr.b = l.elem[bp].p; - tr.c = l.elem[cp].p; - if(tr.Normal().MagSquared() < scaledEps*scaledEps) { - // A vertex with more than two edges will cause us to generate - // zero-area triangles, which must be culled. - } else { - m->AddTriangle(&tr); - } - - // By deleting the point at bp, we may change the ear-ness of the points - // on either side. - l.elem[ap].ear = SPoint::UNKNOWN; - l.elem[cp].ear = SPoint::UNKNOWN; - - l.ClearTags(); - l.elem[bp].tag = 1; - l.RemoveTagged(); -} - -void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) { - Vector tu, tv; - srf->TangentsAt(0.5, 0.5, &tu, &tv); - double s = sqrt(tu.MagSquared() + tv.MagSquared()); - // We would like to apply our tolerances in xyz; but that would be a lot - // of work, so at least scale the epsilon semi-reasonably. That's - // perfect for square planes, less perfect for anything else. - double scaledEps = LENGTH_EPS / s; - - int i; - // Clean the original contour by removing any zero-length edges. - l.ClearTags(); - for(i = 1; i < l.n; i++) { - if((l.elem[i].p).Equals(l.elem[i-1].p)) { - l.elem[i].tag = 1; - } - } - l.RemoveTagged(); - - // Now calculate the ear-ness of each vertex - for(i = 0; i < l.n; i++) { - (l.elem[i]).ear = IsEar(i, scaledEps) ? SPoint::EAR : SPoint::NOT_EAR; - } - - bool toggle = false; - while(l.n > 3) { - // Some points may have changed ear-ness, so recalculate - for(i = 0; i < l.n; i++) { - if(l.elem[i].ear == SPoint::UNKNOWN) { - (l.elem[i]).ear = IsEar(i, scaledEps) ? - SPoint::EAR : SPoint::NOT_EAR; - } - } - - int bestEar = -1; - double bestChordTol = VERY_POSITIVE; - // Alternate the starting position so we generate strip-like - // triangulations instead of fan-like - toggle = !toggle; - int offset = toggle ? -1 : 0; - for(i = 0; i < l.n; i++) { - int ear = WRAP(i+offset, l.n); - if(l.elem[ear].ear == SPoint::EAR) { - if(srf->degm == 1 && srf->degn == 1) { - // This is a plane; any ear is a good ear. - bestEar = ear; - break; - } - // If we are triangulating a curved surface, then try to - // clip ears that have a small chord tolerance from the - // surface. - Vector prev = l.elem[WRAP((i+offset-1), l.n)].p, - next = l.elem[WRAP((i+offset+1), l.n)].p; - double tol = srf->ChordToleranceForEdge(prev, next); - if(tol < bestChordTol - scaledEps) { - bestEar = ear; - bestChordTol = tol; - } - if(bestChordTol < 0.1*SS.ChordTolMm()) { - break; - } - } - } - if(bestEar < 0) { - dbp("couldn't find an ear! fail"); - return; - } - ClipEarInto(m, bestEar, scaledEps); - } - - ClipEarInto(m, 0, scaledEps); // add the last triangle -} - -double SSurface::ChordToleranceForEdge(Vector a, Vector b) { - Vector as = PointAt(a.x, a.y), bs = PointAt(b.x, b.y); - - double worst = VERY_NEGATIVE; - int i; - for(i = 1; i <= 3; i++) { - Vector p = a. Plus((b. Minus(a )).ScaledBy(i/4.0)), - ps = as.Plus((bs.Minus(as)).ScaledBy(i/4.0)); - - Vector pps = PointAt(p.x, p.y); - worst = max(worst, (pps.Minus(ps)).MagSquared()); - } - return sqrt(worst); -} - -Vector SSurface::PointAtMaybeSwapped(double u, double v, bool swapped) { - if(swapped) { - return PointAt(v, u); - } else { - return PointAt(u, v); - } -} - -void SSurface::MakeTriangulationGridInto(List *l, double vs, double vf, - bool swapped) -{ - double worst = 0; - - // Try piecewise linearizing four curves, at u = 0, 1/3, 2/3, 1; choose - // the worst chord tolerance of any of those. - int i; - for(i = 0; i <= 3; i++) { - double u = i/3.0; - - // This chord test should be identical to the one in SBezier::MakePwl - // to make the piecewise linear edges line up with the grid more or - // less. - Vector ps = PointAtMaybeSwapped(u, vs, swapped), - pf = PointAtMaybeSwapped(u, vf, swapped); - - double vm1 = (2*vs + vf) / 3, - vm2 = (vs + 2*vf) / 3; - - Vector pm1 = PointAtMaybeSwapped(u, vm1, swapped), - pm2 = PointAtMaybeSwapped(u, vm2, swapped); - - worst = max(worst, pm1.DistanceToLine(ps, pf.Minus(ps))); - worst = max(worst, pm2.DistanceToLine(ps, pf.Minus(ps))); - } - - double step = 1.0/SS.maxSegments; - if((vf - vs) < step || worst < SS.ChordTolMm()) { - l->Add(&vf); - } else { - MakeTriangulationGridInto(l, vs, (vs+vf)/2, swapped); - MakeTriangulationGridInto(l, (vs+vf)/2, vf, swapped); - } -} - -void SPolygon::UvGridTriangulateInto(SMesh *mesh, SSurface *srf) { - SEdgeList orig; - ZERO(&orig); - MakeEdgesInto(&orig); - - SEdgeList holes; - ZERO(&holes); - - normal = Vector::From(0, 0, 1); - FixContourDirections(); - - // Build a rectangular grid, with horizontal and vertical lines in the - // uv plane. The spacing of these lines is adaptive, so calculate that. - List li, lj; - ZERO(&li); - ZERO(&lj); - double v = 0; - li.Add(&v); - srf->MakeTriangulationGridInto(&li, 0, 1, true); - lj.Add(&v); - srf->MakeTriangulationGridInto(&lj, 0, 1, false); - - // Now iterate over each quad in the grid. If it's outside the polygon, - // or if it intersects the polygon, then we discard it. Otherwise we - // generate two triangles in the mesh, and cut it out of our polygon. - int i, j; - for(i = 0; i < (li.n - 1); i++) { - for(j = 0; j < (lj.n - 1); j++) { - double us = li.elem[i], uf = li.elem[i+1], - vs = lj.elem[j], vf = lj.elem[j+1]; - - Vector a = Vector::From(us, vs, 0), - b = Vector::From(us, vf, 0), - c = Vector::From(uf, vf, 0), - d = Vector::From(uf, vs, 0); - - if(orig.AnyEdgeCrossings(a, b, NULL) || - orig.AnyEdgeCrossings(b, c, NULL) || - orig.AnyEdgeCrossings(c, d, NULL) || - orig.AnyEdgeCrossings(d, a, NULL)) - { - continue; - } - - // There's no intersections, so it doesn't matter which point - // we decide to test. - if(!this->ContainsPoint(a)) { - continue; - } - - // Add the quad to our mesh - STriangle tr; - ZERO(&tr); - tr.a = a; - tr.b = b; - tr.c = c; - mesh->AddTriangle(&tr); - tr.a = a; - tr.b = c; - tr.c = d; - mesh->AddTriangle(&tr); - - holes.AddEdge(a, b); - holes.AddEdge(b, c); - holes.AddEdge(c, d); - holes.AddEdge(d, a); - } - } - - holes.CullExtraneousEdges(); - SPolygon hp; - ZERO(&hp); - holes.AssemblePolygon(&hp, NULL, true); - - SContour *sc; - for(sc = hp.l.First(); sc; sc = hp.l.NextAfter(sc)) { - l.Add(sc); - } - - orig.Clear(); - holes.Clear(); - li.Clear(); - lj.Clear(); - hp.l.Clear(); - - UvTriangulateInto(mesh, srf); -} - - diff --git a/style.cpp b/style.cpp deleted file mode 100644 index cb86cb0..0000000 --- a/style.cpp +++ /dev/null @@ -1,872 +0,0 @@ -//----------------------------------------------------------------------------- -// Implementation of a cosmetic line style, which determines the color and -// other appearance of a line or curve on-screen and in exported files. Some -// styles are predefined, and others can be created by the user. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" -#include - -const Style::Default Style::Defaults[] = { - { ACTIVE_GRP, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, }, - { CONSTRUCTION, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, }, - { INACTIVE_GRP, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, }, - { DATUM, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, }, - { SOLID_EDGE, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, }, - { CONSTRAINT, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, }, - { SELECTED, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, }, - { HOVERED, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, }, - { CONTOUR_FILL, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, }, - { NORMALS, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, }, - { ANALYZE, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, }, - { DRAW_ERROR, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, }, - { DIM_SOLID, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, }, - { 0, NULL, 0, 0.0, }, -}; - -char *Style::CnfColor(char *prefix) { - static char name[100]; - sprintf(name, "Style_%s_Color", prefix); - return name; -} -char *Style::CnfWidth(char *prefix) { - static char name[100]; - sprintf(name, "Style_%s_Width", prefix); - return name; -} - -char *Style::CnfPrefixToName(char *prefix) { - static char name[100]; - int i = 0, j; - strcpy(name, "#def-"); - j = 5; - while(prefix[i] && j < 90) { - if(isupper(prefix[i]) && i != 0) { - name[j++] = '-'; - } - name[j++] = tolower(prefix[i]); - i++; - } - name[j++] = '\0'; - return name; -} - -void Style::CreateAllDefaultStyles(void) { - const Default *d; - for(d = &(Defaults[0]); d->h.v; d++) { - (void)Get(d->h); - } -} - -void Style::CreateDefaultStyle(hStyle h) { - bool isDefaultStyle = true; - const Default *d; - for(d = &(Defaults[0]); d->h.v; d++) { - if(d->h.v == h.v) break; - } - if(!d->h.v) { - // Not a default style; so just create it the same as our default - // active group entity style. - d = &(Defaults[0]); - isDefaultStyle = false; - } - - Style ns; - ZERO(&ns); - ns.color = CnfThawDWORD(d->color, CnfColor(d->cnfPrefix)); - ns.width = CnfThawFloat((float)(d->width), CnfWidth(d->cnfPrefix)); - ns.widthAs = UNITS_AS_PIXELS; - ns.textHeight = DEFAULT_TEXT_HEIGHT; - ns.textHeightAs = UNITS_AS_PIXELS; - ns.textOrigin = 0; - ns.textAngle = 0; - ns.visible = true; - ns.exportable = true; - ns.filled = false; - ns.fillColor = RGBf(0.3, 0.3, 0.3); - ns.h = h; - if(isDefaultStyle) { - ns.name.strcpy(CnfPrefixToName(d->cnfPrefix)); - } else { - ns.name.strcpy("new-custom-style"); - } - - SK.style.Add(&ns); -} - -void Style::LoadFactoryDefaults(void) { - const Default *d; - for(d = &(Defaults[0]); d->h.v; d++) { - Style *s = Get(d->h); - - s->color = d->color; - s->width = d->width; - s->widthAs = UNITS_AS_PIXELS; - s->textHeight = DEFAULT_TEXT_HEIGHT; - s->textHeightAs = UNITS_AS_PIXELS; - s->textOrigin = 0; - s->textAngle = 0; - s->visible = true; - s->exportable = true; - s->filled = false; - s->fillColor = RGBf(0.3, 0.3, 0.3); - s->name.strcpy(CnfPrefixToName(d->cnfPrefix)); - } - SS.backgroundColor = RGB(0, 0, 0); - if(SS.bgImage.fromFile) MemFree(SS.bgImage.fromFile); - SS.bgImage.fromFile = NULL; -} - -void Style::FreezeDefaultStyles(void) { - const Default *d; - for(d = &(Defaults[0]); d->h.v; d++) { - CnfFreezeDWORD(Color(d->h), CnfColor(d->cnfPrefix)); - CnfFreezeFloat((float)Width(d->h), CnfWidth(d->cnfPrefix)); - } -} - -DWORD Style::CreateCustomStyle(void) { - SS.UndoRemember(); - DWORD vs = max(Style::FIRST_CUSTOM, SK.style.MaximumId() + 1); - hStyle hs = { vs }; - (void)Style::Get(hs); - return hs.v; -} - -void Style::AssignSelectionToStyle(DWORD v) { - bool showError = false; - SS.GW.GroupSelection(); - - SS.UndoRemember(); - int i; - for(i = 0; i < SS.GW.gs.entities; i++) { - hEntity he = SS.GW.gs.entity[i]; - if(!he.isFromRequest()) { - showError = true; - continue; - } - - hRequest hr = he.request(); - Request *r = SK.GetRequest(hr); - r->style.v = v; - SS.MarkGroupDirty(r->group); - } - for(i = 0; i < SS.GW.gs.constraints; i++) { - hConstraint hc = SS.GW.gs.constraint[i]; - Constraint *c = SK.GetConstraint(hc); - if(c->type != Constraint::COMMENT) continue; - - c->disp.style.v = v; - } - - if(showError) { - Error("Can't assign style to an entity that's derived from another " - "entity; try assigning a style to this entity's parent."); - } - - SS.GW.ClearSelection(); - InvalidateGraphics(); - SS.later.generateAll = true; - - // And show that style's info screen in the text window. - SS.TW.GoToScreen(TextWindow::SCREEN_STYLE_INFO); - SS.TW.shown.style.v = v; - SS.later.showTW = true; -} - -//----------------------------------------------------------------------------- -// Look up a style by its handle. If that style does not exist, then create -// the style, according to our table of default styles. -//----------------------------------------------------------------------------- -Style *Style::Get(hStyle h) { - if(h.v == 0) h.v = ACTIVE_GRP; - - Style *s = SK.style.FindByIdNoOops(h); - if(s) { - // It exists, good. - return s; - } else { - // It doesn't exist; so we should create it and then return that. - CreateDefaultStyle(h); - return SK.style.FindById(h); - } -} - -//----------------------------------------------------------------------------- -// A couple of wrappers, so that I can call these functions with either an -// hStyle or with the integer corresponding to that hStyle.v. -//----------------------------------------------------------------------------- -DWORD Style::Color(int s, bool forExport) { - hStyle hs = { s }; - return Color(hs, forExport); -} -float Style::Width(int s) { - hStyle hs = { s }; - return Width(hs); -} - -//----------------------------------------------------------------------------- -// If a color is almost white, then we can rewrite it to black, just so that -// it won't disappear on file formats with a light background. -//----------------------------------------------------------------------------- -DWORD Style::RewriteColor(DWORD rgbin) { - Vector rgb = Vector::From(REDf(rgbin), GREENf(rgbin), BLUEf(rgbin)); - rgb = rgb.Minus(Vector::From(1, 1, 1)); - if(rgb.Magnitude() < 0.4 && SS.fixExportColors) { - // This is an almost-white color in a default style, which is - // good for the default on-screen view (black bg) but probably - // not desired in the exported files, which typically are shown - // against white backgrounds. - return RGB(0, 0, 0); - } else { - return rgbin; - } -} - -//----------------------------------------------------------------------------- -// Return the stroke color associated with our style as 8-bit RGB. -//----------------------------------------------------------------------------- -DWORD Style::Color(hStyle h, bool forExport) { - Style *s = Get(h); - if(forExport) { - return RewriteColor(s->color); - } else { - return s->color; - } -} - -//----------------------------------------------------------------------------- -// Return the fill color associated with our style as 8-bit RGB. -//----------------------------------------------------------------------------- -DWORD Style::FillColor(hStyle h, bool forExport) { - Style *s = Get(h); - if(forExport) { - return RewriteColor(s->fillColor); - } else { - return s->fillColor; - } -} - -//----------------------------------------------------------------------------- -// Return the width associated with our style in pixels.. -//----------------------------------------------------------------------------- -float Style::Width(hStyle h) { - double r = 1.0; - Style *s = Get(h); - if(s->widthAs == UNITS_AS_MM) { - r = s->width * SS.GW.scale; - } else if(s->widthAs == UNITS_AS_PIXELS) { - r = s->width; - } - // This returns a float because glLineWidth expects a float, avoid casts. - return (float)r; -} - -//----------------------------------------------------------------------------- -// Return the width associated with our style in millimeters.. -//----------------------------------------------------------------------------- -double Style::WidthMm(int hs) { - double widthpx = Width(hs); - return widthpx / SS.GW.scale; -} - -//----------------------------------------------------------------------------- -// Return the associated text height, in pixels. -//----------------------------------------------------------------------------- -double Style::TextHeight(hStyle hs) { - Style *s = Get(hs); - if(s->textHeightAs == UNITS_AS_MM) { - return s->textHeight * SS.GW.scale; - } else if(s->textHeightAs == UNITS_AS_PIXELS) { - return s->textHeight; - } else { - return DEFAULT_TEXT_HEIGHT; - } -} - -//----------------------------------------------------------------------------- -// Should lines and curves from this style appear in the output file? Only -// if it's both shown and exportable. -//----------------------------------------------------------------------------- -bool Style::Exportable(int si) { - hStyle hs = { si }; - Style *s = Get(hs); - return (s->exportable) && (s->visible); -} - -//----------------------------------------------------------------------------- -// Return the appropriate style for our entity. If the entity has a style -// explicitly assigned, then it's that style. Otherwise it's the appropriate -// default style. -//----------------------------------------------------------------------------- -hStyle Style::ForEntity(hEntity he) { - Entity *e = SK.GetEntity(he); - // If the entity has a special style, use that. If that style doesn't - // exist yet, then it will get created automatically later. - if(e->style.v != 0) { - return e->style; - } - - // Otherwise, we use the default rules. - hStyle hs; - if(e->group.v != SS.GW.activeGroup.v) { - hs.v = INACTIVE_GRP; - } else if(e->construction) { - hs.v = CONSTRUCTION; - } else { - hs.v = ACTIVE_GRP; - } - return hs; -} - -char *Style::DescriptionString(void) { - static char ret[100]; - if(name.str[0]) { - sprintf(ret, "s%03x-%s", h.v, name.str); - } else { - sprintf(ret, "s%03x-(unnamed)", h.v); - } - return ret; -} - - -void TextWindow::ScreenShowListOfStyles(int link, DWORD v) { - SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); -} -void TextWindow::ScreenShowStyleInfo(int link, DWORD v) { - SS.TW.GoToScreen(SCREEN_STYLE_INFO); - SS.TW.shown.style.v = v; -} - -void TextWindow::ScreenLoadFactoryDefaultStyles(int link, DWORD v) { - Style::LoadFactoryDefaults(); - SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); -} - -void TextWindow::ScreenCreateCustomStyle(int link, DWORD v) { - Style::CreateCustomStyle(); -} - -void TextWindow::ScreenChangeBackgroundColor(int link, DWORD v) { - DWORD rgb = SS.backgroundColor; - SS.TW.ShowEditControlWithColorPicker(v, 3, rgb); - SS.TW.edit.meaning = EDIT_BACKGROUND_COLOR; -} - -static int RoundUpToPowerOfTwo(int v) -{ - int i; - for(i = 0; i < 31; i++) { - int vt = (1 << i); - if(vt >= v) { - return vt; - } - } - return 0; -} - -void TextWindow::ScreenBackgroundImage(int link, DWORD v) { - if(SS.bgImage.fromFile) MemFree(SS.bgImage.fromFile); - SS.bgImage.fromFile = NULL; - - if(link == 'l') { - FILE *f = NULL; - png_struct *png_ptr = NULL; - png_info *info_ptr = NULL; - - char importFile[MAX_PATH] = ""; - if(!GetOpenFile(importFile, PNG_EXT, PNG_PATTERN)) goto err; - f = fopen(importFile, "rb"); - if(!f) goto err; - - BYTE header[8]; - fread(header, 1, 8, f); - if(png_sig_cmp(header, 0, 8)) goto err; - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - NULL, NULL, NULL); - if(!png_ptr) goto err; - - info_ptr = png_create_info_struct(png_ptr); - if(!info_ptr) goto err; - - if(setjmp(png_jmpbuf(png_ptr))) goto err; - - png_init_io(png_ptr, f); - png_set_sig_bytes(png_ptr, 8); - - png_read_png(png_ptr, info_ptr, - PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA, NULL); - - int w = info_ptr->width, - h = info_ptr->height; - BYTE **rows = png_get_rows(png_ptr, info_ptr); - - // Round to next-highest powers of two, since the textures require - // that. And round up to 4, to guarantee DWORD alignment. - int rw = max(4, RoundUpToPowerOfTwo(w)), - rh = max(4, RoundUpToPowerOfTwo(h)); - - SS.bgImage.fromFile = (BYTE *)MemAlloc(rw*rh*3); - for(int i = 0; i < h; i++) { - memcpy(SS.bgImage.fromFile + ((h - 1) - i)*(rw*3), rows[i], w*3); - } - SS.bgImage.w = w; - SS.bgImage.h = h; - SS.bgImage.rw = rw; - SS.bgImage.rh = rh; - SS.bgImage.scale = SS.GW.scale; - SS.bgImage.origin = SS.GW.offset.ScaledBy(-1); - -err: - if(png_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - if(f) fclose(f); - } - SS.later.showTW = true; -} - -void TextWindow::ScreenChangeBackgroundImageScale(int link, DWORD v) { - char str[300]; - sprintf(str, "%.3f", SS.bgImage.scale * SS.MmPerUnit()); - SS.TW.edit.meaning = EDIT_BACKGROUND_IMG_SCALE; - SS.TW.ShowEditControl(v, 10, str); -} - -void TextWindow::ShowListOfStyles(void) { - Printf(true, "%Ft color style-name"); - - bool darkbg = false; - Style *s; - for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) { - Printf(false, "%Bp %Bp %Bp %Fl%Ll%f%D%s%E", - darkbg ? 'd' : 'a', - 0x80000000 | s->color, - darkbg ? 'd' : 'a', - ScreenShowStyleInfo, s->h.v, - s->DescriptionString()); - - darkbg = !darkbg; - } - - Printf(true, " %Fl%Ll%fcreate a new custom style%E", - &ScreenCreateCustomStyle); - - Printf(false, ""); - - DWORD rgb = SS.backgroundColor; - Printf(false, "%Ft background color (r, g, b)%E"); - Printf(false, "%Ba %@, %@, %@ %Fl%D%f%Ll[change]%E", - REDf(rgb), GREENf(rgb), BLUEf(rgb), - top[rows-1] + 2, &ScreenChangeBackgroundColor); - - Printf(false, ""); - Printf(false, "%Ft background bitmap image%E"); - if(SS.bgImage.fromFile) { - Printf(false, "%Ba %Ftwidth:%E %dpx %Ftheight:%E %dpx", - SS.bgImage.w, SS.bgImage.h); - - Printf(false, " %Ftscale:%E %# px/%s %Fl%Ll%f%D[change]%E", - SS.bgImage.scale*SS.MmPerUnit(), - SS.UnitName(), - &ScreenChangeBackgroundImageScale, top[rows-1] + 2); - - Printf(false, "%Ba %Fl%Lc%fclear background image%E", - &ScreenBackgroundImage); - } else { - Printf(false, "%Ba none - %Fl%Ll%fload background image%E", - &ScreenBackgroundImage); - Printf(false, " (bottom left will be center of view)"); - } - - Printf(false, ""); - Printf(false, " %Fl%Ll%fload factory defaults%E", - &ScreenLoadFactoryDefaultStyles); -} - - -void TextWindow::ScreenChangeStyleName(int link, DWORD v) { - hStyle hs = { v }; - Style *s = Style::Get(hs); - SS.TW.ShowEditControl(10, 12, s->name.str); - SS.TW.edit.style = hs; - SS.TW.edit.meaning = EDIT_STYLE_NAME; -} - -void TextWindow::ScreenDeleteStyle(int link, DWORD v) { - SS.UndoRemember(); - hStyle hs = { v }; - Style *s = SK.style.FindByIdNoOops(hs); - if(s) { - SK.style.RemoveById(hs); - // And it will get recreated automatically if something is still using - // the style, so no need to do anything else. - } - SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); - InvalidateGraphics(); -} - -void TextWindow::ScreenChangeStyleWidthOrTextHeight(int link, DWORD v) { - hStyle hs = { v }; - Style *s = Style::Get(hs); - double val = (link == 't') ? s->textHeight : s->width; - int units = (link == 't') ? s->textHeightAs : s->widthAs; - - char str[300]; - if(units == Style::UNITS_AS_PIXELS) { - sprintf(str, "%.2f", val); - } else { - strcpy(str, SS.MmToString(val)); - } - int row = 0, col = 9; - if(link == 'w') { - row = 17; // width for a default style - } else if(link == 'W') { - row = 17; // width for a custom style - } else if(link == 't') { - row = 33; // text height (for custom styles only) - col++; - } - SS.TW.ShowEditControl(row, col, str); - SS.TW.edit.style = hs; - SS.TW.edit.meaning = (link == 't') ? EDIT_STYLE_TEXT_HEIGHT : - EDIT_STYLE_WIDTH; -} - -void TextWindow::ScreenChangeStyleTextAngle(int link, DWORD v) { - hStyle hs = { v }; - Style *s = Style::Get(hs); - char str[300]; - sprintf(str, "%.2f", s->textAngle); - SS.TW.ShowEditControl(37, 9, str); - SS.TW.edit.style = hs; - SS.TW.edit.meaning = EDIT_STYLE_TEXT_ANGLE; -} - -void TextWindow::ScreenChangeStyleColor(int link, DWORD v) { - hStyle hs = { v }; - Style *s = Style::Get(hs); - // Same function used for stroke and fill colors - int row, col, em; - DWORD rgb; - if(link == 's') { - row = 15; col = 13; - em = EDIT_STYLE_COLOR; - rgb = s->color; - } else if(link == 'f') { - row = 25; col = 13; - em = EDIT_STYLE_FILL_COLOR; - rgb = s->fillColor; - } else { - oops(); - } - SS.TW.ShowEditControlWithColorPicker(row, col, rgb); - SS.TW.edit.style = hs; - SS.TW.edit.meaning = em; -} - -void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) { - SS.UndoRemember(); - hStyle hs = { v }; - Style *s = Style::Get(hs); - switch(link) { - // Units for the width - case 'w': - if(s->widthAs != Style::UNITS_AS_MM) { - s->widthAs = Style::UNITS_AS_MM; - s->width /= SS.GW.scale; - } - break; - case 'W': - if(s->widthAs != Style::UNITS_AS_PIXELS) { - s->widthAs = Style::UNITS_AS_PIXELS; - s->width *= SS.GW.scale; - } - break; - - // Units for the height - case 'g': - if(s->textHeightAs != Style::UNITS_AS_MM) { - s->textHeightAs = Style::UNITS_AS_MM; - s->textHeight /= SS.GW.scale; - } - break; - - case 'G': - if(s->textHeightAs != Style::UNITS_AS_PIXELS) { - s->textHeightAs = Style::UNITS_AS_PIXELS; - s->textHeight *= SS.GW.scale; - } - break; - - case 'e': - s->exportable = !(s->exportable); - break; - - case 'v': - s->visible = !(s->visible); - break; - - case 'f': - s->filled = !(s->filled); - break; - - // Horizontal text alignment - case 'L': - s->textOrigin |= Style::ORIGIN_LEFT; - s->textOrigin &= ~Style::ORIGIN_RIGHT; - break; - case 'H': - s->textOrigin &= ~Style::ORIGIN_LEFT; - s->textOrigin &= ~Style::ORIGIN_RIGHT; - break; - case 'R': - s->textOrigin &= ~Style::ORIGIN_LEFT; - s->textOrigin |= Style::ORIGIN_RIGHT; - break; - - // Vertical text alignment - case 'B': - s->textOrigin |= Style::ORIGIN_BOT; - s->textOrigin &= ~Style::ORIGIN_TOP; - break; - case 'V': - s->textOrigin &= ~Style::ORIGIN_BOT; - s->textOrigin &= ~Style::ORIGIN_TOP; - break; - case 'T': - s->textOrigin &= ~Style::ORIGIN_BOT; - s->textOrigin |= Style::ORIGIN_TOP; - break; - } - InvalidateGraphics(); -} - -bool TextWindow::EditControlDoneForStyles(char *str) { - Style *s; - switch(edit.meaning) { - case EDIT_STYLE_TEXT_HEIGHT: - case EDIT_STYLE_WIDTH: { - SS.UndoRemember(); - s = Style::Get(edit.style); - - double v; - int units = (edit.meaning == EDIT_STYLE_TEXT_HEIGHT) ? - s->textHeightAs : s->widthAs; - if(units == Style::UNITS_AS_MM) { - v = SS.StringToMm(str); - } else { - v = atof(str); - } - v = max(0, v); - if(edit.meaning == EDIT_STYLE_TEXT_HEIGHT) { - s->textHeight = v; - } else { - s->width = v; - } - break; - } - case EDIT_STYLE_TEXT_ANGLE: - SS.UndoRemember(); - s = Style::Get(edit.style); - s->textAngle = WRAP_SYMMETRIC(atof(str), 360); - break; - - case EDIT_BACKGROUND_COLOR: - case EDIT_STYLE_FILL_COLOR: - case EDIT_STYLE_COLOR: { - Vector rgb; - if(sscanf(str, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) { - rgb = rgb.ClampWithin(0, 1); - if(edit.meaning == EDIT_STYLE_COLOR) { - SS.UndoRemember(); - s = Style::Get(edit.style); - s->color = RGBf(rgb.x, rgb.y, rgb.z); - } else if(edit.meaning == EDIT_STYLE_FILL_COLOR) { - SS.UndoRemember(); - s = Style::Get(edit.style); - s->fillColor = RGBf(rgb.x, rgb.y, rgb.z); - } else { - SS.backgroundColor = RGBf(rgb.x, rgb.y, rgb.z); - } - } else { - Error("Bad format: specify color as r, g, b"); - } - break; - } - case EDIT_STYLE_NAME: - if(!StringAllPrintable(str) || !*str) { - Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); - } else { - SS.UndoRemember(); - s = Style::Get(edit.style); - s->name.strcpy(str); - } - break; - - case EDIT_BACKGROUND_IMG_SCALE: { - Expr *e = Expr::From(str, true); - if(e) { - double ev = e->Eval(); - if(ev < 0.001 || isnan(ev)) { - Error("Scale must not be zero or negative!"); - } else { - SS.bgImage.scale = ev / SS.MmPerUnit(); - } - } - break; - } - default: return false; - } - return true; -} - -void TextWindow::ShowStyleInfo(void) { - Printf(true, "%Fl%f%Ll(back to list of styles)%E", &ScreenShowListOfStyles); - - Style *s = Style::Get(shown.style); - - if(s->h.v < Style::FIRST_CUSTOM) { - Printf(true, "%FtSTYLE %E%s ", s->DescriptionString()); - } else { - Printf(true, "%FtSTYLE %E%s " - "[%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]", - s->DescriptionString(), - s->h.v, &ScreenChangeStyleName, - s->h.v, &ScreenDeleteStyle); - } - - Printf(true, "%Ft line stroke style%E"); - Printf(false, "%Ba %Ftcolor %E%Bp %Ba (%@, %@, %@) %D%f%Ls%Fl[change]%E", - 0x80000000 | s->color, - REDf(s->color), GREENf(s->color), BLUEf(s->color), - s->h.v, ScreenChangeStyleColor); - - // The line width, and its units - if(s->widthAs == Style::UNITS_AS_PIXELS) { - Printf(false, " %Ftwidth%E %@ %D%f%Lp%Fl[change]%E", - s->width, - s->h.v, &ScreenChangeStyleWidthOrTextHeight, - (s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W'); - } else { - Printf(false, " %Ftwidth%E %s %D%f%Lp%Fl[change]%E", - SS.MmToString(s->width), - s->h.v, &ScreenChangeStyleWidthOrTextHeight, - (s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W'); - } - - bool widthpx = (s->widthAs == Style::UNITS_AS_PIXELS); - if(s->h.v < Style::FIRST_CUSTOM) { - Printf(false,"%Ba %Ftin units of %Fdpixels%E"); - } else { - Printf(false,"%Ba %Ftin units of %Fd" - "%D%f%LW%c pixels%E " - "%D%f%Lw%c %s", - s->h.v, &ScreenChangeStyleYesNo, - widthpx ? RADIO_TRUE : RADIO_FALSE, - s->h.v, &ScreenChangeStyleYesNo, - !widthpx ? RADIO_TRUE : RADIO_FALSE, - SS.UnitName()); - } - - if(s->h.v >= Style::FIRST_CUSTOM) { - // The fill color, and whether contours are filled - - Printf(false, ""); - Printf(false, "%Ft contour fill style%E"); - Printf(false, - "%Ba %Ftcolor %E%Bp %Ba (%@, %@, %@) %D%f%Lf%Fl[change]%E", - 0x80000000 | s->fillColor, - REDf(s->fillColor), GREENf(s->fillColor), BLUEf(s->fillColor), - s->h.v, ScreenChangeStyleColor); - - Printf(false, "%Bd %D%f%Lf%c contours are filled%E", - s->h.v, &ScreenChangeStyleYesNo, - s->filled ? CHECK_TRUE : CHECK_FALSE); - } - - // The text height, and its units - Printf(false, ""); - Printf(false, "%Ft text comment style%E"); - - char *chng = (s->h.v < Style::FIRST_CUSTOM) ? "" : "[change]"; - if(s->textHeightAs == Style::UNITS_AS_PIXELS) { - Printf(false, "%Ba %Ftheight %E%@ %D%f%Lt%Fl%s%E", - s->textHeight, - s->h.v, &ScreenChangeStyleWidthOrTextHeight, - chng); - } else { - Printf(false, "%Ba %Ftheight %E%s %D%f%Lt%Fl%s%E", - SS.MmToString(s->textHeight), - s->h.v, &ScreenChangeStyleWidthOrTextHeight, - chng); - } - - bool textHeightpx = (s->textHeightAs == Style::UNITS_AS_PIXELS); - if(s->h.v < Style::FIRST_CUSTOM) { - Printf(false,"%Bd %Ftin units of %Fdpixels"); - } else { - Printf(false,"%Bd %Ftin units of %Fd" - "%D%f%LG%c pixels%E " - "%D%f%Lg%c %s", - s->h.v, &ScreenChangeStyleYesNo, - textHeightpx ? RADIO_TRUE : RADIO_FALSE, - s->h.v, &ScreenChangeStyleYesNo, - !textHeightpx ? RADIO_TRUE : RADIO_FALSE, - SS.UnitName()); - } - - if(s->h.v >= Style::FIRST_CUSTOM) { - Printf(false, "%Ba %Ftangle %E%@ %D%f%Ll%Fl[change]%E", - s->textAngle, - s->h.v, &ScreenChangeStyleTextAngle); - - Printf(false, ""); - Printf(false, "%Ft text comment alignment%E"); - bool neither; - neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT)); - Printf(false, "%Ba " - "%D%f%LL%c left%E " - "%D%f%LH%c center%E " - "%D%f%LR%c right%E ", - s->h.v, &ScreenChangeStyleYesNo, - (s->textOrigin & Style::ORIGIN_LEFT) ? RADIO_TRUE : RADIO_FALSE, - s->h.v, &ScreenChangeStyleYesNo, - neither ? RADIO_TRUE : RADIO_FALSE, - s->h.v, &ScreenChangeStyleYesNo, - (s->textOrigin & Style::ORIGIN_RIGHT) ? RADIO_TRUE : RADIO_FALSE); - - neither = !(s->textOrigin & (Style::ORIGIN_BOT | Style::ORIGIN_TOP)); - Printf(false, "%Bd " - "%D%f%LB%c bottom%E " - "%D%f%LV%c center%E " - "%D%f%LT%c top%E ", - s->h.v, &ScreenChangeStyleYesNo, - (s->textOrigin & Style::ORIGIN_BOT) ? RADIO_TRUE : RADIO_FALSE, - s->h.v, &ScreenChangeStyleYesNo, - neither ? RADIO_TRUE : RADIO_FALSE, - s->h.v, &ScreenChangeStyleYesNo, - (s->textOrigin & Style::ORIGIN_TOP) ? RADIO_TRUE : RADIO_FALSE); - } - - if(s->h.v >= Style::FIRST_CUSTOM) { - Printf(false, ""); - - Printf(false, " %Fd%D%f%Lv%c show these objects on screen%E", - s->h.v, &ScreenChangeStyleYesNo, - s->visible ? CHECK_TRUE : CHECK_FALSE); - - Printf(false, " %Fd%D%f%Le%c export these objects%E", - s->h.v, &ScreenChangeStyleYesNo, - s->exportable ? CHECK_TRUE : CHECK_FALSE); - - Printf(false, ""); - Printf(false, "To assign lines or curves to this style,"); - Printf(false, "right-click them on the drawing."); - } -} - -void TextWindow::ScreenAssignSelectionToStyle(int link, DWORD v) { - Style::AssignSelectionToStyle(v); -} - diff --git a/textscreens.cpp b/textscreens.cpp deleted file mode 100644 index 8527b1f..0000000 --- a/textscreens.cpp +++ /dev/null @@ -1,758 +0,0 @@ -//----------------------------------------------------------------------------- -// The text-based browser window, used to view the structure of the model by -// groups and for other similar purposes. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -//----------------------------------------------------------------------------- -// A navigation bar that always appears at the top of the window, with a -// link to bring us back home. -//----------------------------------------------------------------------------- -void TextWindow::ScreenHome(int link, DWORD v) { - SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS); -} -void TextWindow::ShowHeader(bool withNav) { - ClearScreen(); - - char cd[1024], cd2[1024]; - if(SS.GW.LockedInWorkplane()) { - sprintf(cd, "in plane: "); - strcpy(cd2, SK.GetEntity(SS.GW.ActiveWorkplane())->DescriptionString()); - } else { - sprintf(cd, "drawing / constraining in 3d"); - strcpy(cd2, ""); - } - - // Navigation buttons - if(withNav) { - Printf(false, " %Fl%Lh%fhome%E %Ft%s%E%s", - (&TextWindow::ScreenHome), cd, cd2); - } else { - Printf(false, " %Ft%s%E%s", cd, cd2); - } - - // Leave space for the icons that are painted here. - Printf(false, ""); - Printf(false, ""); -} - -//----------------------------------------------------------------------------- -// The screen that shows a list of every group in the sketch, with options -// to hide or show them, and to view them in detail. This is our home page. -//----------------------------------------------------------------------------- -void TextWindow::ScreenSelectGroup(int link, DWORD v) { - SS.TW.GoToScreen(SCREEN_GROUP_INFO); - SS.TW.shown.group.v = v; -} -void TextWindow::ScreenToggleGroupShown(int link, DWORD v) { - hGroup hg = { v }; - Group *g = SK.GetGroup(hg); - g->visible = !(g->visible); - // If a group was just shown, then it might not have been generated - // previously, so regenerate. - SS.GenerateAll(); -} -void TextWindow::ScreenShowGroupsSpecial(int link, DWORD v) { - int i; - for(i = 0; i < SK.group.n; i++) { - Group *g = &(SK.group.elem[i]); - - if(link == 's') { - g->visible = true; - } else { - g->visible = false; - } - } -} -void TextWindow::ScreenActivateGroup(int link, DWORD v) { - hGroup hg = { v }; - Group *g = SK.GetGroup(hg); - g->visible = true; - SS.GW.activeGroup.v = v; - SK.GetGroup(SS.GW.activeGroup)->Activate(); - SS.GW.ClearSuper(); -} -void TextWindow::ReportHowGroupSolved(hGroup hg) { - SS.GW.ClearSuper(); - SS.TW.GoToScreen(SCREEN_GROUP_SOLVE_INFO); - SS.TW.shown.group.v = hg.v; - SS.later.showTW = true; -} -void TextWindow::ScreenHowGroupSolved(int link, DWORD v) { - if(SS.GW.activeGroup.v != v) { - ScreenActivateGroup(link, v); - } - SS.TW.GoToScreen(SCREEN_GROUP_SOLVE_INFO); - SS.TW.shown.group.v = v; -} -void TextWindow::ScreenShowConfiguration(int link, DWORD v) { - SS.TW.GoToScreen(SCREEN_CONFIGURATION); -} -void TextWindow::ScreenShowEditView(int link, DWORD v) { - SS.TW.GoToScreen(SCREEN_EDIT_VIEW); -} -void TextWindow::ScreenGoToWebsite(int link, DWORD v) { - OpenWebsite("http://solvespace.com/txtlink"); -} -void TextWindow::ShowListOfGroups(void) { - char radioTrue[] = { ' ', (char)RADIO_TRUE, ' ', 0 }, - radioFalse[] = { ' ', (char)RADIO_FALSE, ' ', 0 }, - checkTrue[] = { ' ', (char)CHECK_TRUE, ' ', 0 }, - checkFalse[] = { ' ', (char)CHECK_FALSE, ' ', 0 }; - - Printf(true, "%Ft active"); - Printf(false, "%Ft shown ok group-name%E"); - int i; - bool afterActive = false; - for(i = 0; i < SK.group.n; i++) { - Group *g = &(SK.group.elem[i]); - char *s = g->DescriptionString(); - bool active = (g->h.v == SS.GW.activeGroup.v); - bool shown = g->visible; - bool ok = (g->solved.how == System::SOLVED_OKAY); - bool ref = (g->h.v == Group::HGROUP_REFERENCES.v); - Printf(false, "%Bp%Fd " - "%Ft%s%Fb%D%f%Ll%s%E " - "%Fb%s%D%f%Ll%s%E " - "%Fp%D%f%s%Ll%s%E " - "%Fl%Ll%D%f%s", - // Alternate between light and dark backgrounds, for readability - (i & 1) ? 'd' : 'a', - // Link that activates the group - ref ? " " : "", - g->h.v, (&TextWindow::ScreenActivateGroup), - ref ? "" : (active ? radioTrue : radioFalse), - // Link that hides or shows the group - afterActive ? " - " : "", - g->h.v, (&TextWindow::ScreenToggleGroupShown), - afterActive ? "" : (shown ? checkTrue : checkFalse), - // Link to the errors, if a problem occured while solving - ok ? 's' : 'x', g->h.v, (&TextWindow::ScreenHowGroupSolved), - ok ? "ok" : "", - ok ? "" : "NO", - // Link to a screen that gives more details on the group - g->h.v, (&TextWindow::ScreenSelectGroup), s); - - if(active) afterActive = true; - } - - Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E", - &(TextWindow::ScreenShowGroupsSpecial), - &(TextWindow::ScreenShowGroupsSpecial)); - Printf(true, " %Fl%Ls%fline styles%E /" - " %Fl%Ls%fview%E /" - " %Fl%Ls%fconfiguration%E", - &(TextWindow::ScreenShowListOfStyles), - &(TextWindow::ScreenShowEditView), - &(TextWindow::ScreenShowConfiguration)); -} - - -//----------------------------------------------------------------------------- -// The screen that shows information about a specific group, and allows the -// user to edit various things about it. -//----------------------------------------------------------------------------- -void TextWindow::ScreenHoverConstraint(int link, DWORD v) { - if(!SS.GW.showConstraints) return; - - hConstraint hc = { v }; - Constraint *c = SK.GetConstraint(hc); - if(c->group.v != SS.GW.activeGroup.v) { - // Only constraints in the active group are visible - return; - } - SS.GW.hover.Clear(); - SS.GW.hover.constraint = hc; - SS.GW.hover.emphasized = true; -} -void TextWindow::ScreenHoverRequest(int link, DWORD v) { - SS.GW.hover.Clear(); - hRequest hr = { v }; - SS.GW.hover.entity = hr.entity(0); - SS.GW.hover.emphasized = true; -} -void TextWindow::ScreenSelectConstraint(int link, DWORD v) { - SS.GW.ClearSelection(); - GraphicsWindow::Selection sel; - ZERO(&sel); - sel.constraint.v = v; - SS.GW.selection.Add(&sel); -} -void TextWindow::ScreenSelectRequest(int link, DWORD v) { - SS.GW.ClearSelection(); - GraphicsWindow::Selection sel; - ZERO(&sel); - hRequest hr = { v }; - sel.entity = hr.entity(0); - SS.GW.selection.Add(&sel); -} - -void TextWindow::ScreenChangeGroupOption(int link, DWORD v) { - SS.UndoRemember(); - Group *g = SK.GetGroup(SS.TW.shown.group); - - switch(link) { - case 's': g->subtype = Group::ONE_SIDED; break; - case 'S': g->subtype = Group::TWO_SIDED; break; - - case 'k': g->skipFirst = true; break; - case 'K': g->skipFirst = false; break; - - case 'c': g->meshCombine = v; break; - - case 'P': g->suppress = !(g->suppress); break; - - case 'r': g->relaxConstraints = !(g->relaxConstraints); break; - - case 'v': g->visible = !(g->visible); break; - - case 'd': g->allDimsReference = !(g->allDimsReference); break; - - case 'f': g->forceToMesh = !(g->forceToMesh); break; - } - - SS.MarkGroupDirty(g->h); - SS.GenerateAll(); - SS.GW.ClearSuper(); -} - -void TextWindow::ScreenColor(int link, DWORD v) { - SS.UndoRemember(); - - Group *g = SK.GetGroup(SS.TW.shown.group); - SS.TW.ShowEditControlWithColorPicker(v, 3, g->color); - SS.TW.edit.meaning = EDIT_GROUP_COLOR; -} -void TextWindow::ScreenChangeExprA(int link, DWORD v) { - Group *g = SK.GetGroup(SS.TW.shown.group); - - // There's an extra line for the skipFirst parameter in one-sided groups. - int r = (g->subtype == Group::ONE_SIDED) ? 16 : 14; - - char str[1024]; - sprintf(str, "%d", (int)g->valA); - SS.TW.ShowEditControl(r, 10, str); - SS.TW.edit.meaning = EDIT_TIMES_REPEATED; - SS.TW.edit.group.v = v; -} -void TextWindow::ScreenChangeGroupName(int link, DWORD v) { - Group *g = SK.GetGroup(SS.TW.shown.group); - SS.TW.ShowEditControl(7, 12, g->DescriptionString()+5); - SS.TW.edit.meaning = EDIT_GROUP_NAME; - SS.TW.edit.group.v = v; -} -void TextWindow::ScreenChangeGroupScale(int link, DWORD v) { - Group *g = SK.GetGroup(SS.TW.shown.group); - - char str[1024]; - sprintf(str, "%.3f", g->scale); - SS.TW.ShowEditControl(14, 13, str); - SS.TW.edit.meaning = EDIT_GROUP_SCALE; - SS.TW.edit.group.v = v; -} -void TextWindow::ScreenDeleteGroup(int link, DWORD v) { - SS.UndoRemember(); - - hGroup hg = SS.TW.shown.group; - if(hg.v == SS.GW.activeGroup.v) { - Error("This group is currently active; activate a different group " - "before proceeding."); - return; - } - SK.group.RemoveById(SS.TW.shown.group); - // This is a major change, so let's re-solve everything. - SS.TW.ClearSuper(); - SS.GW.ClearSuper(); - SS.GenerateAll(0, INT_MAX); -} -void TextWindow::ShowGroupInfo(void) { - Group *g = SK.group.FindById(shown.group); - char *s = "???"; - - if(shown.group.v == Group::HGROUP_REFERENCES.v) { - Printf(true, "%FtGROUP %E%s", g->DescriptionString()); - goto list_items; - } else { - Printf(true, "%FtGROUP %E%s [%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]", - g->DescriptionString(), - g->h.v, &TextWindow::ScreenChangeGroupName, - g->h.v, &TextWindow::ScreenDeleteGroup); - } - - if(g->type == Group::LATHE) { - Printf(true, " %Ftlathe plane sketch"); - } else if(g->type == Group::EXTRUDE || g->type == Group::ROTATE || - g->type == Group::TRANSLATE) - { - if(g->type == Group::EXTRUDE) { - s = "extrude plane sketch"; - } else if(g->type == Group::TRANSLATE) { - s = "translate original sketch"; - } else if(g->type == Group::ROTATE) { - s = "rotate original sketch"; - } - Printf(true, " %Ft%s%E", s); - - bool one = (g->subtype == Group::ONE_SIDED); - Printf(false, - "%Ba %f%Ls%Fd%c one-sided%E " - "%f%LS%Fd%c two-sided%E", - &TextWindow::ScreenChangeGroupOption, - one ? RADIO_TRUE : RADIO_FALSE, - &TextWindow::ScreenChangeGroupOption, - !one ? RADIO_TRUE : RADIO_FALSE); - - if(g->type == Group::ROTATE || g->type == Group::TRANSLATE) { - if(g->subtype == Group::ONE_SIDED) { - bool skip = g->skipFirst; - Printf(false, - "%Bd %Ftstart %f%LK%Fd%c with original%E " - "%f%Lk%Fd%c with copy #1%E", - &ScreenChangeGroupOption, - !skip ? RADIO_TRUE : RADIO_FALSE, - &ScreenChangeGroupOption, - skip ? RADIO_TRUE : RADIO_FALSE); - } - - int times = (int)(g->valA); - Printf(false, "%Bp %Ftrepeat%E %d time%s %Fl%Ll%D%f[change]%E", - (g->subtype == Group::ONE_SIDED) ? 'a' : 'd', - times, times == 1 ? "" : "s", - g->h.v, &TextWindow::ScreenChangeExprA); - } - } else if(g->type == Group::IMPORTED) { - Printf(true, " %Ftimport geometry from file%E"); - Printf(false, "%Ba '%s'", g->impFileRel); - Printf(false, "%Bd %Ftscaled by%E %# %Fl%Ll%f%D[change]%E", - g->scale, - &TextWindow::ScreenChangeGroupScale, g->h.v); - } else if(g->type == Group::DRAWING_3D) { - Printf(true, " %Ftsketch in 3d%E"); - } else if(g->type == Group::DRAWING_WORKPLANE) { - Printf(true, " %Ftsketch in new workplane%E"); - } else { - Printf(true, "???"); - } - Printf(false, ""); - - if(g->type == Group::EXTRUDE || - g->type == Group::LATHE || - g->type == Group::IMPORTED) - { - bool un = (g->meshCombine == Group::COMBINE_AS_UNION); - bool diff = (g->meshCombine == Group::COMBINE_AS_DIFFERENCE); - bool asy = (g->meshCombine == Group::COMBINE_AS_ASSEMBLE); - bool asa = (g->type == Group::IMPORTED); - - Printf(false, " %Ftsolid model as"); - Printf(false, "%Ba %f%D%Lc%Fd%c union%E " - "%f%D%Lc%Fd%c difference%E " - "%f%D%Lc%Fd%c%s%E ", - &TextWindow::ScreenChangeGroupOption, - Group::COMBINE_AS_UNION, - un ? RADIO_TRUE : RADIO_FALSE, - &TextWindow::ScreenChangeGroupOption, - Group::COMBINE_AS_DIFFERENCE, - diff ? RADIO_TRUE : RADIO_FALSE, - &TextWindow::ScreenChangeGroupOption, - Group::COMBINE_AS_ASSEMBLE, - asa ? (asy ? RADIO_TRUE : RADIO_FALSE) : 0, - asa ? " assemble" : ""); - - if(g->type == Group::EXTRUDE || - g->type == Group::LATHE) - { - Printf(false, - "%Bd %Ftcolor %E%Bp %Bd (%@, %@, %@) %f%D%Lf%Fl[change]%E", - 0x80000000 | g->color, - REDf(g->color), GREENf(g->color), BLUEf(g->color), - ScreenColor, top[rows-1] + 2); - } else if(g->type == Group::IMPORTED) { - bool sup = g->suppress; - Printf(false, " %Fd%f%LP%c suppress this group's solid model", - &TextWindow::ScreenChangeGroupOption, - g->suppress ? CHECK_TRUE : CHECK_FALSE); - } - - Printf(false, ""); - } - - Printf(false, " %f%Lv%Fd%c show entities from this group", - &TextWindow::ScreenChangeGroupOption, - g->visible ? CHECK_TRUE : CHECK_FALSE); - - Group *pg = g->PreviousGroup(); - if(pg && pg->runningMesh.IsEmpty() && g->thisMesh.IsEmpty()) { - Printf(false, " %f%Lf%Fd%c force NURBS surfaces to triangle mesh", - &TextWindow::ScreenChangeGroupOption, - g->forceToMesh ? CHECK_TRUE : CHECK_FALSE); - } else { - Printf(false, " (model already forced to triangle mesh)"); - } - - Printf(true, " %f%Lr%Fd%c relax constraints and dimensions", - &TextWindow::ScreenChangeGroupOption, - g->relaxConstraints ? CHECK_TRUE : CHECK_FALSE); - - Printf(false, " %f%Ld%Fd%c treat all dimensions as reference", - &TextWindow::ScreenChangeGroupOption, - g->allDimsReference ? CHECK_TRUE : CHECK_FALSE); - - if(g->booleanFailed) { - Printf(false, ""); - Printf(false, "The Boolean operation failed. It may be "); - Printf(false, "possible to fix the problem by choosing "); - Printf(false, "'force NURBS surfaces to triangle mesh'."); - } - -list_items: - Printf(false, ""); - Printf(false, "%Ft requests in group"); - - int i, a = 0; - for(i = 0; i < SK.request.n; i++) { - Request *r = &(SK.request.elem[i]); - - if(r->group.v == shown.group.v) { - char *s = r->DescriptionString(); - Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", - (a & 1) ? 'd' : 'a', - r->h.v, (&TextWindow::ScreenSelectRequest), - &(TextWindow::ScreenHoverRequest), s); - a++; - } - } - if(a == 0) Printf(false, "%Ba (none)"); - - a = 0; - Printf(false, ""); - Printf(false, "%Ft constraints in group (%d DOF)", g->solved.dof); - for(i = 0; i < SK.constraint.n; i++) { - Constraint *c = &(SK.constraint.elem[i]); - - if(c->group.v == shown.group.v) { - char *s = c->DescriptionString(); - Printf(false, "%Bp %Fl%Ll%D%f%h%s%E %s", - (a & 1) ? 'd' : 'a', - c->h.v, (&TextWindow::ScreenSelectConstraint), - (&TextWindow::ScreenHoverConstraint), s, - c->reference ? "(ref)" : ""); - a++; - } - } - if(a == 0) Printf(false, "%Ba (none)"); -} - -//----------------------------------------------------------------------------- -// The screen that's displayed when the sketch fails to solve. A report of -// what failed, and (if the problem is a singular Jacobian) a list of -// constraints that could be removed to fix it. -//----------------------------------------------------------------------------- -void TextWindow::ShowGroupSolveInfo(void) { - Group *g = SK.group.FindById(shown.group); - if(g->solved.how == System::SOLVED_OKAY) { - // Go back to the default group info screen - shown.screen = SCREEN_GROUP_INFO; - Show(); - return; - } - - Printf(true, "%FtGROUP %E%s", g->DescriptionString()); - switch(g->solved.how) { - case System::DIDNT_CONVERGE: - Printf(true, "%FxSOLVE FAILED!%Fd no convergence"); - Printf(true, "the following constraints are unsatisfied"); - break; - - case System::SINGULAR_JACOBIAN: - Printf(true, "%FxSOLVE FAILED!%Fd inconsistent system"); - Printf(true, "remove any one of these to fix it"); - break; - - case System::TOO_MANY_UNKNOWNS: - Printf(true, "Too many unknowns in a single group!"); - return; - } - - for(int i = 0; i < g->solved.remove.n; i++) { - hConstraint hc = g->solved.remove.elem[i]; - Constraint *c = SK.constraint.FindByIdNoOops(hc); - if(!c) continue; - - Printf(false, "%Bp %Fl%Ll%D%f%h%s%E", - (i & 1) ? 'd' : 'a', - c->h.v, (&TextWindow::ScreenSelectConstraint), - (&TextWindow::ScreenHoverConstraint), - c->DescriptionString()); - } - - Printf(true, "It may be possible to fix the problem "); - Printf(false, "by selecting Edit -> Undo."); -} - -//----------------------------------------------------------------------------- -// When we're stepping a dimension. User specifies the finish value, and -// how many steps to take in between current and finish, re-solving each -// time. -//----------------------------------------------------------------------------- -void TextWindow::ScreenStepDimFinish(int link, DWORD v) { - SS.TW.edit.meaning = EDIT_STEP_DIM_FINISH; - char s[1024]; - if(SS.TW.shown.dimIsDistance) { - strcpy(s, SS.MmToString(SS.TW.shown.dimFinish)); - } else { - sprintf(s, "%.3f", SS.TW.shown.dimFinish); - } - SS.TW.ShowEditControl(12, 12, s); -} -void TextWindow::ScreenStepDimSteps(int link, DWORD v) { - char str[1024]; - sprintf(str, "%d", SS.TW.shown.dimSteps); - SS.TW.edit.meaning = EDIT_STEP_DIM_STEPS; - SS.TW.ShowEditControl(14, 12, str); -} -void TextWindow::ScreenStepDimGo(int link, DWORD v) { - hConstraint hc = SS.TW.shown.constraint; - Constraint *c = SK.constraint.FindByIdNoOops(hc); - if(c) { - SS.UndoRemember(); - double start = c->valA, finish = SS.TW.shown.dimFinish; - int i, n = SS.TW.shown.dimSteps; - for(i = 1; i <= n; i++) { - c = SK.GetConstraint(hc); - c->valA = start + ((finish - start)*i)/n; - SS.MarkGroupDirty(c->group); - SS.GenerateAll(); - if(!SS.AllGroupsOkay()) { - // Failed to solve, so quit - break; - } - PaintGraphics(); - } - } - InvalidateGraphics(); - SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS); -} -void TextWindow::ShowStepDimension(void) { - Constraint *c = SK.constraint.FindByIdNoOops(shown.constraint); - if(!c) { - shown.screen = SCREEN_LIST_OF_GROUPS; - Show(); - return; - } - - Printf(true, "%FtSTEP DIMENSION%E %s", c->DescriptionString()); - - if(shown.dimIsDistance) { - Printf(true, "%Ba %Ftstart%E %s", SS.MmToString(c->valA)); - Printf(false, "%Bd %Ftfinish%E %s %Fl%Ll%f[change]%E", - SS.MmToString(shown.dimFinish), &ScreenStepDimFinish); - } else { - Printf(true, "%Ba %Ftstart%E %@", c->valA); - Printf(false, "%Bd %Ftfinish%E %@ %Fl%Ll%f[change]%E", - shown.dimFinish, &ScreenStepDimFinish); - } - Printf(false, "%Ba %Ftsteps%E %d %Fl%Ll%f%D[change]%E", - shown.dimSteps, &ScreenStepDimSteps); - - Printf(true, " %Fl%Ll%fstep dimension now%E", &ScreenStepDimGo); - - Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome); -} - -//----------------------------------------------------------------------------- -// When we're creating tangent arcs (as requests, not as some parametric -// thing). User gets to specify the radius, and whether the old untrimmed -// curves are kept or deleted. -//----------------------------------------------------------------------------- -void TextWindow::ScreenChangeTangentArc(int link, DWORD v) { - switch(link) { - case 'r': { - char str[1024]; - strcpy(str, SS.MmToString(SS.tangentArcRadius)); - SS.TW.edit.meaning = EDIT_TANGENT_ARC_RADIUS; - SS.TW.ShowEditControl(12, 3, str); - break; - } - - case 'a': SS.tangentArcManual = !SS.tangentArcManual; break; - case 'd': SS.tangentArcDeleteOld = !SS.tangentArcDeleteOld; break; - } -} -void TextWindow::ShowTangentArc(void) { - Printf(true, "%FtTANGENT ARC PARAMETERS%E"); - - Printf(true, "%Ft radius of created arc%E"); - if(SS.tangentArcManual) { - Printf(false, "%Ba %s %Fl%Lr%f[change]%E", - SS.MmToString(SS.tangentArcRadius), - &(TextWindow::ScreenChangeTangentArc)); - } else { - Printf(false, "%Ba automatic"); - } - - Printf(false, ""); - Printf(false, " %Fd%f%La%c choose radius automatically%E", - &ScreenChangeTangentArc, - !SS.tangentArcManual ? CHECK_TRUE : CHECK_FALSE); - Printf(false, " %Fd%f%Ld%c delete original entities afterward%E", - &ScreenChangeTangentArc, - SS.tangentArcDeleteOld ? CHECK_TRUE : CHECK_FALSE); - - Printf(false, ""); - Printf(false, "To create a tangent arc at a point,"); - Printf(false, "select that point and then choose"); - Printf(false, "Sketch -> Tangent Arc at Point."); - Printf(true, "(or %Fl%Ll%fback to home screen%E)", &ScreenHome); -} - -//----------------------------------------------------------------------------- -// The edit control is visible, and the user just pressed enter. -//----------------------------------------------------------------------------- -void TextWindow::EditControlDone(char *s) { - edit.showAgain = false; - - switch(edit.meaning) { - case EDIT_TIMES_REPEATED: { - Expr *e = Expr::From(s, true); - if(e) { - SS.UndoRemember(); - - double ev = e->Eval(); - if((int)ev < 1) { - Error("Can't repeat fewer than 1 time."); - break; - } - if((int)ev > 999) { - Error("Can't repeat more than 999 times."); - break; - } - - Group *g = SK.GetGroup(edit.group); - g->valA = ev; - - if(g->type == Group::ROTATE) { - int i, c = 0; - for(i = 0; i < SK.constraint.n; i++) { - if(SK.constraint.elem[i].group.v == g->h.v) c++; - } - // If the group does not contain any constraints, then - // set the numerical guess to space the copies uniformly - // over one rotation. Don't touch the guess if we're - // already constrained, because that would break - // convergence. - if(c == 0) { - double copies = (g->skipFirst) ? (ev + 1) : ev; - SK.GetParam(g->h.param(3))->val = PI/(2*copies); - } - } - - SS.MarkGroupDirty(g->h); - SS.later.generateAll = true; - } - break; - } - case EDIT_GROUP_NAME: { - if(!StringAllPrintable(s) || !*s) { - Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); - } else { - SS.UndoRemember(); - - Group *g = SK.GetGroup(edit.group); - g->name.strcpy(s); - } - break; - } - case EDIT_GROUP_SCALE: { - Expr *e = Expr::From(s, true); - if(e) { - double ev = e->Eval(); - if(fabs(ev) < 1e-6) { - Error("Scale cannot be zero."); - } else { - Group *g = SK.GetGroup(edit.group); - g->scale = ev; - SS.MarkGroupDirty(g->h); - SS.later.generateAll = true; - } - } - break; - } - case EDIT_GROUP_COLOR: { - Vector rgb; - if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) { - rgb = rgb.ClampWithin(0, 1); - - Group *g = SK.group.FindByIdNoOops(SS.TW.shown.group); - if(!g) break; - g->color = RGBf(rgb.x, rgb.y, rgb.z); - - SS.MarkGroupDirty(g->h); - SS.later.generateAll = true; - SS.GW.ClearSuper(); - } else { - Error("Bad format: specify color as r, g, b"); - } - break; - } - case EDIT_TTF_TEXT: { - SS.UndoRemember(); - Request *r = SK.request.FindByIdNoOops(edit.request); - if(r) { - r->str.strcpy(s); - SS.MarkGroupDirty(r->group); - SS.later.generateAll = true; - } - break; - } - case EDIT_STEP_DIM_FINISH: { - Expr *e = Expr::From(s, true); - if(!e) { - break; - } - if(shown.dimIsDistance) { - shown.dimFinish = SS.ExprToMm(e); - } else { - shown.dimFinish = e->Eval(); - } - break; - } - case EDIT_STEP_DIM_STEPS: - shown.dimSteps = min(300, max(1, atoi(s))); - break; - - case EDIT_TANGENT_ARC_RADIUS: { - Expr *e = Expr::From(s, true); - if(!e) break; - if(e->Eval() < LENGTH_EPS) { - Error("Radius cannot be zero or negative."); - break; - } - SS.tangentArcRadius = SS.ExprToMm(e); - break; - } - - default: { - int cnt = 0; - if(EditControlDoneForStyles(s)) cnt++; - if(EditControlDoneForConfiguration(s)) cnt++; - if(EditControlDoneForPaste(s)) cnt++; - if(EditControlDoneForView(s)) cnt++; - if(cnt > 1) { - // The identifiers were somehow assigned not uniquely? - oops(); - } - break; - } - } - InvalidateGraphics(); - SS.later.showTW = true; - - if(!edit.showAgain) { - HideEditControl(); - edit.meaning = EDIT_NOTHING; - } -} - diff --git a/textwin.cpp b/textwin.cpp deleted file mode 100644 index b3cd511..0000000 --- a/textwin.cpp +++ /dev/null @@ -1,1040 +0,0 @@ -//----------------------------------------------------------------------------- -// Helper functions for the text-based browser window. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" -#include "obj/icons-proto.h" -#include - -const TextWindow::Color TextWindow::fgColors[] = { - { 'd', RGB(255, 255, 255) }, - { 'l', RGB(100, 100, 255) }, - { 't', RGB(255, 200, 0) }, - { 'h', RGB( 90, 90, 90) }, - { 's', RGB( 40, 255, 40) }, - { 'm', RGB(200, 200, 0) }, - { 'r', RGB( 0, 0, 0) }, - { 'x', RGB(255, 20, 20) }, - { 'i', RGB( 0, 255, 255) }, - { 'g', RGB(160, 160, 160) }, - { 'b', RGB(200, 200, 200) }, - { 0, 0 }, -}; -const TextWindow::Color TextWindow::bgColors[] = { - { 'd', RGB( 0, 0, 0) }, - { 't', RGB( 34, 15, 15) }, - { 'a', RGB( 25, 25, 25) }, - { 'r', RGB(255, 255, 255) }, - { 0, 0 }, -}; - -bool TextWindow::SPACER = false; -TextWindow::HideShowIcon TextWindow::hideShowIcons[] = { - { &(SS.GW.showWorkplanes), Icon_workplane, "workplanes from inactive groups"}, - { &(SS.GW.showNormals), Icon_normal, "normals" }, - { &(SS.GW.showPoints), Icon_point, "points" }, - { &(SS.GW.showConstraints), Icon_constraint, "constraints and dimensions" }, - { &(SS.GW.showFaces), Icon_faces, "XXX - special cased" }, - { &SPACER, 0 }, - { &(SS.GW.showShaded), Icon_shaded, "shaded view of solid model" }, - { &(SS.GW.showEdges), Icon_edges, "edges of solid model" }, - { &(SS.GW.showMesh), Icon_mesh, "triangle mesh of solid model" }, - { &SPACER, 0 }, - { &(SS.GW.showHdnLines), Icon_hidden_lines, "hidden lines" }, - { 0, 0 }, -}; - -void TextWindow::MakeColorTable(const Color *in, float *out) { - int i; - for(i = 0; in[i].c != 0; i++) { - int c = in[i].c; - if(c < 0 || c > 255) oops(); - out[c*3 + 0] = REDf(in[i].color); - out[c*3 + 1] = GREENf(in[i].color); - out[c*3 + 2] = BLUEf(in[i].color); - } -} - -void TextWindow::Init(void) { - ClearSuper(); -} - -void TextWindow::ClearSuper(void) { - HideEditControl(); - - memset(this, 0, sizeof(*this)); - MakeColorTable(fgColors, fgColorTable); - MakeColorTable(bgColors, bgColorTable); - - ClearScreen(); - Show(); -} - -void TextWindow::HideEditControl(void) { - editControl.colorPicker.show = false; - HideTextEditControl(); -} - -void TextWindow::ShowEditControl(int halfRow, int col, char *s) { - editControl.halfRow = halfRow; - editControl.col = col; - - int x = LEFT_MARGIN + CHAR_WIDTH*col; - int y = (halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2); - - ShowTextEditControl(x - 3, y + 2, s); -} - -void TextWindow::ShowEditControlWithColorPicker(int halfRow, int col, DWORD rgb) -{ - char str[1024]; - sprintf(str, "%.2f, %.2f, %.2f", REDf(rgb), GREENf(rgb), BLUEf(rgb)); - - SS.later.showTW = true; - - editControl.colorPicker.show = true; - editControl.colorPicker.rgb = rgb; - editControl.colorPicker.h = 0; - editControl.colorPicker.s = 0; - editControl.colorPicker.v = 1; - ShowEditControl(halfRow, col, str); -} - -void TextWindow::ClearScreen(void) { - int i, j; - for(i = 0; i < MAX_ROWS; i++) { - for(j = 0; j < MAX_COLS; j++) { - text[i][j] = ' '; - meta[i][j].fg = 'd'; - meta[i][j].bg = 'd'; - meta[i][j].link = NOT_A_LINK; - } - top[i] = i*2; - } - rows = 0; -} - -void TextWindow::Printf(bool halfLine, char *fmt, ...) { - va_list vl; - va_start(vl, fmt); - - if(rows >= MAX_ROWS) return; - - int r, c; - r = rows; - top[r] = (r == 0) ? 0 : (top[r-1] + (halfLine ? 3 : 2)); - rows++; - - for(c = 0; c < MAX_COLS; c++) { - text[r][c] = ' '; - meta[r][c].link = NOT_A_LINK; - } - - int fg = 'd', bg = 'd'; - int link = NOT_A_LINK; - DWORD data = 0; - LinkFunction *f = NULL, *h = NULL; - - c = 0; - while(*fmt) { - char buf[1024]; - - if(*fmt == '%') { - fmt++; - if(*fmt == '\0') goto done; - strcpy(buf, ""); - switch(*fmt) { - case 'd': { - int v = va_arg(vl, int); - sprintf(buf, "%d", v); - break; - } - case 'x': { - DWORD v = va_arg(vl, DWORD); - sprintf(buf, "%08x", v); - break; - } - case '@': { - double v = va_arg(vl, double); - sprintf(buf, "%.2f", v); - break; - } - case '2': { - double v = va_arg(vl, double); - sprintf(buf, "%s%.2f", v < 0 ? "" : " ", v); - break; - } - case '3': { - double v = va_arg(vl, double); - sprintf(buf, "%s%.3f", v < 0 ? "" : " ", v); - break; - } - case '#': { - double v = va_arg(vl, double); - sprintf(buf, "%.3f", v); - break; - } - case 's': { - char *s = va_arg(vl, char *); - memcpy(buf, s, min(sizeof(buf), strlen(s)+1)); - break; - } - case 'c': { - char v = va_arg(vl, char); - if(v == 0) { - strcpy(buf, ""); - } else { - sprintf(buf, "%c", v); - } - break; - } - case 'E': - fg = 'd'; - // leave the background, though - link = NOT_A_LINK; - data = 0; - f = NULL; - h = NULL; - break; - - case 'F': - case 'B': { - int color; - if(fmt[1] == '\0') goto done; - if(fmt[1] == 'p') { - color = va_arg(vl, int); - } else { - color = fmt[1]; - } - if((color < 0 || color > 255) && !(color & 0x80000000)) { - color = 0; - } - if(*fmt == 'F') { - fg = color; - } else { - bg = color; - } - fmt++; - break; - } - case 'L': - if(fmt[1] == '\0') goto done; - fmt++; - if(*fmt == 'p') { - link = va_arg(vl, int); - } else { - link = *fmt; - } - break; - - case 'f': - f = va_arg(vl, LinkFunction *); - break; - - case 'h': - h = va_arg(vl, LinkFunction *); - break; - - case 'D': - data = va_arg(vl, DWORD); - break; - - case '%': - strcpy(buf, "%"); - break; - } - } else { - buf[0] = *fmt; - buf[1]= '\0'; - } - - for(unsigned i = 0; i < strlen(buf); i++) { - if(c >= MAX_COLS) goto done; - text[r][c] = buf[i]; - meta[r][c].fg = fg; - meta[r][c].bg = bg; - meta[r][c].link = link; - meta[r][c].data = data; - meta[r][c].f = f; - meta[r][c].h = h; - c++; - } - - fmt++; - } - while(c < MAX_COLS) { - meta[r][c].fg = fg; - meta[r][c].bg = bg; - c++; - } - -done: - va_end(vl); -} - -#define gs (SS.GW.gs) -void TextWindow::Show(void) { - if(!(SS.GW.pending.operation)) SS.GW.ClearPending(); - - SS.GW.GroupSelection(); - - // Make sure these tests agree with test used to draw indicator line on - // main list of groups screen. - if(SS.GW.pending.description) { - // A pending operation (that must be completed with the mouse in - // the graphics window) will preempt our usual display. - HideEditControl(); - ShowHeader(false); - Printf(false, ""); - Printf(false, "%s", SS.GW.pending.description); - Printf(true, "%Fl%f%Ll(cancel operation)%E", - &TextWindow::ScreenUnselectAll); - } else if((gs.n > 0 || gs.constraints > 0) && - shown.screen != SCREEN_PASTE_TRANSFORMED) - { - if(edit.meaning != EDIT_TTF_TEXT) HideEditControl(); - ShowHeader(false); - DescribeSelection(); - } else { - if(edit.meaning == EDIT_TTF_TEXT) HideEditControl(); - ShowHeader(true); - switch(shown.screen) { - default: - shown.screen = SCREEN_LIST_OF_GROUPS; - // fall through - case SCREEN_LIST_OF_GROUPS: ShowListOfGroups(); break; - case SCREEN_GROUP_INFO: ShowGroupInfo(); break; - case SCREEN_GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break; - case SCREEN_CONFIGURATION: ShowConfiguration(); break; - case SCREEN_STEP_DIMENSION: ShowStepDimension(); break; - case SCREEN_LIST_OF_STYLES: ShowListOfStyles(); break; - case SCREEN_STYLE_INFO: ShowStyleInfo(); break; - case SCREEN_PASTE_TRANSFORMED: ShowPasteTransformed(); break; - case SCREEN_EDIT_VIEW: ShowEditView(); break; - case SCREEN_TANGENT_ARC: ShowTangentArc(); break; - } - } - Printf(false, ""); - - // Make sure there's room for the color picker - if(editControl.colorPicker.show) { - int pickerHeight = 25; - int halfRow = editControl.halfRow; - if(top[rows-1] - halfRow < pickerHeight && rows < MAX_ROWS) { - rows++; - top[rows-1] = halfRow + pickerHeight; - } - } - - InvalidateText(); -} - -void TextWindow::TimerCallback(void) -{ - tooltippedIcon = hoveredIcon; - InvalidateText(); -} - -void TextWindow::DrawOrHitTestIcons(int how, double mx, double my) -{ - int width, height; - GetTextWindowSize(&width, &height); - - int x = 20, y = 33 + LINE_HEIGHT; - y -= scrollPos*(LINE_HEIGHT/2); - - double grey = 30.0/255; - double top = y - 28, bot = y + 4; - glColor4d(grey, grey, grey, 1.0); - glxAxisAlignedQuad(0, width, top, bot); - - HideShowIcon *oldHovered = hoveredIcon; - if(how != PAINT) { - hoveredIcon = NULL; - } - - HideShowIcon *hsi; - for(hsi = &(hideShowIcons[0]); hsi->var; hsi++) { - if(hsi->var == &SPACER) { - // Draw a darker-grey spacer in between the groups of icons. - if(how == PAINT) { - int l = x, r = l + 4, - t = y, b = t - 24; - glColor4d(0.17, 0.17, 0.17, 1); - glxAxisAlignedQuad(l, r, t, b); - } - x += 12; - continue; - } - - if(how == PAINT) { - glPushMatrix(); - glTranslated(x, y-24, 0); - // Only thing that matters about the color is the alpha, - // should be one for no transparency - glColor3d(0, 0, 0); - glxDrawPixelsWithTexture(hsi->icon, 24, 24); - glPopMatrix(); - - if(hsi == hoveredIcon) { - glColor4d(1, 1, 0, 0.3); - glxAxisAlignedQuad(x - 2, x + 26, y + 2, y - 26); - } - if(!*(hsi->var)) { - glColor4d(1, 0, 0, 0.6); - glLineWidth(2); - int s = 0, f = 24; - glBegin(GL_LINES); - glVertex2d(x+s, y-s); - glVertex2d(x+f, y-f); - glVertex2d(x+s, y-f); - glVertex2d(x+f, y-s); - glEnd(); - } - } else { - if(mx > x - 2 && mx < x + 26 && - my < y + 2 && my > y - 26) - { - // The mouse is hovered over this icon, so do the tooltip - // stuff. - if(hsi != tooltippedIcon) { - oldMousePos = Point2d::From(mx, my); - } - if(hsi != oldHovered || how == CLICK) { - SetTimerFor(1000); - } - hoveredIcon = hsi; - if(how == CLICK) { - SS.GW.ToggleBool(hsi->var); - } - } - } - - x += 32; - } - - if(how != PAINT && hoveredIcon != oldHovered) { - InvalidateText(); - } - - if(tooltippedIcon) { - if(how == PAINT) { - char str[1024]; - - if(tooltippedIcon->icon == Icon_faces) { - if(SS.GW.showFaces) { - strcpy(str, "Don't make faces selectable with mouse"); - } else { - strcpy(str, "Make faces selectable with mouse"); - } - } else { - sprintf(str, "%s %s", *(tooltippedIcon->var) ? "Hide" : "Show", - tooltippedIcon->tip); - } - - double ox = oldMousePos.x, oy = oldMousePos.y - LINE_HEIGHT; - ox += 3; - oy -= 3; - int tw = (strlen(str) + 1)*CHAR_WIDTH; - ox = min(ox, (width - 25) - tw); - oy = max(oy, 5); - - glxCreateBitmapFont(); - glLineWidth(1); - glColor4d(1.0, 1.0, 0.6, 1.0); - glxAxisAlignedQuad(ox, ox+tw, oy, oy+LINE_HEIGHT); - glColor4d(0.0, 0.0, 0.0, 1.0); - glxAxisAlignedLineLoop(ox, ox+tw, oy, oy+LINE_HEIGHT); - - glColor4d(0, 0, 0, 1); - glxBitmapText(str, Vector::From(ox+5, oy-3+LINE_HEIGHT, 0)); - } else { - if(!hoveredIcon || - (hoveredIcon != tooltippedIcon)) - { - tooltippedIcon = NULL; - InvalidateGraphics(); - } - // And if we're hovered, then we've set a timer that will cause - // us to show the tool tip later. - } - } -} - -//---------------------------------------------------------------------------- -// Given (x, y, z) = (h, s, v) in [0,6), [0,1], [0,1], return (x, y, z) = -// (r, g, b) all in [0, 1]. -//---------------------------------------------------------------------------- -Vector TextWindow::HsvToRgb(Vector hsv) { - if(hsv.x >= 6) hsv.x -= 6; - - Vector rgb; - double hmod2 = hsv.x; - while(hmod2 >= 2) hmod2 -= 2; - double x = (1 - fabs(hmod2 - 1)); - if(hsv.x < 1) { - rgb = Vector::From(1, x, 0); - } else if(hsv.x < 2) { - rgb = Vector::From(x, 1, 0); - } else if(hsv.x < 3) { - rgb = Vector::From(0, 1, x); - } else if(hsv.x < 4) { - rgb = Vector::From(0, x, 1); - } else if(hsv.x < 5) { - rgb = Vector::From(x, 0, 1); - } else { - rgb = Vector::From(1, 0, x); - } - double c = hsv.y*hsv.z; - double m = 1 - hsv.z; - rgb = rgb.ScaledBy(c); - rgb = rgb.Plus(Vector::From(m, m, m)); - - return rgb; -} - -BYTE *TextWindow::HsvPattern2d(void) { - static BYTE Texture[256*256*3]; - static bool Init; - - if(!Init) { - int i, j, p; - p = 0; - for(i = 0; i < 256; i++) { - for(j = 0; j < 256; j++) { - Vector hsv = Vector::From(6.0*i/255.0, 1.0*j/255.0, 1); - Vector rgb = HsvToRgb(hsv); - rgb = rgb.ScaledBy(255); - Texture[p++] = (BYTE)rgb.x; - Texture[p++] = (BYTE)rgb.y; - Texture[p++] = (BYTE)rgb.z; - } - } - Init = true; - } - return Texture; -} - -BYTE *TextWindow::HsvPattern1d(double h, double s) { - static BYTE Texture[256*4]; - - int i, p; - p = 0; - for(i = 0; i < 256; i++) { - Vector hsv = Vector::From(6*h, s, 1.0*(255 - i)/255.0); - Vector rgb = HsvToRgb(hsv); - rgb = rgb.ScaledBy(255); - Texture[p++] = (BYTE)rgb.x; - Texture[p++] = (BYTE)rgb.y; - Texture[p++] = (BYTE)rgb.z; - // Needs a padding byte, to make things four-aligned - p++; - } - return Texture; -} - -void TextWindow::ColorPickerDone(void) { - char str[1024]; - DWORD rgb = editControl.colorPicker.rgb; - sprintf(str, "%.2f, %.2f, %.3f", REDf(rgb), GREENf(rgb), BLUEf(rgb)); - EditControlDone(str); -} - -bool TextWindow::DrawOrHitTestColorPicker(int how, bool leftDown, - double x, double y) -{ - bool mousePointerAsHand = false; - - if(how == HOVER && !leftDown) { - editControl.colorPicker.picker1dActive = false; - editControl.colorPicker.picker2dActive = false; - } - - if(!editControl.colorPicker.show) return false; - if(how == CLICK || (how == HOVER && leftDown)) InvalidateText(); - - static const DWORD BaseColor[12] = { - RGB(255, 0, 0), - RGB( 0, 255, 0), - RGB( 0, 0, 255), - - RGB( 0, 255, 255), - RGB(255, 0, 255), - RGB(255, 255, 0), - - RGB(255, 127, 0), - RGB(255, 0, 127), - RGB( 0, 255, 127), - RGB(127, 255, 0), - RGB(127, 0, 255), - RGB( 0, 127, 255), - }; - - int width, height; - GetTextWindowSize(&width, &height); - - int px = LEFT_MARGIN + CHAR_WIDTH*editControl.col; - int py = (editControl.halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2); - - py += LINE_HEIGHT + 5; - - static const int WIDTH = 16, HEIGHT = 12; - static const int PITCH = 18, SIZE = 15; - - px = min(px, width - (WIDTH*PITCH + 40)); - - int pxm = px + WIDTH*PITCH + 11, - pym = py + HEIGHT*PITCH + 7; - - int bw = 6; - if(how == PAINT) { - glColor4d(0.2, 0.2, 0.2, 1); - glxAxisAlignedQuad(px, pxm+bw, py, pym+bw); - glColor4d(0.0, 0.0, 0.0, 1); - glxAxisAlignedQuad(px+(bw/2), pxm+(bw/2), py+(bw/2), pym+(bw/2)); - } else { - if(x < px || x > pxm+(bw/2) || - y < py || y > pym+(bw/2)) - { - return false; - } - } - px += (bw/2); - py += (bw/2); - - int i, j; - for(i = 0; i < WIDTH/2; i++) { - for(j = 0; j < HEIGHT; j++) { - Vector rgb; - DWORD d; - if(i == 0 && j < 8) { - d = SS.modelColor[j]; - rgb = Vector::From(REDf(d), GREENf(d), BLUEf(d)); - } else if(i == 0) { - double a = (j - 8.0)/3.0; - rgb = Vector::From(a, a, a); - } else { - d = BaseColor[j]; - rgb = Vector::From(REDf(d), GREENf(d), BLUEf(d)); - if(i >= 2 && i <= 4) { - double a = (i == 2) ? 0.2 : (i == 3) ? 0.3 : 0.4; - rgb = rgb.Plus(Vector::From(a, a, a)); - } - if(i >= 5 && i <= 7) { - double a = (i == 5) ? 0.7 : (i == 6) ? 0.4 : 0.18; - rgb = rgb.ScaledBy(a); - } - } - - rgb = rgb.ClampWithin(0, 1); - int sx = px + 5 + PITCH*(i + 8) + 4, sy = py + 5 + PITCH*j; - - if(how == PAINT) { - glColor4d(CO(rgb), 1); - glxAxisAlignedQuad(sx, sx+SIZE, sy, sy+SIZE); - } else if(how == CLICK) { - if(x >= sx && x <= sx+SIZE && y >= sy && y <= sy+SIZE) { - editControl.colorPicker.rgb = RGBf(rgb.x, rgb.y, rgb.z); - ColorPickerDone(); - } - } else if(how == HOVER) { - if(x >= sx && x <= sx+SIZE && y >= sy && y <= sy+SIZE) { - mousePointerAsHand = true; - } - } - } - } - - int hxm, hym; - int hx = px + 5, hy = py + 5; - hxm = hx + PITCH*7 + SIZE; - hym = hy + PITCH*2 + SIZE; - if(how == PAINT) { - glxColorRGB(editControl.colorPicker.rgb); - glxAxisAlignedQuad(hx, hxm, hy, hym); - } else if(how == CLICK) { - if(x >= hx && x <= hxm && y >= hy && y <= hym) { - ColorPickerDone(); - } - } else if(how == HOVER) { - if(x >= hx && x <= hxm && y >= hy && y <= hym) { - mousePointerAsHand = true; - } - } - - hy += PITCH*3; - - hxm = hx + PITCH*7 + SIZE; - hym = hy + PITCH*1 + SIZE; - // The one-dimensional thing to pick the color's value - if(how == PAINT) { - glBindTexture(GL_TEXTURE_2D, TEXTURE_COLOR_PICKER_1D); - 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, 1, 256, 0, - GL_RGB, GL_UNSIGNED_BYTE, - HsvPattern1d(editControl.colorPicker.h, - editControl.colorPicker.s)); - - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - glTexCoord2d(0, 0); - glVertex2d(hx, hy); - - glTexCoord2d(1, 0); - glVertex2d(hx, hym); - - glTexCoord2d(1, 1); - glVertex2d(hxm, hym); - - glTexCoord2d(0, 1); - glVertex2d(hxm, hy); - glEnd(); - glDisable(GL_TEXTURE_2D); - - double cx = hx+(hxm-hx)*(1 - editControl.colorPicker.v); - glColor4d(0, 0, 0, 1); - glLineWidth(1); - glBegin(GL_LINES); - glVertex2d(cx, hy); - glVertex2d(cx, hym); - glEnd(); - glEnd(); - } else if(how == CLICK || - (how == HOVER && leftDown && editControl.colorPicker.picker1dActive)) - { - if(x >= hx && x <= hxm && y >= hy && y <= hym) { - editControl.colorPicker.v = 1 - (x - hx)/(hxm - hx); - - Vector rgb = HsvToRgb(Vector::From( - 6*editControl.colorPicker.h, - editControl.colorPicker.s, - editControl.colorPicker.v)); - editControl.colorPicker.rgb = RGBf(rgb.x, rgb.y, rgb.z); - - editControl.colorPicker.picker1dActive = true; - } - } - // and advance our vertical position - hy += PITCH*2; - - hxm = hx + PITCH*7 + SIZE; - hym = hy + PITCH*6 + SIZE; - // Two-dimensional thing to pick a color by hue and saturation - if(how == PAINT) { - glBindTexture(GL_TEXTURE_2D, TEXTURE_COLOR_PICKER_2D); - 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, 256, 256, 0, - GL_RGB, GL_UNSIGNED_BYTE, HsvPattern2d()); - - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - glTexCoord2d(0, 0); - glVertex2d(hx, hy); - - glTexCoord2d(1, 0); - glVertex2d(hx, hym); - - glTexCoord2d(1, 1); - glVertex2d(hxm, hym); - - glTexCoord2d(0, 1); - glVertex2d(hxm, hy); - glEnd(); - glDisable(GL_TEXTURE_2D); - - glColor4d(1, 1, 1, 1); - glLineWidth(1); - double cx = hx+(hxm-hx)*editControl.colorPicker.h, - cy = hy+(hym-hy)*editControl.colorPicker.s; - glBegin(GL_LINES); - glVertex2d(cx - 5, cy); - glVertex2d(cx + 4, cy); - glVertex2d(cx, cy - 5); - glVertex2d(cx, cy + 4); - glEnd(); - } else if(how == CLICK || - (how == HOVER && leftDown && editControl.colorPicker.picker2dActive)) - { - if(x >= hx && x <= hxm && y >= hy && y <= hym) { - double h = (x - hx)/(hxm - hx), - s = (y - hy)/(hym - hy); - editControl.colorPicker.h = h; - editControl.colorPicker.s = s; - - Vector rgb = HsvToRgb(Vector::From( - 6*editControl.colorPicker.h, - editControl.colorPicker.s, - editControl.colorPicker.v)); - editControl.colorPicker.rgb = RGBf(rgb.x, rgb.y, rgb.z); - - editControl.colorPicker.picker2dActive = true; - } - } - - SetMousePointerToHand(mousePointerAsHand); - return true; -} - -void TextWindow::Paint(void) { - int width, height; - GetTextWindowSize(&width, &height); - - // We would like things pixel-exact, to avoid shimmering. - glViewport(0, 0, width, height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glColor3d(1, 1, 1); - - glTranslated(-1, 1, 0); - glScaled(2.0/width, -2.0/height, 1); - // Make things round consistently, avoiding exact integer boundary - glTranslated(-0.1, -0.1, 0); - - halfRows = height / (LINE_HEIGHT/2); - - int bottom = top[rows-1] + 2; - scrollPos = min(scrollPos, bottom - halfRows); - scrollPos = max(scrollPos, 0); - - // Let's set up the scroll bar first - MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows); - - // Create the bitmap font that we're going to use. - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); - - // Now paint the window. - int r, c, a; - for(a = 0; a < 2; a++) { - if(a == 0) { - glBegin(GL_QUADS); - } else if(a == 1) { - glEnable(GL_TEXTURE_2D); - glxCreateBitmapFont(); - glBegin(GL_QUADS); - } - - for(r = 0; r < rows; r++) { - int ltop = top[r]; - if(ltop < (scrollPos-1)) continue; - if(ltop > scrollPos+halfRows) break; - - for(c = 0; c < min((width/CHAR_WIDTH)+1, MAX_COLS); c++) { - int x = LEFT_MARGIN + c*CHAR_WIDTH; - int y = (ltop-scrollPos)*(LINE_HEIGHT/2) + 4; - - int fg = meta[r][c].fg; - int bg = meta[r][c].bg; - - // On the first pass, all the background quads; on the next - // pass, all the foreground (i.e., font) quads. - if(a == 0) { - int bh = LINE_HEIGHT, adj = -2; - if(bg & 0x80000000) { - glColor3f(REDf(bg), GREENf(bg), BLUEf(bg)); - bh = CHAR_HEIGHT; - adj += 2; - } else { - glColor3fv(&(bgColorTable[bg*3])); - } - - if(!(bg == 'd')) { - // Move the quad down a bit, so that the descenders - // still have the correct background. - y += adj; - glxAxisAlignedQuad(x, x + CHAR_WIDTH, y, y + bh); - y -= adj; - } - } else if(a == 1) { - glColor3fv(&(fgColorTable[fg*3])); - glxBitmapCharQuad(text[r][c], x, y + CHAR_HEIGHT); - - // If this is a link and it's hovered, then draw the - // underline - if(meta[r][c].link && meta[r][c].link != 'n' && - (r == hoveredRow && c == hoveredCol)) - { - int cs = c, cf = c; - while(cs >= 0 && meta[r][cs].link && - meta[r][cs].f == meta[r][c].f && - meta[r][cs].data == meta[r][c].data) - { - cs--; - } - cs++; - - while( meta[r][cf].link && - meta[r][cf].f == meta[r][c].f && - meta[r][cf].data == meta[r][c].data) - { - cf++; - } - - // But don't underline checkboxes or radio buttons - while((text[r][cs] & 0x80 || text[r][cs] == ' ') && - cs < cf) - { - cs++; - } - - glEnd(); - - // Always use the color of the rightmost character - // in the link, so that underline is consistent color - fg = meta[r][cf-1].fg; - glColor3fv(&(fgColorTable[fg*3])); - glDisable(GL_TEXTURE_2D); - glLineWidth(1); - glBegin(GL_LINES); - int yp = y + CHAR_HEIGHT; - glVertex2d(LEFT_MARGIN + cs*CHAR_WIDTH, yp); - glVertex2d(LEFT_MARGIN + cf*CHAR_WIDTH, yp); - glEnd(); - - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - } - } - } - } - - glEnd(); - glDisable(GL_TEXTURE_2D); - } - - // The line to indicate the column of radio buttons that indicates the - // active group. - SS.GW.GroupSelection(); - // Make sure this test agrees with test to determine which screen is drawn - if(!SS.GW.pending.description && gs.n == 0 && gs.constraints == 0 && - shown.screen == SCREEN_LIST_OF_GROUPS) - { - int x = 29, y = 70 + LINE_HEIGHT; - y -= scrollPos*(LINE_HEIGHT/2); - - glLineWidth(1); - glColor3fv(&(fgColorTable['t'*3])); - glBegin(GL_LINES); - glVertex2d(x, y); - glVertex2d(x, y+40); - glEnd(); - } - - // The header has some icons that are drawn separately from the text - DrawOrHitTestIcons(PAINT, 0, 0); - - // And we may show a color picker for certain editable fields - DrawOrHitTestColorPicker(PAINT, false, 0, 0); -} - -void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) { - if(TextEditControlIsVisible() || GraphicsEditControlIsVisible()) { - if(DrawOrHitTestColorPicker(leftClick ? CLICK : HOVER, leftDown, x, y)) - { - return; - } - - if(leftClick) { - HideEditControl(); - HideGraphicsEditControl(); - } else { - SetMousePointerToHand(false); - } - return; - } - - DrawOrHitTestIcons(leftClick ? CLICK : HOVER, x, y); - - GraphicsWindow::Selection ps = SS.GW.hover; - SS.GW.hover.Clear(); - - int prevHoveredRow = hoveredRow, - prevHoveredCol = hoveredCol; - hoveredRow = 0; - hoveredCol = 0; - - // Find the corresponding character in the text buffer - int c = (int)((x - LEFT_MARGIN) / CHAR_WIDTH); - int hh = (LINE_HEIGHT)/2; - y += scrollPos*hh; - int r; - for(r = 0; r < rows; r++) { - if(y >= top[r]*hh && y <= (top[r]+2)*hh) { - break; - } - } - if(r >= rows) { - SetMousePointerToHand(false); - goto done; - } - - hoveredRow = r; - hoveredCol = c; - -#define META (meta[r][c]) - if(leftClick) { - if(META.link && META.f) { - (META.f)(META.link, META.data); - Show(); - InvalidateGraphics(); - } - } else { - if(META.link) { - SetMousePointerToHand(true); - if(META.h) { - (META.h)(META.link, META.data); - } - } else { - SetMousePointerToHand(false); - } - } - -done: - if((!ps.Equals(&(SS.GW.hover))) || - prevHoveredRow != hoveredRow || - prevHoveredCol != hoveredCol) - { - InvalidateGraphics(); - InvalidateText(); - } -} - -void TextWindow::MouseLeave(void) { - tooltippedIcon = NULL; - hoveredIcon = NULL; - hoveredRow = 0; - hoveredCol = 0; - InvalidateText(); -} - -void TextWindow::ScrollbarEvent(int newPos) { - int bottom = top[rows-1] + 2; - newPos = min(newPos, bottom - halfRows); - newPos = max(newPos, 0); - - if(newPos != scrollPos) { - scrollPos = newPos; - MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows); - - if(TextEditControlIsVisible()) { - ShowEditControl(editControl.halfRow, editControl.col, NULL); - } - InvalidateText(); - } -} - diff --git a/toolbar.cpp b/toolbar.cpp deleted file mode 100644 index 088e50b..0000000 --- a/toolbar.cpp +++ /dev/null @@ -1,257 +0,0 @@ -//----------------------------------------------------------------------------- -// The toolbar that appears at the top left of the graphics window, where the -// user can select icons with the mouse, to perform operations equivalent to -// selecting a menu item or using a keyboard shortcut. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" -#include "obj/icons.h" - -BYTE SPACER[1]; -static const struct { - BYTE *image; - int menu; - char *tip; -} Toolbar[] = { - { Icon_line, GraphicsWindow::MNU_LINE_SEGMENT, "Sketch line segment" }, - { Icon_rectangle, GraphicsWindow::MNU_RECTANGLE, "Sketch rectangle" }, - { Icon_circle, GraphicsWindow::MNU_CIRCLE, "Sketch circle" }, - { Icon_arc, GraphicsWindow::MNU_ARC, "Sketch arc of a circle" }, - { Icon_text, GraphicsWindow::MNU_TTF_TEXT, "Sketch curves from text in a TrueType font" }, - { Icon_tangent_arc, GraphicsWindow::MNU_TANGENT_ARC, "Create tangent arc at selected point" }, - { Icon_bezier, GraphicsWindow::MNU_CUBIC, "Sketch cubic Bezier spline" }, - { Icon_point, GraphicsWindow::MNU_DATUM_POINT, "Sketch datum point" }, - { Icon_construction, GraphicsWindow::MNU_CONSTRUCTION, "Toggle construction" }, - { Icon_trim, GraphicsWindow::MNU_SPLIT_CURVES, "Split lines / curves where they intersect" }, - { SPACER }, - - { Icon_length, GraphicsWindow::MNU_DISTANCE_DIA, "Constrain distance / diameter / length" }, - { Icon_angle, GraphicsWindow::MNU_ANGLE, "Constrain angle" }, - { Icon_horiz, GraphicsWindow::MNU_HORIZONTAL, "Constrain to be horizontal" }, - { Icon_vert, GraphicsWindow::MNU_VERTICAL, "Constrain to be vertical" }, - { Icon_parallel, GraphicsWindow::MNU_PARALLEL, "Constrain to be parallel or tangent" }, - { Icon_perpendicular, GraphicsWindow::MNU_PERPENDICULAR, "Constrain to be perpendicular" }, - { Icon_pointonx, GraphicsWindow::MNU_ON_ENTITY, "Constrain point on line / curve / plane / point" }, - { Icon_symmetric, GraphicsWindow::MNU_SYMMETRIC, "Constrain symmetric" }, - { Icon_equal, GraphicsWindow::MNU_EQUAL, "Constrain equal length / radius / angle" }, - { Icon_same_orientation,GraphicsWindow::MNU_ORIENTED_SAME, "Constrain normals in same orientation" }, - { Icon_other_supp, GraphicsWindow::MNU_OTHER_ANGLE, "Other supplementary angle" }, - { Icon_ref, GraphicsWindow::MNU_REFERENCE, "Toggle reference dimension" }, - { SPACER }, - - { Icon_extrude, GraphicsWindow::MNU_GROUP_EXTRUDE, "New group extruding active sketch" }, - { Icon_sketch_in_plane, GraphicsWindow::MNU_GROUP_WRKPL, "New group in new workplane (thru given entities)" }, - { Icon_step_rotate, GraphicsWindow::MNU_GROUP_ROT, "New group step and repeat rotating" }, - { Icon_step_translate, GraphicsWindow::MNU_GROUP_TRANS, "New group step and repeat translating" }, - { Icon_sketch_in_3d, GraphicsWindow::MNU_GROUP_3D, "New group in 3d" }, - { Icon_assemble, GraphicsWindow::MNU_GROUP_IMPORT, "New group importing / assembling file" }, - { SPACER }, - - { Icon_in3d, GraphicsWindow::MNU_NEAREST_ISO, "Nearest isometric view" }, - { Icon_ontoworkplane, GraphicsWindow::MNU_ONTO_WORKPLANE, "Align view to active workplane" }, - { NULL }, -}; - -void GraphicsWindow::ToolbarDraw(void) { - ToolbarDrawOrHitTest(0, 0, true, NULL); -} - -bool GraphicsWindow::ToolbarMouseMoved(int x, int y) { - x += ((int)width/2); - y += ((int)height/2); - - int nh; - bool withinToolbar = ToolbarDrawOrHitTest(x, y, false, &nh); - if(!withinToolbar) nh = 0; - - if(nh != toolbarTooltipped) { - // Don't let the tool tip move around if the mouse moves within the - // same item. - toolbarMouseX = x; - toolbarMouseY = y; - toolbarTooltipped = 0; - } - - if(nh != toolbarHovered) { - toolbarHovered = nh; - SetTimerFor(1000); - PaintGraphics(); - } - // So if we moved off the toolbar, then toolbarHovered is now equal to - // zero, so it doesn't matter if the tool tip timer expires. And if - // we moved from one item to another, we reset the timer, so also okay. - return withinToolbar; -} - -bool GraphicsWindow::ToolbarMouseDown(int x, int y) { - x += ((int)width/2); - y += ((int)height/2); - - int nh = -1; - bool withinToolbar = ToolbarDrawOrHitTest(x, y, false, &nh); - // They might have clicked within the toolbar, but not on a button. - if(withinToolbar && nh >= 0) { - for(int i = 0; SS.GW.menu[i].level >= 0; i++) { - if(nh == SS.GW.menu[i].id) { - (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)SS.GW.menu[i].id); - break; - } - } - } - return withinToolbar; -} - -bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my, - bool paint, int *menu) -{ - int i; - int x = 17, y = (int)(height - 52); - - int fudge = 8; - int h = 32*15 + 3*16 + fudge; - int aleft = 0, aright = 66, atop = y+16+fudge/2, abot = y+16-h; - - bool withinToolbar = - (mx >= aleft && mx <= aright && my <= atop && my >= abot); - - if(!paint && !withinToolbar) { - // This gets called every MouseMove event, so return quickly. - return false; - } - - if(paint) { - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glTranslated(-1, -1, 0); - glScaled(2.0/width, 2.0/height, 0); - glDisable(GL_LIGHTING); - - double c = 30.0/255; - glColor4d(c, c, c, 1.0); - glxAxisAlignedQuad(aleft, aright, atop, abot); - } - - struct { - bool show; - char *str; - } toolTip = { false, NULL }; - - bool leftpos = true; - for(i = 0; Toolbar[i].image; i++) { - if(Toolbar[i].image == SPACER) { - if(!leftpos) { - leftpos = true; - y -= 32; - x -= 32; - } - y -= 16; - - if(paint) { - // Draw a separator bar in a slightly different color. - int divw = 30, divh = 2; - glColor4d(0.17, 0.17, 0.17, 1); - x += 16; - y += 24; - glxAxisAlignedQuad(x+divw, x-divw, y+divh, y-divh); - x -= 16; - y -= 24; - } - - continue; - } - - if(paint) { - glRasterPos2i(x - 12, y - 12); - glDrawPixels(24, 24, GL_RGB, GL_UNSIGNED_BYTE, Toolbar[i].image); - - if(toolbarHovered == Toolbar[i].menu) { - // Highlight the hovered or pending item. - glColor4d(1, 1, 0, 0.3); - int boxhw = 15; - glxAxisAlignedQuad(x+boxhw, x-boxhw, y+boxhw, y-boxhw); - } - - if(toolbarTooltipped == Toolbar[i].menu) { - // Display the tool tip for this item; postpone till later - // so that no one draws over us. Don't need position since - // that's just wherever the mouse is. - toolTip.show = true; - toolTip.str = Toolbar[i].tip; - } - } else { - int boxhw = 16; - if(mx < (x+boxhw) && mx > (x - boxhw) && - my < (y+boxhw) && my > (y - boxhw)) - { - if(menu) *menu = Toolbar[i].menu; - } - } - - if(leftpos) { - x += 32; - leftpos = false; - } else { - x -= 32; - y -= 32; - leftpos = true; - } - } - - if(paint) { - // Do this last so that nothing can draw over it. - if(toolTip.show) { - glxCreateBitmapFont(); - char str[1024]; - if(strlen(toolTip.str) >= 200) oops(); - strcpy(str, toolTip.str); - - for(i = 0; SS.GW.menu[i].level >= 0; i++) { - if(toolbarTooltipped == SS.GW.menu[i].id) { - int accel = SS.GW.menu[i].accel; - int ac = accel & 0xff; - - char *s = str+strlen(str); - if(isalnum(ac) || ac == '[') { - if(accel & 0x100) { - sprintf(s, " (Shift+%c)", ac); - } else if((accel & ~0xff) == 0) { - sprintf(s, " (%c)", ac); - } - } else if(ac == 0xf3) { - sprintf(s, " (F3)"); - } - break; - } - } - - int tw = strlen(str)*SS.TW.CHAR_WIDTH + 10, - th = SS.TW.LINE_HEIGHT + 2; - - double ox = toolbarMouseX + 3, oy = toolbarMouseY + 3; - glLineWidth(1); - glColor4d(1.0, 1.0, 0.6, 1.0); - glxAxisAlignedQuad(ox, ox+tw, oy, oy+th); - glColor4d(0.0, 0.0, 0.0, 1.0); - glxAxisAlignedLineLoop(ox, ox+tw, oy, oy+th); - - glColor4d(0, 0, 0, 1); - glPushMatrix(); - glTranslated(ox+5, oy+3, 0); - glScaled(1, -1, 1); - glxBitmapText(str, Vector::From(0, 0, 0)); - glPopMatrix(); - } - glxDepthRangeLockToFront(false); - } - - return withinToolbar; -} - -void GraphicsWindow::TimerCallback(void) { - SS.GW.toolbarTooltipped = SS.GW.toolbarHovered; - PaintGraphics(); -} - diff --git a/tools/Makefile b/tools/Makefile deleted file mode 100644 index dd236a9..0000000 --- a/tools/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -all: - cl ttf2c.cpp user32.lib gdi32.lib comctl32.lib - ttf2c.exe > ..\bitmapfont.table - diff --git a/tools/ttf2c.cpp b/tools/ttf2c.cpp deleted file mode 100644 index 2e11dfa..0000000 --- a/tools/ttf2c.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include - -//----------------------------------------------------------------------------- -// Entry point into the program. -//----------------------------------------------------------------------------- -int main(void) -{ - InitCommonControls(); - - // A monospaced font - HFONT font = CreateFont(16, 9, 0, 0, FW_REGULAR, FALSE, - FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, FF_DONTCARE, "Lucida Console"); - - HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL); - HBITMAP bitmap = CreateCompatibleBitmap(hdc, 30, 30); - - SelectObject(hdc, bitmap); - SelectObject(hdc, font); - - printf("static const BYTE FontTexture[256*16*16] = {\n"); - - int c; - for(c = 0; c < 128; c++) { - - RECT r; - r.left = 0; r.top = 0; - r.right = 30; r.bottom = 30; - FillRect(hdc, &r, (HBRUSH)GetStockObject(BLACK_BRUSH)); - - SetBkColor(hdc, RGB(0, 0, 0)); - SetTextColor(hdc, RGB(255, 255, 255)); - char str[2] = { c, 0 }; - TextOut(hdc, 0, 0, str, 1); - - int i, j; - for(i = 0; i < 16; i++) { - for(j = 0; j < 16; j++) { - COLORREF c = GetPixel(hdc, i, j); - printf("%3d, ", c ? 255 : 0); - } - printf("\n"); - } - printf("\n"); - } - printf("#include \"bitmapextra.table\"\n"); - printf("};\n"); - - return 0; -} diff --git a/ttf.cpp b/ttf.cpp deleted file mode 100644 index 4c5060d..0000000 --- a/ttf.cpp +++ /dev/null @@ -1,710 +0,0 @@ -//----------------------------------------------------------------------------- -// Routines to read a TrueType font as vector outlines, and generate them -// as entities, since they're always representable as either lines or -// quadratic Bezier curves. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -//----------------------------------------------------------------------------- -// Get the list of available font filenames, and load the name for each of -// them. Only that, though, not the glyphs too. -//----------------------------------------------------------------------------- -void TtfFontList::LoadAll(void) { - if(loaded) return; - - // Get the list of font files from the platform-specific code. - LoadAllFontFiles(); - - int i; - for(i = 0; i < l.n; i++) { - TtfFont *tf = &(l.elem[i]); - tf->LoadFontFromFile(true); - } - - loaded = true; -} - -void TtfFontList::PlotString(char *font, char *str, double spacing, - SBezierList *sbl, - Vector origin, Vector u, Vector v) -{ - LoadAll(); - - int i; - for(i = 0; i < l.n; i++) { - TtfFont *tf = &(l.elem[i]); - if(strcmp(tf->FontFileBaseName(), font)==0) { - tf->LoadFontFromFile(false); - tf->PlotString(str, spacing, sbl, origin, u, v); - return; - } - } - - // Couldn't find the font; so draw a big X for an error marker. - SBezier sb; - sb = SBezier::From(origin, origin.Plus(u).Plus(v)); - sbl->l.Add(&sb); - sb = SBezier::From(origin.Plus(v), origin.Plus(u)); - sbl->l.Add(&sb); -} - - -//============================================================================= - -//----------------------------------------------------------------------------- -// Get a single character from the open .ttf file; EOF is an error, since -// we can always see that coming. -//----------------------------------------------------------------------------- -int TtfFont::Getc(void) { - int c = fgetc(fh); - if(c < 0) { - throw "EOF"; - } - return c; -} - -//----------------------------------------------------------------------------- -// Helpers to get 1, 2, or 4 bytes from the .ttf file. Big endian. -//----------------------------------------------------------------------------- -int TtfFont::GetBYTE(void) { - return Getc(); -} -int TtfFont::GetWORD(void) { - BYTE b0, b1; - b1 = Getc(); - b0 = Getc(); - - return (b1 << 8) | b0; -} -int TtfFont::GetDWORD(void) { - BYTE b0, b1, b2, b3; - b3 = Getc(); - b2 = Getc(); - b1 = Getc(); - b0 = Getc(); - - return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; -} - -//----------------------------------------------------------------------------- -// Load a glyph from the .ttf file into memory. Assumes that the .ttf file -// is already seeked to the correct location, and writes the result to -// glyphs[index] -//----------------------------------------------------------------------------- -void TtfFont::LoadGlyph(int index) { - if(index < 0 || index >= glyphs) return; - - int i; - - SWORD contours = GetWORD(); - SWORD xMin = GetWORD(); - SWORD yMin = GetWORD(); - SWORD xMax = GetWORD(); - SWORD yMax = GetWORD(); - - if(useGlyph['A'] == index) { - scale = (1024*1024) / yMax; - } - - if(contours > 0) { - WORD *endPointsOfContours = - (WORD *)AllocTemporary(contours*sizeof(WORD)); - - for(i = 0; i < contours; i++) { - endPointsOfContours[i] = GetWORD(); - } - WORD totalPts = endPointsOfContours[i-1] + 1; - - WORD instructionLength = GetWORD(); - for(i = 0; i < instructionLength; i++) { - // We can ignore the instructions, since we're doing vector - // output. - (void)GetBYTE(); - } - - BYTE *flags = (BYTE *)AllocTemporary(totalPts*sizeof(BYTE)); - SWORD *x = (SWORD *)AllocTemporary(totalPts*sizeof(SWORD)); - SWORD *y = (SWORD *)AllocTemporary(totalPts*sizeof(SWORD)); - - // Flags, that indicate format of the coordinates -#define FLAG_ON_CURVE (1 << 0) -#define FLAG_DX_IS_BYTE (1 << 1) -#define FLAG_DY_IS_BYTE (1 << 2) -#define FLAG_REPEAT (1 << 3) -#define FLAG_X_IS_SAME (1 << 4) -#define FLAG_X_IS_POSITIVE (1 << 4) -#define FLAG_Y_IS_SAME (1 << 5) -#define FLAG_Y_IS_POSITIVE (1 << 5) - for(i = 0; i < totalPts; i++) { - flags[i] = GetBYTE(); - if(flags[i] & FLAG_REPEAT) { - int n = GetBYTE(); - BYTE f = flags[i]; - int j; - for(j = 0; j < n; j++) { - i++; - if(i >= totalPts) { - throw "too many points in glyph"; - } - flags[i] = f; - } - } - } - - // x coordinates - SWORD xa = 0; - for(i = 0; i < totalPts; i++) { - if(flags[i] & FLAG_DX_IS_BYTE) { - BYTE v = GetBYTE(); - if(flags[i] & FLAG_X_IS_POSITIVE) { - xa += v; - } else { - xa -= v; - } - } else { - if(flags[i] & FLAG_X_IS_SAME) { - // no change - } else { - SWORD d = GetWORD(); - xa += d; - } - } - x[i] = xa; - } - - // y coordinates - SWORD ya = 0; - for(i = 0; i < totalPts; i++) { - if(flags[i] & FLAG_DY_IS_BYTE) { - BYTE v = GetBYTE(); - if(flags[i] & FLAG_Y_IS_POSITIVE) { - ya += v; - } else { - ya -= v; - } - } else { - if(flags[i] & FLAG_Y_IS_SAME) { - // no change - } else { - SWORD d = GetWORD(); - ya += d; - } - } - y[i] = ya; - } - - Glyph *g = &(glyph[index]); - g->pt = (FontPoint *)MemAlloc(totalPts*sizeof(FontPoint)); - int contour = 0; - for(i = 0; i < totalPts; i++) { - g->pt[i].x = x[i]; - g->pt[i].y = y[i]; - g->pt[i].onCurve = (BYTE)(flags[i] & FLAG_ON_CURVE); - - if(i == endPointsOfContours[contour]) { - g->pt[i].lastInContour = true; - contour++; - } else { - g->pt[i].lastInContour = false; - } - } - g->pts = totalPts; - g->xMax = xMax; - g->xMin = xMin; - - } else { - // This is a composite glyph, TODO. - } -} - -//----------------------------------------------------------------------------- -// Return the basename of our font filename; that's how the requests and -// entities that reference us will store it. -//----------------------------------------------------------------------------- -char *TtfFont::FontFileBaseName(void) { - char *sb = strrchr(fontFile, '\\'); - char *sf = strrchr(fontFile, '/'); - char *s = sf ? sf : sb; - if(!s) return ""; - return s + 1; -} - -//----------------------------------------------------------------------------- -// Load a TrueType font into memory. We care about the curves that define -// the letter shapes, and about the mappings that determine which glyph goes -// with which character. -//----------------------------------------------------------------------------- -bool TtfFont::LoadFontFromFile(bool nameOnly) { - if(loaded) return true; - - int i; - - fh = fopen(fontFile, "rb"); - if(!fh) { - return false; - } - - try { - // First, load the Offset Table - DWORD version = GetDWORD(); - WORD numTables = GetWORD(); - WORD searchRange = GetWORD(); - WORD entrySelector = GetWORD(); - WORD rangeShift = GetWORD(); - - // Now load the Table Directory; our goal in doing this will be to - // find the addresses of the tables that we will need. - DWORD glyfAddr = -1, glyfLen; - DWORD cmapAddr = -1, cmapLen; - DWORD headAddr = -1, headLen; - DWORD locaAddr = -1, locaLen; - DWORD maxpAddr = -1, maxpLen; - DWORD nameAddr = -1, nameLen; - DWORD hmtxAddr = -1, hmtxLen; - DWORD hheaAddr = -1, hheaLen; - - for(i = 0; i < numTables; i++) { - char tag[5] = "xxxx"; - tag[0] = GetBYTE(); - tag[1] = GetBYTE(); - tag[2] = GetBYTE(); - tag[3] = GetBYTE(); - DWORD checksum = GetDWORD(); - DWORD offset = GetDWORD(); - DWORD length = GetDWORD(); - - if(strcmp(tag, "glyf")==0) { - glyfAddr = offset; - glyfLen = length; - } else if(strcmp(tag, "cmap")==0) { - cmapAddr = offset; - cmapLen = length; - } else if(strcmp(tag, "head")==0) { - headAddr = offset; - headLen = length; - } else if(strcmp(tag, "loca")==0) { - locaAddr = offset; - locaLen = length; - } else if(strcmp(tag, "maxp")==0) { - maxpAddr = offset; - maxpLen = length; - } else if(strcmp(tag, "name")==0) { - nameAddr = offset; - nameLen = length; - } else if(strcmp(tag, "hhea")==0) { - hheaAddr = offset; - hheaLen = length; - } else if(strcmp(tag, "hmtx")==0) { - hmtxAddr = offset; - hmtxLen = length; - } - } - - if(glyfAddr == -1 || cmapAddr == -1 || headAddr == -1 || - locaAddr == -1 || maxpAddr == -1 || hmtxAddr == -1 || - nameAddr == -1 || hheaAddr == -1) - { - throw "missing table addr"; - } - - // Load the name table. This gives us display names for the font, which - // we need when we're giving the user a list to choose from. - fseek(fh, nameAddr, SEEK_SET); - - WORD nameFormat = GetWORD(); - WORD nameCount = GetWORD(); - WORD nameStringOffset = GetWORD(); - // And now we're at the name records. Go through those till we find - // one that we want. - int displayNameOffset, displayNameLength; - for(i = 0; i < nameCount; i++) { - WORD platformID = GetWORD(); - WORD encodingID = GetWORD(); - WORD languageID = GetWORD(); - WORD nameId = GetWORD(); - WORD length = GetWORD(); - WORD offset = GetWORD(); - - if(nameId == 4) { - displayNameOffset = offset; - displayNameLength = length; - break; - } - } - if(nameOnly && i >= nameCount) { - throw "no name"; - } - - if(nameOnly) { - // Find the display name, and store it in the provided buffer. - fseek(fh, nameAddr+nameStringOffset+displayNameOffset, SEEK_SET); - int c = 0; - for(i = 0; i < displayNameLength; i++) { - BYTE b = GetBYTE(); - if(b && c < (sizeof(name.str) - 2)) { - name.str[c++] = b; - } - } - name.str[c++] = '\0'; - - fclose(fh); - return true; - } - - - // Load the head table; we need this to determine the format of the - // loca table, 16- or 32-bit entries - fseek(fh, headAddr, SEEK_SET); - - DWORD headVersion = GetDWORD(); - DWORD headFontRevision = GetDWORD(); - DWORD headCheckSumAdj = GetDWORD(); - DWORD headMagicNumber = GetDWORD(); - WORD headFlags = GetWORD(); - WORD headUnitsPerEm = GetWORD(); - (void)GetDWORD(); // created time - (void)GetDWORD(); - (void)GetDWORD(); // modified time - (void)GetDWORD(); - WORD headXmin = GetWORD(); - WORD headYmin = GetWORD(); - WORD headXmax = GetWORD(); - WORD headYmax = GetWORD(); - WORD headMacStyle = GetWORD(); - WORD headLowestRecPPEM = GetWORD(); - WORD headFontDirectionHint = GetWORD(); - WORD headIndexToLocFormat = GetWORD(); - WORD headGlyphDataFormat = GetWORD(); - - if(headMagicNumber != 0x5F0F3CF5) { - throw "bad magic number"; - } - - // Load the hhea table, which contains the number of entries in the - // horizontal metrics (hmtx) table. - fseek(fh, hheaAddr, SEEK_SET); - DWORD hheaVersion = GetDWORD(); - WORD hheaAscender = GetWORD(); - WORD hheaDescender = GetWORD(); - WORD hheaLineGap = GetWORD(); - WORD hheaAdvanceWidthMax = GetWORD(); - WORD hheaMinLsb = GetWORD(); - WORD hheaMinRsb = GetWORD(); - WORD hheaXMaxExtent = GetWORD(); - WORD hheaCaretSlopeRise = GetWORD(); - WORD hheaCaretSlopeRun = GetWORD(); - WORD hheaCaretOffset = GetWORD(); - (void)GetWORD(); - (void)GetWORD(); - (void)GetWORD(); - (void)GetWORD(); - WORD hheaMetricDataFormat = GetWORD(); - WORD hheaNumberOfMetrics = GetWORD(); - - // Load the maxp table, which determines (among other things) the number - // of glyphs in the font - fseek(fh, maxpAddr, SEEK_SET); - - DWORD maxpVersion = GetDWORD(); - WORD maxpNumGlyphs = GetWORD(); - WORD maxpMaxPoints = GetWORD(); - WORD maxpMaxContours = GetWORD(); - WORD maxpMaxComponentPoints = GetWORD(); - WORD maxpMaxComponentContours = GetWORD(); - WORD maxpMaxZones = GetWORD(); - WORD maxpMaxTwilightPoints = GetWORD(); - WORD maxpMaxStorage = GetWORD(); - WORD maxpMaxFunctionDefs = GetWORD(); - WORD maxpMaxInstructionDefs = GetWORD(); - WORD maxpMaxStackElements = GetWORD(); - WORD maxpMaxSizeOfInstructions = GetWORD(); - WORD maxpMaxComponentElements = GetWORD(); - WORD maxpMaxComponentDepth = GetWORD(); - - glyphs = maxpNumGlyphs; - glyph = (Glyph *)MemAlloc(glyphs*sizeof(glyph[0])); - - // Load the hmtx table, which gives the horizontal metrics (spacing - // and advance width) of the font. - fseek(fh, hmtxAddr, SEEK_SET); - - WORD hmtxAdvanceWidth; - SWORD hmtxLsb; - for(i = 0; i < min(glyphs, hheaNumberOfMetrics); i++) { - hmtxAdvanceWidth = GetWORD(); - hmtxLsb = (SWORD)GetWORD(); - - glyph[i].leftSideBearing = hmtxLsb; - glyph[i].advanceWidth = hmtxAdvanceWidth; - } - // The last entry in the table applies to all subsequent glyphs also. - for(; i < glyphs; i++) { - glyph[i].leftSideBearing = hmtxLsb; - glyph[i].advanceWidth = hmtxAdvanceWidth; - } - - // Load the cmap table, which determines the mapping of characters to - // glyphs. - fseek(fh, cmapAddr, SEEK_SET); - - DWORD usedTableAddr = -1; - - WORD cmapVersion = GetWORD(); - WORD cmapTableCount = GetWORD(); - for(i = 0; i < cmapTableCount; i++) { - WORD platformId = GetWORD(); - WORD encodingId = GetWORD(); - DWORD offset = GetDWORD(); - - if(platformId == 3 && encodingId == 1) { - // The Windows Unicode mapping is our preference - usedTableAddr = cmapAddr + offset; - } - } - - if(usedTableAddr == -1) { - throw "no used table addr"; - } - - // So we can load the desired subtable; in this case, Windows Unicode, - // which is us. - fseek(fh, usedTableAddr, SEEK_SET); - - WORD mapFormat = GetWORD(); - WORD mapLength = GetWORD(); - WORD mapVersion = GetWORD(); - WORD mapSegCountX2 = GetWORD(); - WORD mapSearchRange = GetWORD(); - WORD mapEntrySelector = GetWORD(); - WORD mapRangeShift = GetWORD(); - - if(mapFormat != 4) { - // Required to use format 4 per spec - throw "not format 4"; - } - - int segCount = mapSegCountX2 / 2; - WORD *endChar = (WORD *)AllocTemporary(segCount*sizeof(WORD)); - WORD *startChar = (WORD *)AllocTemporary(segCount*sizeof(WORD)); - WORD *idDelta = (WORD *)AllocTemporary(segCount*sizeof(WORD)); - WORD *idRangeOffset = (WORD *)AllocTemporary(segCount*sizeof(WORD)); - - DWORD *filePos = (DWORD *)AllocTemporary(segCount*sizeof(DWORD)); - - for(i = 0; i < segCount; i++) { - endChar[i] = GetWORD(); - } - WORD mapReservedPad = GetWORD(); - for(i = 0; i < segCount; i++) { - startChar[i] = GetWORD(); - } - for(i = 0; i < segCount; i++) { - idDelta[i] = GetWORD(); - } - for(i = 0; i < segCount; i++) { - filePos[i] = ftell(fh); - idRangeOffset[i] = GetWORD(); - } - - // So first, null out the glyph table in our in-memory representation - // of the font; any character for which cmap does not provide a glyph - // corresponds to -1 - for(i = 0; i < arraylen(useGlyph); i++) { - useGlyph[i] = 0; - } - - for(i = 0; i < segCount; i++) { - WORD v = idDelta[i]; - if(idRangeOffset[i] == 0) { - int j; - for(j = startChar[i]; j <= endChar[i]; j++) { - if(j > 0 && j < arraylen(useGlyph)) { - // Don't create a reference to a glyph that we won't - // store because it's bigger than the table. - if((WORD)(j + v) < glyphs) { - // Arithmetic is modulo 2^16 - useGlyph[j] = (WORD)(j + v); - } - } - } - } else { - int j; - for(j = startChar[i]; j <= endChar[i]; j++) { - if(j > 0 && j < arraylen(useGlyph)) { - int fp = filePos[i]; - fp += (j - startChar[i])*sizeof(WORD); - fp += idRangeOffset[i]; - fseek(fh, fp, SEEK_SET); - - useGlyph[j] = GetWORD(); - } - } - } - } - - // Load the loca table. This contains the offsets of each glyph, - // relative to the beginning of the glyf table. - fseek(fh, locaAddr, SEEK_SET); - - DWORD *glyphOffsets = (DWORD *)AllocTemporary(glyphs*sizeof(DWORD)); - - for(i = 0; i < glyphs; i++) { - if(headIndexToLocFormat == 1) { - // long offsets, 32 bits - glyphOffsets[i] = GetDWORD(); - } else if(headIndexToLocFormat == 0) { - // short offsets, 16 bits but divided by 2 - glyphOffsets[i] = GetWORD()*2; - } else { - throw "bad headIndexToLocFormat"; - } - } - - scale = 1024; - // Load the glyf table. This contains the actual representations of the - // letter forms, as piecewise linear or quadratic outlines. - for(i = 0; i < glyphs; i++) { - fseek(fh, glyfAddr + glyphOffsets[i], SEEK_SET); - LoadGlyph(i); - } - } catch (char *s) { - dbp("failed: '%s'", s); - fclose(fh); - return false; - } - - fclose(fh); - loaded = true; - return true; -} - -void TtfFont::Flush(void) { - lastWas = NOTHING; -} - -void TtfFont::Handle(int *dx, int x, int y, bool onCurve) { - x = ((x + *dx)*scale + 512) >> 10; - y = (y*scale + 512) >> 10; - - if(lastWas == ON_CURVE && onCurve) { - // This is a line segment. - LineSegment(lastOnCurve.x, lastOnCurve.y, x, y); - } else if(lastWas == ON_CURVE && !onCurve) { - // We can't do the Bezier until we get the next on-curve point, - // but we must store the off-curve point. - } else if(lastWas == OFF_CURVE && onCurve) { - // We are ready to do a Bezier. - Bezier(lastOnCurve.x, lastOnCurve.y, - lastOffCurve.x, lastOffCurve.y, - x, y); - } else if(lastWas == OFF_CURVE && !onCurve) { - // Two consecutive off-curve points implicitly have an on-point - // curve between them, and that should trigger us to generate a - // Bezier. - IntPoint fake; - fake.x = (x + lastOffCurve.x) / 2; - fake.y = (y + lastOffCurve.y) / 2; - Bezier(lastOnCurve.x, lastOnCurve.y, - lastOffCurve.x, lastOffCurve.y, - fake.x, fake.y); - - lastOnCurve.x = fake.x; - lastOnCurve.y = fake.y; - } - - if(onCurve) { - lastOnCurve.x = x; - lastOnCurve.y = y; - lastWas = ON_CURVE; - } else { - lastOffCurve.x = x; - lastOffCurve.y = y; - lastWas = OFF_CURVE; - } -} - -void TtfFont::PlotCharacter(int *dx, int c, double spacing) { - int gli = useGlyph[c]; - - if(gli < 0 || gli >= glyphs) return; - Glyph *g = &(glyph[gli]); - if(!g->pt) return; - - if(c == ' ') { - *dx += g->advanceWidth; - return; - } - - int dx0 = *dx; - - // A point that has x = xMin should be plotted at (dx0 + lsb); fix up - // our x-position so that the curve-generating code will put stuff - // at the right place. - *dx = dx0 - g->xMin; - *dx += g->leftSideBearing; - - int i; - int firstInContour = 0; - for(i = 0; i < g->pts; i++) { - Handle(dx, g->pt[i].x, g->pt[i].y, g->pt[i].onCurve); - - if(g->pt[i].lastInContour) { - int f = firstInContour; - Handle(dx, g->pt[f].x, g->pt[f].y, g->pt[f].onCurve); - firstInContour = i + 1; - Flush(); - } - } - - // And we're done, so advance our position by the requested advance - // width, plus the user-requested extra advance. - *dx = dx0 + g->advanceWidth + (int)(spacing + 0.5); -} - -void TtfFont::PlotString(char *str, double spacing, - SBezierList *sbl, - Vector porigin, Vector pu, Vector pv) -{ - beziers = sbl; - u = pu; - v = pv; - origin = porigin; - - if(!loaded || !str || *str == '\0') { - LineSegment(0, 0, 1024, 0); - LineSegment(1024, 0, 1024, 1024); - LineSegment(1024, 1024, 0, 1024); - LineSegment(0, 1024, 0, 0); - return; - } - - int dx = 0; - - while(*str) { - PlotCharacter(&dx, *str, spacing); - str++; - } -} - -Vector TtfFont::TransformIntPoint(int x, int y) { - Vector r = origin; - r = r.Plus(u.ScaledBy(x / 1024.0)); - r = r.Plus(v.ScaledBy(y / 1024.0)); - return r; -} - -void TtfFont::LineSegment(int x0, int y0, int x1, int y1) { - SBezier sb = SBezier::From(TransformIntPoint(x0, y0), - TransformIntPoint(x1, y1)); - beziers->l.Add(&sb); -} - -void TtfFont::Bezier(int x0, int y0, int x1, int y1, int x2, int y2) { - SBezier sb = SBezier::From(TransformIntPoint(x0, y0), - TransformIntPoint(x1, y1), - TransformIntPoint(x2, y2)); - beziers->l.Add(&sb); -} - diff --git a/undoredo.cpp b/undoredo.cpp deleted file mode 100644 index 7ce1b6b..0000000 --- a/undoredo.cpp +++ /dev/null @@ -1,157 +0,0 @@ -//----------------------------------------------------------------------------- -// The user-visible undo/redo operation; whenever they change something, we -// record our state and push it on a stack, and we pop the stack when they -// select undo. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -void SolveSpace::UndoRemember(void) { - unsaved = true; - PushFromCurrentOnto(&undo); - UndoClearStack(&redo); - UndoEnableMenus(); -} - -void SolveSpace::UndoUndo(void) { - if(undo.cnt <= 0) return; - - PushFromCurrentOnto(&redo); - PopOntoCurrentFrom(&undo); - UndoEnableMenus(); -} - -void SolveSpace::UndoRedo(void) { - if(redo.cnt <= 0) return; - - PushFromCurrentOnto(&undo); - PopOntoCurrentFrom(&redo); - UndoEnableMenus(); -} - -void SolveSpace::UndoEnableMenus(void) { - EnableMenuById(GraphicsWindow::MNU_UNDO, undo.cnt > 0); - EnableMenuById(GraphicsWindow::MNU_REDO, redo.cnt > 0); -} - -void SolveSpace::PushFromCurrentOnto(UndoStack *uk) { - int i; - - if(uk->cnt == MAX_UNDO) { - UndoClearState(&(uk->d[uk->write])); - // And then write in to this one again - } else { - (uk->cnt)++; - } - - UndoState *ut = &(uk->d[uk->write]); - ZERO(ut); - for(i = 0; i < SK.group.n; i++) { - Group *src = &(SK.group.elem[i]); - Group dest = *src; - // And then clean up all the stuff that needs to be a deep copy, - // and zero out all the dynamic stuff that will get regenerated. - dest.clean = false; - ZERO(&(dest.solved)); - ZERO(&(dest.polyLoops)); - ZERO(&(dest.bezierLoops)); - ZERO(&(dest.bezierOpens)); - ZERO(&(dest.polyError)); - ZERO(&(dest.thisMesh)); - ZERO(&(dest.runningMesh)); - ZERO(&(dest.thisShell)); - ZERO(&(dest.runningShell)); - ZERO(&(dest.displayMesh)); - ZERO(&(dest.displayEdges)); - - ZERO(&(dest.remap)); - src->remap.DeepCopyInto(&(dest.remap)); - - ZERO(&(dest.impMesh)); - ZERO(&(dest.impShell)); - ZERO(&(dest.impEntity)); - ut->group.Add(&dest); - } - for(i = 0; i < SK.request.n; i++) { - ut->request.Add(&(SK.request.elem[i])); - } - for(i = 0; i < SK.constraint.n; i++) { - Constraint *src = &(SK.constraint.elem[i]); - Constraint dest = *src; - ZERO(&(dest.dogd)); - ut->constraint.Add(&dest); - } - for(i = 0; i < SK.param.n; i++) { - ut->param.Add(&(SK.param.elem[i])); - } - for(i = 0; i < SK.style.n; i++) { - ut->style.Add(&(SK.style.elem[i])); - } - ut->activeGroup = SS.GW.activeGroup; - - uk->write = WRAP(uk->write + 1, MAX_UNDO); -} - -void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) { - if(uk->cnt <= 0) oops(); - (uk->cnt)--; - uk->write = WRAP(uk->write - 1, MAX_UNDO); - - UndoState *ut = &(uk->d[uk->write]); - - // Free everything in the main copy of the program before replacing it - Group *g; - for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) { - g->Clear(); - } - SK.group.Clear(); - SK.request.Clear(); - SK.constraint.Clear(); - SK.param.Clear(); - SK.style.Clear(); - - // And then do a shallow copy of the state from the undo list - ut->group.MoveSelfInto(&(SK.group)); - ut->request.MoveSelfInto(&(SK.request)); - ut->constraint.MoveSelfInto(&(SK.constraint)); - ut->param.MoveSelfInto(&(SK.param)); - ut->style.MoveSelfInto(&(SK.style)); - SS.GW.activeGroup = ut->activeGroup; - - // No need to free it, since a shallow copy was made above - ZERO(ut); - - // And reset the state everywhere else in the program, since the - // sketch just changed a lot. - SS.GW.ClearSuper(); - SS.TW.ClearSuper(); - SS.ReloadAllImported(); - SS.GenerateAll(0, INT_MAX); - later.showTW = true; -} - -void SolveSpace::UndoClearStack(UndoStack *uk) { - while(uk->cnt > 0) { - uk->write = WRAP(uk->write - 1, MAX_UNDO); - (uk->cnt)--; - UndoClearState(&(uk->d[uk->write])); - } - ZERO(uk); // for good measure -} - -void SolveSpace::UndoClearState(UndoState *ut) { - int i; - for(i = 0; i < ut->group.n; i++) { - Group *g = &(ut->group.elem[i]); - - g->remap.Clear(); - } - ut->group.Clear(); - ut->request.Clear(); - ut->constraint.Clear(); - ut->param.Clear(); - ut->style.Clear(); - ZERO(ut); -} - diff --git a/view.cpp b/view.cpp deleted file mode 100644 index 58d96c2..0000000 --- a/view.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//----------------------------------------------------------------------------- -// The View menu, stuff to snap to certain special vews of the model, and to -// display our current view of the model to the user. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include "solvespace.h" - -void TextWindow::ShowEditView(void) { - Printf(true, "%Ft3D VIEW PARAMETERS%E"); - - Printf(true, "%Bd %Ftoverall scale factor%E"); - Printf(false, "%Ba %# px/%s %Fl%Ll%f[edit]%E", - SS.GW.scale * SS.MmPerUnit(), - SS.UnitName(), - &ScreenChangeViewScale); - Printf(false, ""); - - Printf(false, "%Bd %Ftorigin (maps to center of screen)%E"); - Printf(false, "%Ba (%s, %s, %s) %Fl%Ll%f[edit]%E", - SS.MmToString(-SS.GW.offset.x), - SS.MmToString(-SS.GW.offset.y), - SS.MmToString(-SS.GW.offset.z), - &ScreenChangeViewOrigin); - Printf(false, ""); - - Vector n = (SS.GW.projRight).Cross(SS.GW.projUp); - Printf(false, "%Bd %Ftprojection onto screen%E"); - Printf(false, "%Ba %Ftright%E (%3, %3, %3) %Fl%Ll%f[edit]%E", - CO(SS.GW.projRight), - &ScreenChangeViewProjection); - Printf(false, "%Bd %Ftup%E (%3, %3, %3)", CO(SS.GW.projUp)); - Printf(false, "%Ba %Ftout%E (%3, %3, %3)", CO(n)); - Printf(false, ""); - - Printf(false, "The perspective may be changed in the"); - Printf(false, "configuration screen."); -} - -void TextWindow::ScreenChangeViewScale(int link, DWORD v) { - char buf[1024]; - sprintf(buf, "%.3f", SS.GW.scale * SS.MmPerUnit()); - - SS.TW.edit.meaning = EDIT_VIEW_SCALE; - SS.TW.ShowEditControl(12, 3, buf); -} - -void TextWindow::ScreenChangeViewOrigin(int link, DWORD v) { - char buf[1024]; - sprintf(buf, "%s, %s, %s", - SS.MmToString(-SS.GW.offset.x), - SS.MmToString(-SS.GW.offset.y), - SS.MmToString(-SS.GW.offset.z)); - - SS.TW.edit.meaning = EDIT_VIEW_ORIGIN; - SS.TW.ShowEditControl(18, 3, buf); -} - -void TextWindow::ScreenChangeViewProjection(int link, DWORD v) { - char buf[1024]; - sprintf(buf, "%.3f, %.3f, %.3f", CO(SS.GW.projRight)); - SS.TW.edit.meaning = EDIT_VIEW_PROJ_RIGHT; - SS.TW.ShowEditControl(24, 10, buf); -} - -bool TextWindow::EditControlDoneForView(char *s) { - switch(edit.meaning) { - case EDIT_VIEW_SCALE: { - Expr *e = Expr::From(s, true); - if(e) { - double v = e->Eval() / SS.MmPerUnit(); - if(v > LENGTH_EPS) { - SS.GW.scale = v; - } else { - Error("Scale cannot be zero or negative."); - } - } - break; - } - - case EDIT_VIEW_ORIGIN: { - Vector pt; - if(sscanf(s, "%lf, %lf, %lf", &pt.x, &pt.y, &pt.z) == 3) { - pt = pt.ScaledBy(SS.MmPerUnit()); - SS.GW.offset = pt.ScaledBy(-1); - } else { - Error("Bad format: specify x, y, z"); - } - break; - } - - case EDIT_VIEW_PROJ_RIGHT: - case EDIT_VIEW_PROJ_UP: { - Vector pt; - if(sscanf(s, "%lf, %lf, %lf", &pt.x, &pt.y, &pt.z) != 3) { - Error("Bad format: specify x, y, z"); - break; - } - if(edit.meaning == EDIT_VIEW_PROJ_RIGHT) { - SS.GW.projRight = pt; - SS.GW.NormalizeProjectionVectors(); - edit.meaning = EDIT_VIEW_PROJ_UP; - char buf[1024]; - sprintf(buf, "%.3f, %.3f, %.3f", CO(SS.GW.projUp)); - HideEditControl(); - ShowEditControl(26, 10, buf); - edit.showAgain = true; - } else { - SS.GW.projUp = pt; - SS.GW.NormalizeProjectionVectors(); - } - break; - } - - default: - return false; - } - return true; -} - diff --git a/win32/manifest.xml b/win32/manifest.xml deleted file mode 100644 index f27a236..0000000 --- a/win32/manifest.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - -Parametric 3d CAD tool. - - - - - - diff --git a/win32/resource.rc b/win32/resource.rc deleted file mode 100644 index 19e0a82..0000000 --- a/win32/resource.rc +++ /dev/null @@ -1,6 +0,0 @@ - -// we need a manifest if we want visual styles; put in numbers since somethings a bit screwy -// with my SDK install (I don't think I've got *.rh right) -1 24 "manifest.xml" - -4000 ICON "../icon.ico" diff --git a/win32/w32main.cpp b/win32/w32main.cpp deleted file mode 100644 index 5d34e63..0000000 --- a/win32/w32main.cpp +++ /dev/null @@ -1,1203 +0,0 @@ -//----------------------------------------------------------------------------- -// Our WinMain() functions, and Win32-specific stuff to set up our windows -// and otherwise handle our interface to the operating system. Everything -// outside win32/... should be standard C++ and gl. -// -// Copyright 2008-2013 Jonathan Westhues. -//----------------------------------------------------------------------------- -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "solvespace.h" - -#define FREEZE_SUBKEY "SolveSpace" -#include "freeze.h" - -// For the edit controls -#define EDIT_WIDTH 220 -#define EDIT_HEIGHT 21 - -HINSTANCE Instance; - -HWND TextWnd; -HWND TextWndScrollBar; -HWND TextEditControl; -HGLRC TextGl; - -HWND GraphicsWnd; -HGLRC GraphicsGl; -HWND GraphicsEditControl; -struct { - int x, y; -} LastMousePos; - -char RecentFile[MAX_RECENT][MAX_PATH]; -HMENU SubMenus[100]; -HMENU RecentOpenMenu, RecentImportMenu; - -HMENU ContextMenu, ContextSubmenu; - -int ClientIsSmallerBy; - -HFONT FixedFont; - -// The 6-DOF input device. -SiHdl SpaceNavigator = SI_NO_HANDLE; - -//----------------------------------------------------------------------------- -// Routines to display message boxes on screen. Do our own, instead of using -// MessageBox, because that is not consistent from version to version and -// there's word wrap problems. -//----------------------------------------------------------------------------- - -HWND MessageWnd, OkButton; -BOOL MessageDone; -char *MessageString; - -static LRESULT CALLBACK MessageProc(HWND hwnd, UINT msg, WPARAM wParam, - LPARAM lParam) -{ - switch (msg) { - case WM_COMMAND: - if((HWND)lParam == OkButton && wParam == BN_CLICKED) { - MessageDone = TRUE; - } - break; - - case WM_CLOSE: - case WM_DESTROY: - MessageDone = TRUE; - break; - - case WM_PAINT: { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - int row = 0, col = 0, i; - SelectObject(hdc, FixedFont); - SetTextColor(hdc, RGB(0, 0, 0)); - SetBkMode(hdc, TRANSPARENT); - for(i = 0; MessageString[i]; i++) { - if(MessageString[i] == '\n') { - col = 0; - row++; - } else { - TextOut(hdc, col*SS.TW.CHAR_WIDTH + 10, - row*SS.TW.LINE_HEIGHT + 10, - &(MessageString[i]), 1); - col++; - } - } - EndPaint(hwnd, &ps); - break; - } - - default: - return DefWindowProc(hwnd, msg, wParam, lParam); - } - - return 1; -} - -HWND CreateWindowClient(DWORD exStyle, char *className, char *windowName, - DWORD style, int x, int y, int width, int height, HWND parent, - HMENU menu, HINSTANCE instance, void *param) -{ - HWND h = CreateWindowEx(exStyle, className, windowName, style, x, y, - width, height, parent, menu, instance, param); - - RECT r; - GetClientRect(h, &r); - width = width - (r.right - width); - height = height - (r.bottom - height); - - SetWindowPos(h, HWND_TOP, x, y, width, height, 0); - - return h; -} - -void DoMessageBox(char *str, int rows, int cols, BOOL error) -{ - EnableWindow(GraphicsWnd, FALSE); - EnableWindow(TextWnd, FALSE); - HWND h = GetForegroundWindow(); - - // Register the window class for our dialog. - WNDCLASSEX wc; - memset(&wc, 0, sizeof(wc)); - wc.cbSize = sizeof(wc); - wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC; - wc.lpfnWndProc = (WNDPROC)MessageProc; - wc.hInstance = Instance; - wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW; - wc.lpszClassName = "MessageWnd"; - wc.lpszMenuName = NULL; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000), - IMAGE_ICON, 32, 32, 0); - wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000), - IMAGE_ICON, 16, 16, 0); - RegisterClassEx(&wc); - - // Create the window. - MessageString = str; - RECT r; - GetWindowRect(GraphicsWnd, &r); - char *title = error ? "SolveSpace - Error" : "SolveSpace - Message"; - int width = cols*SS.TW.CHAR_WIDTH + 20, - height = rows*SS.TW.LINE_HEIGHT + 60; - MessageWnd = CreateWindowClient(0, "MessageWnd", title, - WS_OVERLAPPED | WS_SYSMENU, - r.left + 100, r.top + 100, width, height, NULL, NULL, Instance, NULL); - - OkButton = CreateWindowEx(0, WC_BUTTON, "OK", - WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON, - (width - 70)/2, rows*SS.TW.LINE_HEIGHT + 20, - 70, 25, MessageWnd, NULL, Instance, NULL); - SendMessage(OkButton, WM_SETFONT, (WPARAM)FixedFont, TRUE); - - ShowWindow(MessageWnd, TRUE); - SetFocus(OkButton); - - MSG msg; - DWORD ret; - MessageDone = FALSE; - while((ret = GetMessage(&msg, NULL, 0, 0)) && !MessageDone) { - if((msg.message == WM_KEYDOWN && - (msg.wParam == VK_RETURN || - msg.wParam == VK_ESCAPE)) || - (msg.message == WM_KEYUP && - (msg.wParam == VK_SPACE))) - { - MessageDone = TRUE; - break; - } - - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - MessageString = NULL; - EnableWindow(TextWnd, TRUE); - EnableWindow(GraphicsWnd, TRUE); - SetForegroundWindow(GraphicsWnd); - DestroyWindow(MessageWnd); -} - -void AddContextMenuItem(char *label, int id) -{ - if(!ContextMenu) ContextMenu = CreatePopupMenu(); - - if(id == CONTEXT_SUBMENU) { - AppendMenu(ContextMenu, MF_STRING | MF_POPUP, - (UINT_PTR)ContextSubmenu, label); - ContextSubmenu = NULL; - } else { - HMENU m = ContextSubmenu ? ContextSubmenu : ContextMenu; - if(id == CONTEXT_SEPARATOR) { - AppendMenu(m, MF_SEPARATOR, 0, ""); - } else { - AppendMenu(m, MF_STRING, id, label); - } - } -} - -void CreateContextSubmenu(void) -{ - ContextSubmenu = CreatePopupMenu(); -} - -int ShowContextMenu(void) -{ - POINT p; - GetCursorPos(&p); - int r = TrackPopupMenu(ContextMenu, - TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_TOPALIGN, - p.x, p.y, 0, GraphicsWnd, NULL); - - DestroyMenu(ContextMenu); - ContextMenu = NULL; - return r; -} - -void CALLBACK TimerCallback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time) -{ - // The timer is periodic, so needs to be killed explicitly. - KillTimer(GraphicsWnd, 1); - SS.GW.TimerCallback(); - SS.TW.TimerCallback(); -} -void SetTimerFor(int milliseconds) -{ - SetTimer(GraphicsWnd, 1, milliseconds, TimerCallback); -} - -static void GetWindowSize(HWND hwnd, int *w, int *h) -{ - RECT r; - GetClientRect(hwnd, &r); - *w = r.right - r.left; - *h = r.bottom - r.top; -} -void GetGraphicsWindowSize(int *w, int *h) -{ - GetWindowSize(GraphicsWnd, w, h); -} -void GetTextWindowSize(int *w, int *h) -{ - GetWindowSize(TextWnd, w, h); -} - -void OpenWebsite(char *url) { - ShellExecute(GraphicsWnd, "open", url, NULL, NULL, SW_SHOWNORMAL); -} - -void ExitNow(void) { - PostQuitMessage(0); -} - -//----------------------------------------------------------------------------- -// Helpers so that we can read/write registry keys from the platform- -// independent code. -//----------------------------------------------------------------------------- -void CnfFreezeString(char *str, char *name) - { FreezeStringF(str, FREEZE_SUBKEY, name); } - -void CnfFreezeDWORD(DWORD v, char *name) - { FreezeDWORDF(v, FREEZE_SUBKEY, name); } - -void CnfFreezeFloat(float v, char *name) - { FreezeDWORDF(*((DWORD *)&v), FREEZE_SUBKEY, name); } - -void CnfThawString(char *str, int maxLen, char *name) - { ThawStringF(str, maxLen, FREEZE_SUBKEY, name); } - -DWORD CnfThawDWORD(DWORD v, char *name) - { return ThawDWORDF(v, FREEZE_SUBKEY, name); } - -float CnfThawFloat(float v, char *name) { - DWORD d = ThawDWORDF(*((DWORD *)&v), FREEZE_SUBKEY, name); - return *((float *)&d); -} - -void SetWindowTitle(char *str) { - SetWindowText(GraphicsWnd, str); -} - -void SetMousePointerToHand(bool yes) { - SetCursor(LoadCursor(NULL, yes ? IDC_HAND : IDC_ARROW)); -} - -static void PaintTextWnd(HDC hdc) -{ - wglMakeCurrent(GetDC(TextWnd), TextGl); - - SS.TW.Paint(); - SwapBuffers(GetDC(TextWnd)); - - // Leave the graphics window context active, except when we're painting - // this text window. - wglMakeCurrent(GetDC(GraphicsWnd), GraphicsGl); -} - -void MoveTextScrollbarTo(int pos, int maxPos, int page) -{ - SCROLLINFO si; - memset(&si, 0, sizeof(si)); - si.cbSize = sizeof(si); - si.fMask = SIF_DISABLENOSCROLL | SIF_ALL; - si.nMin = 0; - si.nMax = maxPos; - si.nPos = pos; - si.nPage = page; - SetScrollInfo(TextWndScrollBar, SB_CTL, &si, TRUE); -} - -void HandleTextWindowScrollBar(WPARAM wParam, LPARAM lParam) -{ - int maxPos, minPos, pos; - GetScrollRange(TextWndScrollBar, SB_CTL, &minPos, &maxPos); - pos = GetScrollPos(TextWndScrollBar, SB_CTL); - - switch(LOWORD(wParam)) { - case SB_LINEUP: pos--; break; - case SB_PAGEUP: pos -= 4; break; - - case SB_LINEDOWN: pos++; break; - case SB_PAGEDOWN: pos += 4; break; - - case SB_TOP: pos = 0; break; - - case SB_BOTTOM: pos = maxPos; break; - - case SB_THUMBTRACK: - case SB_THUMBPOSITION: pos = HIWORD(wParam); break; - } - - SS.TW.ScrollbarEvent(pos); -} - -static void MouseWheel(int thisDelta) { - static int DeltaAccum; - int delta = 0; - // Handle mouse deltas of less than 120 (like from an un-detented mouse - // wheel) correctly, even though no one ever uses those. - DeltaAccum += thisDelta; - while(DeltaAccum >= 120) { - DeltaAccum -= 120; - delta += 120; - } - while(DeltaAccum <= -120) { - DeltaAccum += 120; - delta -= 120; - } - if(delta == 0) return; - - POINT pt; - GetCursorPos(&pt); - HWND hw = WindowFromPoint(pt); - - // Make the mousewheel work according to which window the mouse is - // over, not according to which window is active. - bool inTextWindow; - if(hw == TextWnd) { - inTextWindow = true; - } else if(hw == GraphicsWnd) { - inTextWindow = false; - } else if(GetForegroundWindow() == TextWnd) { - inTextWindow = true; - } else { - inTextWindow = false; - } - - if(inTextWindow) { - int i; - for(i = 0; i < abs(delta/40); i++) { - HandleTextWindowScrollBar(delta > 0 ? SB_LINEUP : SB_LINEDOWN, 0); - } - } else { - SS.GW.MouseScroll(LastMousePos.x, LastMousePos.y, delta); - } -} - -LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_ERASEBKGND: - break; - - case WM_CLOSE: - case WM_DESTROY: - SolveSpace::MenuFile(GraphicsWindow::MNU_EXIT); - break; - - case WM_PAINT: { - // Actually paint the text window, with gl. - PaintTextWnd(GetDC(TextWnd)); - // And then just make Windows happy. - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - EndPaint(hwnd, &ps); - break; - } - - case WM_SIZING: { - RECT *r = (RECT *)lParam; - int hc = (r->bottom - r->top) - ClientIsSmallerBy; - int extra = hc % (SS.TW.LINE_HEIGHT/2); - switch(wParam) { - case WMSZ_BOTTOM: - case WMSZ_BOTTOMLEFT: - case WMSZ_BOTTOMRIGHT: - r->bottom -= extra; - break; - - case WMSZ_TOP: - case WMSZ_TOPLEFT: - case WMSZ_TOPRIGHT: - r->top += extra; - break; - } - int tooNarrow = (SS.TW.MIN_COLS*SS.TW.CHAR_WIDTH) - - (r->right - r->left); - if(tooNarrow >= 0) { - switch(wParam) { - case WMSZ_RIGHT: - case WMSZ_BOTTOMRIGHT: - case WMSZ_TOPRIGHT: - r->right += tooNarrow; - break; - - case WMSZ_LEFT: - case WMSZ_BOTTOMLEFT: - case WMSZ_TOPLEFT: - r->left -= tooNarrow; - break; - } - } - break; - } - - case WM_MOUSELEAVE: - SS.TW.MouseLeave(); - break; - - case WM_LBUTTONDOWN: - case WM_MOUSEMOVE: { - // We need this in order to get the WM_MOUSELEAVE - TRACKMOUSEEVENT tme; - ZERO(&tme); - tme.cbSize = sizeof(tme); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = TextWnd; - TrackMouseEvent(&tme); - - // And process the actual message - int x = LOWORD(lParam); - int y = HIWORD(lParam); - SS.TW.MouseEvent(msg == WM_LBUTTONDOWN, wParam & MK_LBUTTON, x, y); - break; - } - - case WM_SIZE: { - RECT r; - GetWindowRect(TextWndScrollBar, &r); - int sw = r.right - r.left; - GetClientRect(hwnd, &r); - MoveWindow(TextWndScrollBar, r.right - sw, r.top, sw, - (r.bottom - r.top), TRUE); - // If the window is growing, then the scrollbar position may - // be moving, so it's as if we're dragging the scrollbar. - HandleTextWindowScrollBar(-1, -1); - InvalidateRect(TextWnd, NULL, FALSE); - break; - } - - case WM_MOUSEWHEEL: - MouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); - break; - - case WM_VSCROLL: - HandleTextWindowScrollBar(wParam, lParam); - break; - - default: - return DefWindowProc(hwnd, msg, wParam, lParam); - } - - return 1; -} - -static BOOL ProcessKeyDown(WPARAM wParam) -{ - if(GraphicsEditControlIsVisible() && wParam != VK_ESCAPE) { - if(wParam == VK_RETURN) { - char s[1024]; - memset(s, 0, sizeof(s)); - SendMessage(GraphicsEditControl, WM_GETTEXT, 900, (LPARAM)s); - SS.GW.EditControlDone(s); - return TRUE; - } else { - return FALSE; - } - } - if(TextEditControlIsVisible() && wParam != VK_ESCAPE) { - if(wParam == VK_RETURN) { - char s[1024]; - memset(s, 0, sizeof(s)); - SendMessage(TextEditControl, WM_GETTEXT, 900, (LPARAM)s); - SS.TW.EditControlDone(s); - } else { - return FALSE; - } - } - - int c; - switch(wParam) { - case VK_OEM_PLUS: c = '+'; break; - case VK_OEM_MINUS: c = '-'; break; - case VK_ESCAPE: c = 27; break; - case VK_OEM_1: c = ';'; break; - case VK_OEM_3: c = '`'; break; - case VK_OEM_4: c = '['; break; - case VK_OEM_6: c = ']'; break; - case VK_OEM_5: c = '\\'; break; - case VK_OEM_PERIOD: c = '.'; break; - case VK_SPACE: c = ' '; break; - case VK_DELETE: c = 127; break; - case VK_TAB: c = '\t'; break; - - case VK_BROWSER_BACK: - case VK_BACK: c = 1 + 'h' - 'a'; break; - - case VK_F1: - case VK_F2: - case VK_F3: - case VK_F4: - case VK_F5: - case VK_F6: - case VK_F7: - case VK_F8: - case VK_F9: - case VK_F10: - case VK_F11: - case VK_F12: c = (wParam - VK_F1) + 0xf1; break; - - // These overlap with some character codes that I'm using, so - // don't let them trigger by accident. - case VK_F16: - case VK_INSERT: - case VK_EXECUTE: - case VK_APPS: - case VK_LWIN: - case VK_RWIN: return FALSE; - - default: - c = wParam; - break; - } - if(GetAsyncKeyState(VK_SHIFT) & 0x8000) c |= 0x100; - if(GetAsyncKeyState(VK_CONTROL) & 0x8000) c |= 0x200; - - for(int i = 0; SS.GW.menu[i].level >= 0; i++) { - if(c == SS.GW.menu[i].accel) { - (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)SS.GW.menu[i].id); - break; - } - } - - if(SS.GW.KeyDown(c)) return TRUE; - - // No accelerator; process the key as normal. - return FALSE; -} - -void ShowTextWindow(BOOL visible) -{ - ShowWindow(TextWnd, visible ? SW_SHOWNOACTIVATE : SW_HIDE); -} - -static void CreateGlContext(HWND hwnd, HGLRC *glrc) -{ - HDC hdc = GetDC(hwnd); - - PIXELFORMATDESCRIPTOR pfd; - int pixelFormat; - - memset(&pfd, 0, sizeof(pfd)); - pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | - PFD_DOUBLEBUFFER; - pfd.dwLayerMask = PFD_MAIN_PLANE; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 32; - pfd.cDepthBits = 24; - pfd.cAccumBits = 0; - pfd.cStencilBits = 0; - - pixelFormat = ChoosePixelFormat(hdc, &pfd); - if(!pixelFormat) oops(); - - if(!SetPixelFormat(hdc, pixelFormat, &pfd)) oops(); - - *glrc = wglCreateContext(hdc); - wglMakeCurrent(hdc, *glrc); -} - -void PaintGraphics(void) -{ - SS.GW.Paint(); - SwapBuffers(GetDC(GraphicsWnd)); -} -void InvalidateGraphics(void) -{ - InvalidateRect(GraphicsWnd, NULL, FALSE); -} - -SDWORD GetMilliseconds(void) -{ - LARGE_INTEGER t, f; - QueryPerformanceCounter(&t); - QueryPerformanceFrequency(&f); - LONGLONG d = t.QuadPart/(f.QuadPart/1000); - return (SDWORD)d; -} - -SQWORD GetUnixTime(void) -{ - __time64_t ret; - _time64(&ret); - return ret; -} - -void InvalidateText(void) -{ - InvalidateRect(TextWnd, NULL, FALSE); -} - -static void ShowEditControl(HWND h, int x, int y, char *s) { - MoveWindow(h, x, y, EDIT_WIDTH, EDIT_HEIGHT, TRUE); - ShowWindow(h, SW_SHOW); - if(s) { - SendMessage(h, WM_SETTEXT, 0, (LPARAM)s); - SendMessage(h, EM_SETSEL, 0, strlen(s)); - SetFocus(h); - } -} -void ShowTextEditControl(int x, int y, char *s) -{ - if(GraphicsEditControlIsVisible()) return; - - ShowEditControl(TextEditControl, x, y, s); -} -void HideTextEditControl(void) -{ - ShowWindow(TextEditControl, SW_HIDE); -} -BOOL TextEditControlIsVisible(void) -{ - return IsWindowVisible(TextEditControl); -} -void ShowGraphicsEditControl(int x, int y, char *s) -{ - if(GraphicsEditControlIsVisible()) return; - - RECT r; - GetClientRect(GraphicsWnd, &r); - x = x + (r.right - r.left)/2; - y = (r.bottom - r.top)/2 - y; - - // (x, y) are the bottom left, but the edit control is placed by its - // top left corner - y -= 20; - - ShowEditControl(GraphicsEditControl, x, y, s); -} -void HideGraphicsEditControl(void) -{ - ShowWindow(GraphicsEditControl, SW_HIDE); -} -BOOL GraphicsEditControlIsVisible(void) -{ - return IsWindowVisible(GraphicsEditControl); -} - -LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, - LPARAM lParam) -{ - switch (msg) { - case WM_ERASEBKGND: - break; - - case WM_SIZE: - InvalidateRect(GraphicsWnd, NULL, FALSE); - break; - - case WM_PAINT: { - // Actually paint the window, with gl. - PaintGraphics(); - // And make Windows happy. - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - EndPaint(hwnd, &ps); - break; - } - - case WM_MOUSELEAVE: - SS.GW.MouseLeave(); - break; - - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_MBUTTONDOWN: { - int x = LOWORD(lParam); - int y = HIWORD(lParam); - - // We need this in order to get the WM_MOUSELEAVE - TRACKMOUSEEVENT tme; - ZERO(&tme); - tme.cbSize = sizeof(tme); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = GraphicsWnd; - TrackMouseEvent(&tme); - - // Convert to xy (vs. ij) style coordinates, with (0, 0) at center - RECT r; - GetClientRect(GraphicsWnd, &r); - x = x - (r.right - r.left)/2; - y = (r.bottom - r.top)/2 - y; - - LastMousePos.x = x; - LastMousePos.y = y; - - if(msg == WM_LBUTTONDOWN) { - SS.GW.MouseLeftDown(x, y); - } else if(msg == WM_LBUTTONUP) { - SS.GW.MouseLeftUp(x, y); - } else if(msg == WM_LBUTTONDBLCLK) { - SS.GW.MouseLeftDoubleClick(x, y); - } else if(msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN) { - SS.GW.MouseMiddleOrRightDown(x, y); - } else if(msg == WM_RBUTTONUP) { - SS.GW.MouseRightUp(x, y); - } else if(msg == WM_MOUSEMOVE) { - SS.GW.MouseMoved(x, y, - !!(wParam & MK_LBUTTON), - !!(wParam & MK_MBUTTON), - !!(wParam & MK_RBUTTON), - !!(wParam & MK_SHIFT), - !!(wParam & MK_CONTROL)); - } else { - oops(); - } - break; - } - case WM_MOUSEWHEEL: - MouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); - break; - - case WM_COMMAND: { - if(HIWORD(wParam) == 0) { - int id = LOWORD(wParam); - if((id >= RECENT_OPEN && id < (RECENT_OPEN + MAX_RECENT))) { - SolveSpace::MenuFile(id); - break; - } - if((id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT))) { - Group::MenuGroup(id); - break; - } - int i; - for(i = 0; SS.GW.menu[i].level >= 0; i++) { - if(id == SS.GW.menu[i].id) { - (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)id); - break; - } - } - if(SS.GW.menu[i].level < 0) oops(); - } - break; - } - - case WM_CLOSE: - case WM_DESTROY: - SolveSpace::MenuFile(GraphicsWindow::MNU_EXIT); - return 1; - - default: - return DefWindowProc(hwnd, msg, wParam, lParam); - } - - return 1; -} - -//----------------------------------------------------------------------------- -// Common dialog routines, to open or save a file. -//----------------------------------------------------------------------------- -BOOL GetOpenFile(char *file, char *defExtension, char *selPattern) -{ - OPENFILENAME ofn; - - memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hInstance = Instance; - ofn.hwndOwner = GraphicsWnd; - ofn.lpstrFilter = selPattern; - ofn.lpstrDefExt = defExtension; - ofn.lpstrFile = file; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; - - EnableWindow(GraphicsWnd, FALSE); - EnableWindow(TextWnd, FALSE); - - BOOL r = GetOpenFileName(&ofn); - - EnableWindow(TextWnd, TRUE); - EnableWindow(GraphicsWnd, TRUE); - SetForegroundWindow(GraphicsWnd); - - return r; -} -BOOL GetSaveFile(char *file, char *defExtension, char *selPattern) -{ - OPENFILENAME ofn; - - memset(&ofn, 0, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hInstance = Instance; - ofn.hwndOwner = GraphicsWnd; - ofn.lpstrFilter = selPattern; - ofn.lpstrDefExt = defExtension; - ofn.lpstrFile = file; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; - - EnableWindow(GraphicsWnd, FALSE); - EnableWindow(TextWnd, FALSE); - - BOOL r = GetSaveFileName(&ofn); - - EnableWindow(TextWnd, TRUE); - EnableWindow(GraphicsWnd, TRUE); - SetForegroundWindow(GraphicsWnd); - - return r; -} -int SaveFileYesNoCancel(void) -{ - EnableWindow(GraphicsWnd, FALSE); - EnableWindow(TextWnd, FALSE); - - int r = MessageBox(GraphicsWnd, - "The program has changed since it was last saved.\r\n\r\n" - "Do you want to save the changes?", "SolveSpace", - MB_YESNOCANCEL | MB_ICONWARNING); - - EnableWindow(TextWnd, TRUE); - EnableWindow(GraphicsWnd, TRUE); - SetForegroundWindow(GraphicsWnd); - - return r; -} - -void LoadAllFontFiles(void) -{ - WIN32_FIND_DATA wfd; - char dir[MAX_PATH]; - GetWindowsDirectory(dir, MAX_PATH - 30); - strcat(dir, "\\fonts\\*.ttf"); - - HANDLE h = FindFirstFile(dir, &wfd); - - while(h != INVALID_HANDLE_VALUE) { - TtfFont tf; - ZERO(&tf); - - char fullPath[MAX_PATH]; - GetWindowsDirectory(fullPath, MAX_PATH - (30 + strlen(wfd.cFileName))); - strcat(fullPath, "\\fonts\\"); - strcat(fullPath, wfd.cFileName); - - strcpy(tf.fontFile, fullPath); - SS.fonts.l.Add(&tf); - - if(!FindNextFile(h, &wfd)) break; - } -} - -static void MenuById(int id, BOOL yes, BOOL check) -{ - int i; - int subMenu = -1; - - for(i = 0; SS.GW.menu[i].level >= 0; i++) { - if(SS.GW.menu[i].level == 0) subMenu++; - - if(SS.GW.menu[i].id == id) { - if(subMenu < 0) oops(); - if(subMenu >= (sizeof(SubMenus)/sizeof(SubMenus[0]))) oops(); - - if(check) { - CheckMenuItem(SubMenus[subMenu], id, - yes ? MF_CHECKED : MF_UNCHECKED); - } else { - EnableMenuItem(SubMenus[subMenu], id, - yes ? MF_ENABLED : MF_GRAYED); - } - return; - } - } - oops(); -} -void CheckMenuById(int id, BOOL checked) -{ - MenuById(id, checked, TRUE); -} -void EnableMenuById(int id, BOOL enabled) -{ - MenuById(id, enabled, FALSE); -} -static void DoRecent(HMENU m, int base) -{ - while(DeleteMenu(m, 0, MF_BYPOSITION)) - ; - int i, c = 0; - for(i = 0; i < MAX_RECENT; i++) { - char *s = RecentFile[i]; - if(*s) { - AppendMenu(m, MF_STRING, base+i, s); - c++; - } - } - if(c == 0) AppendMenu(m, MF_STRING | MF_GRAYED, 0, "(no recent files)"); -} -void RefreshRecentMenus(void) -{ - DoRecent(RecentOpenMenu, RECENT_OPEN); - DoRecent(RecentImportMenu, RECENT_IMPORT); -} - -HMENU CreateGraphicsWindowMenus(void) -{ - HMENU top = CreateMenu(); - HMENU m; - - int i; - int subMenu = 0; - - for(i = 0; SS.GW.menu[i].level >= 0; i++) { - if(SS.GW.menu[i].level == 0) { - m = CreateMenu(); - AppendMenu(top, MF_STRING | MF_POPUP, (UINT_PTR)m, - SS.GW.menu[i].label); - - if(subMenu >= arraylen(SubMenus)) oops(); - SubMenus[subMenu] = m; - subMenu++; - } else if(SS.GW.menu[i].level == 1) { - if(SS.GW.menu[i].label) { - AppendMenu(m, MF_STRING, SS.GW.menu[i].id, SS.GW.menu[i].label); - } else { - AppendMenu(m, MF_SEPARATOR, SS.GW.menu[i].id, ""); - } - } else if(SS.GW.menu[i].level == 10) { - RecentOpenMenu = CreateMenu(); - AppendMenu(m, MF_STRING | MF_POPUP, - (UINT_PTR)RecentOpenMenu, SS.GW.menu[i].label); - } else if(SS.GW.menu[i].level == 11) { - RecentImportMenu = CreateMenu(); - AppendMenu(m, MF_STRING | MF_POPUP, - (UINT_PTR)RecentImportMenu, SS.GW.menu[i].label); - } else oops(); - } - RefreshRecentMenus(); - - return top; -} - -static void CreateMainWindows(void) -{ - WNDCLASSEX wc; - - memset(&wc, 0, sizeof(wc)); - wc.cbSize = sizeof(wc); - - // The graphics window, where the sketch is drawn and shown. - wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC | - CS_DBLCLKS; - wc.lpfnWndProc = (WNDPROC)GraphicsWndProc; - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wc.lpszClassName = "GraphicsWnd"; - wc.lpszMenuName = NULL; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000), - IMAGE_ICON, 32, 32, 0); - wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000), - IMAGE_ICON, 16, 16, 0); - if(!RegisterClassEx(&wc)) oops(); - - HMENU top = CreateGraphicsWindowMenus(); - GraphicsWnd = CreateWindowEx(0, "GraphicsWnd", - "SolveSpace (not yet saved)", - WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | - WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_CLIPSIBLINGS, - 50, 50, 900, 600, NULL, top, Instance, NULL); - if(!GraphicsWnd) oops(); - - GraphicsEditControl = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "", - WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS, - 50, 50, 100, 21, GraphicsWnd, NULL, Instance, NULL); - SendMessage(GraphicsEditControl, WM_SETFONT, (WPARAM)FixedFont, TRUE); - - // The text window, with a comand line and some textual information - // about the sketch. - wc.style &= ~CS_DBLCLKS; - wc.lpfnWndProc = (WNDPROC)TextWndProc; - wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - wc.lpszClassName = "TextWnd"; - wc.hCursor = NULL; - if(!RegisterClassEx(&wc)) oops(); - - // We get the desired Alt+Tab behaviour by specifying that the text - // window is a child of the graphics window. - TextWnd = CreateWindowEx(0, - "TextWnd", "SolveSpace - Browser", WS_THICKFRAME | WS_CLIPCHILDREN, - 650, 500, 420, 300, GraphicsWnd, (HMENU)NULL, Instance, NULL); - if(!TextWnd) oops(); - - TextWndScrollBar = CreateWindowEx(0, WC_SCROLLBAR, "", WS_CHILD | - SBS_VERT | SBS_LEFTALIGN | WS_VISIBLE | WS_CLIPSIBLINGS, - 200, 100, 100, 100, TextWnd, NULL, Instance, NULL); - // Force the scrollbar to get resized to the window, - TextWndProc(TextWnd, WM_SIZE, 0, 0); - - TextEditControl = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "", - WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS, - 50, 50, 100, 21, TextWnd, NULL, Instance, NULL); - SendMessage(TextEditControl, WM_SETFONT, (WPARAM)FixedFont, TRUE); - - // Now that all our windows exist, set up gl contexts. - CreateGlContext(TextWnd, &TextGl); - CreateGlContext(GraphicsWnd, &GraphicsGl); - - RECT r, rc; - GetWindowRect(TextWnd, &r); - GetClientRect(TextWnd, &rc); - ClientIsSmallerBy = (r.bottom - r.top) - (rc.bottom - rc.top); -} - -//----------------------------------------------------------------------------- -// Test if a message comes from the SpaceNavigator device. If yes, dispatch -// it appropriately and return TRUE. Otherwise, do nothing and return FALSE. -//----------------------------------------------------------------------------- -static BOOL ProcessSpaceNavigatorMsg(MSG *msg) { - if(SpaceNavigator == SI_NO_HANDLE) return FALSE; - - SiGetEventData sged; - SiSpwEvent sse; - - SiGetEventWinInit(&sged, msg->message, msg->wParam, msg->lParam); - int ret = SiGetEvent(SpaceNavigator, 0, &sged, &sse); - if(ret == SI_NOT_EVENT) return FALSE; - // So the device is a SpaceNavigator event, or a SpaceNavigator error. - - if(ret == SI_IS_EVENT) { - if(sse.type == SI_MOTION_EVENT) { - // The Z axis translation and rotation are both - // backwards in the default mapping. - double tx = sse.u.spwData.mData[SI_TX]*1.0, - ty = sse.u.spwData.mData[SI_TY]*1.0, - tz = -sse.u.spwData.mData[SI_TZ]*1.0, - rx = sse.u.spwData.mData[SI_RX]*0.001, - ry = sse.u.spwData.mData[SI_RY]*0.001, - rz = -sse.u.spwData.mData[SI_RZ]*0.001; - SS.GW.SpaceNavigatorMoved(tx, ty, tz, rx, ry, rz, - !!(GetAsyncKeyState(VK_SHIFT) & 0x8000)); - } else if(sse.type == SI_BUTTON_EVENT) { - int button; - button = SiButtonReleased(&sse); - if(button == SI_APP_FIT_BUTTON) SS.GW.SpaceNavigatorButtonUp(); - } - } - return TRUE; -} - -//----------------------------------------------------------------------------- -// Entry point into the program. -//----------------------------------------------------------------------------- -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - LPSTR lpCmdLine, INT nCmdShow) -{ - Instance = hInstance; - - InitCommonControls(); - - // A monospaced font - FixedFont = CreateFont(SS.TW.CHAR_HEIGHT, SS.TW.CHAR_WIDTH, 0, 0, - FW_REGULAR, FALSE, - FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, FF_DONTCARE, "Lucida Console"); - if(!FixedFont) - FixedFont = (HFONT)GetStockObject(SYSTEM_FONT); - - // Create the root windows: one for control, with text, and one for - // the graphics - CreateMainWindows(); - - ThawWindowPos(TextWnd); - ThawWindowPos(GraphicsWnd); - - ShowWindow(TextWnd, SW_SHOWNOACTIVATE); - ShowWindow(GraphicsWnd, SW_SHOW); - - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - SwapBuffers(GetDC(GraphicsWnd)); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - SwapBuffers(GetDC(GraphicsWnd)); - - // Create the heaps for all dynamic memory (AllocTemporary, MemAlloc) - InitHeaps(); - - // A filename may have been specified on the command line; if so, then - // strip any quotation marks, and make it absolute. - char file[MAX_PATH] = ""; - if(strlen(lpCmdLine)+1 < MAX_PATH) { - char *s = lpCmdLine; - while(*s == ' ' || *s == '"') s++; - strcpy(file, s); - s = strrchr(file, '"'); - if(s) *s = '\0'; - } - if(*file != '\0') { - GetAbsoluteFilename(file); - } - - // Initialize the SpaceBall, if present. Test if the driver is running - // first, to avoid a long timeout if it's not. - HWND swdc = FindWindow("SpaceWare Driver Class", NULL); - if(swdc != NULL) { - SiOpenData sod; - SiInitialize(); - SiOpenWinInit(&sod, GraphicsWnd); - SpaceNavigator = - SiOpen("GraphicsWnd", SI_ANY_DEVICE, SI_NO_MASK, SI_EVENT, &sod); - SiSetUiMode(SpaceNavigator, SI_UI_NO_CONTROLS); - } - - // Call in to the platform-independent code, and let them do their init - SS.Init(file); - - // And now it's the message loop. All calls in to the rest of the code - // will be from the wndprocs. - MSG msg; - DWORD ret; - while(ret = GetMessage(&msg, NULL, 0, 0)) { - // Is it a message from the six degree of freedom input device? - if(ProcessSpaceNavigatorMsg(&msg)) goto done; - - // A message from the keyboard, which should be processed as a keyboard - // accelerator? - if(msg.message == WM_KEYDOWN) { - if(ProcessKeyDown(msg.wParam)) goto done; - } - if(msg.message == WM_SYSKEYDOWN && msg.hwnd == TextWnd) { - // If the user presses the Alt key when the text window has focus, - // then that should probably go to the graphics window instead. - SetForegroundWindow(GraphicsWnd); - } - - // None of the above; so just a normal message to process. - TranslateMessage(&msg); - DispatchMessage(&msg); -done: - SS.DoLater(); - } - - if(swdc != NULL) { - if(SpaceNavigator != SI_NO_HANDLE) SiClose(SpaceNavigator); - SiTerminate(); - } - - // Write everything back to the registry - FreezeWindowPos(TextWnd); - FreezeWindowPos(GraphicsWnd); - return 0; -} diff --git a/wishlist.txt b/wishlist.txt deleted file mode 100644 index f9db34a..0000000 --- a/wishlist.txt +++ /dev/null @@ -1,19 +0,0 @@ -O(n*log(n)) assembly of edges into contours -fix anti-aliased edge bug with filled contours -crude DXF, HPGL import -a request to import a plane thing -make export assemble only contours in same group -rotation of model view works about z of first point under cursor -a way to kill a slow operation - ------ -rounding, as a special group -associative entities from solid model, as a special group -better level of detail -some kind of import -faster triangulation -loop detection -IGES export -incremental regen of entities - -