From 15446e8f0a4661a198345b26e5fbb4b0df59c275 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 25 Oct 2016 12:53:58 +0000 Subject: [PATCH] Remove all UI-related files, and move the rest to match master. --- Makefile | 102 - bitmapextra.table | 68 - bitmapfont.table | 2179 ------------- bsp.cpp | 680 ----- clipboard.cpp | 395 --- confscreen.cpp | 433 --- describescreen.cpp | 339 -- draw.cpp | 820 ----- drawconstraint.cpp | 1101 ------- drawentity.cpp | 648 ---- export.cpp | 732 ----- exportstep.cpp | 363 --- exportvector.cpp | 713 ----- exposed/Makefile | 50 - exposed/obj/t | 0 extlib/libpng.lib | Bin 316462 -> 0 bytes extlib/png.h | 3569 ---------------------- extlib/pngconf.h | 1481 --------- extlib/si/si.h | 373 --- extlib/si/siSync.h | 206 -- extlib/si/siSyncPriv.h | 127 - extlib/si/siapp.h | 121 - extlib/si/siapp.lib | Bin 63646 -> 0 bytes extlib/si/spwdata.h | 63 - extlib/si/spwerror.h | 64 - extlib/si/spwmacro.h | 48 - extlib/zconf.h | 332 -- extlib/zlib.h | 1357 -------- extlib/zlib.lib | Bin 100642 -> 0 bytes file.cpp | 676 ---- font.table | 1774 ----------- generate.cpp | 489 --- glhelper.cpp | 607 ---- graphicswin.cpp | 935 ------ group.cpp | 760 ----- groupmesh.cpp | 569 ---- icon.ico | Bin 29926 -> 0 bytes icons/angle.png | Bin 27359 -> 0 bytes icons/arc.png | Bin 27478 -> 0 bytes icons/assemble.png | Bin 27011 -> 0 bytes icons/bezier.png | Bin 27089 -> 0 bytes icons/char-0-check-false.png | Bin 218 -> 0 bytes icons/char-1-check-true.png | Bin 243 -> 0 bytes icons/char-2-radio-false.png | Bin 228 -> 0 bytes icons/char-3-radio-true.png | Bin 231 -> 0 bytes icons/circle.png | Bin 27376 -> 0 bytes icons/constraint.png | Bin 26545 -> 0 bytes icons/construction.png | Bin 27069 -> 0 bytes icons/edges.png | Bin 27374 -> 0 bytes icons/equal.png | Bin 27427 -> 0 bytes icons/extrude.png | Bin 27030 -> 0 bytes icons/faces.png | Bin 609 -> 0 bytes icons/hidden-lines.png | Bin 26946 -> 0 bytes icons/horiz.png | Bin 26274 -> 0 bytes icons/in3d.png | Bin 26608 -> 0 bytes icons/length.png | Bin 26577 -> 0 bytes icons/line.png | Bin 26295 -> 0 bytes icons/mesh.png | Bin 28445 -> 0 bytes icons/normal.png | Bin 26211 -> 0 bytes icons/ontoworkplane.png | Bin 26007 -> 0 bytes icons/other-supp.png | Bin 27260 -> 0 bytes icons/parallel.png | Bin 26455 -> 0 bytes icons/perpendicular.png | Bin 26400 -> 0 bytes icons/point.png | Bin 25742 -> 0 bytes icons/pointonx.png | Bin 26848 -> 0 bytes icons/rectangle.png | Bin 26335 -> 0 bytes icons/ref.png | Bin 25761 -> 0 bytes icons/same-orientation.png | Bin 26407 -> 0 bytes icons/shaded.png | Bin 317 -> 0 bytes icons/sketch-in-3d.png | Bin 26669 -> 0 bytes icons/sketch-in-plane.png | Bin 26671 -> 0 bytes icons/step-rotate.png | Bin 27352 -> 0 bytes icons/step-translate.png | Bin 26253 -> 0 bytes icons/symmetric.png | Bin 26180 -> 0 bytes icons/tangent-arc.png | Bin 27250 -> 0 bytes icons/text.png | Bin 26716 -> 0 bytes icons/trim.png | Bin 26748 -> 0 bytes icons/vert.png | Bin 26517 -> 0 bytes icons/workplane.png | Bin 395 -> 0 bytes mesh.cpp | 974 ------ modify.cpp | 652 ---- mouse.cpp | 1331 -------- obj/t | 0 png2c.pl | 41 - pngchar2c.pl | 28 - polygon.cpp | 866 ------ request.cpp | 199 -- solvespace.cpp | 736 ----- constraint.cpp => src/constraint.cpp | 0 constrainteq.cpp => src/constrainteq.cpp | 0 dsc.h => src/dsc.h | 0 entity.cpp => src/entity.cpp | 0 expr.cpp => src/expr.cpp | 0 expr.h => src/expr.h | 0 {exposed => src}/lib.cpp | 0 {win32 => src/platform}/w32util.cpp | 0 polygon.h => src/polygon.h | 0 sketch.h => src/sketch.h | 0 {exposed => src}/slvs.h | 0 solvespace.h => src/solvespace.h | 0 {srf => src/srf}/surface.h | 0 system.cpp => src/system.cpp | 0 ui.h => src/ui.h | 0 util.cpp => src/util.cpp | 0 srf/boolean.cpp | 981 ------ srf/curve.cpp | 860 ------ srf/merge.cpp | 129 - srf/ratpoly.cpp | 598 ---- srf/raycast.cpp | 613 ---- srf/surface.cpp | 884 ------ srf/surfinter.cpp | 533 ---- srf/triangulate.cpp | 515 ---- style.cpp | 872 ------ textscreens.cpp | 758 ----- textwin.cpp | 1040 ------- toolbar.cpp | 257 -- tools/Makefile | 4 - tools/ttf2c.cpp | 52 - ttf.cpp | 710 ----- undoredo.cpp | 157 - view.cpp | 120 - win32/manifest.xml | 22 - win32/resource.rc | 6 - win32/w32main.cpp | 1203 -------- wishlist.txt | 19 - 125 files changed, 37334 deletions(-) delete mode 100644 Makefile delete mode 100644 bitmapextra.table delete mode 100644 bitmapfont.table delete mode 100644 bsp.cpp delete mode 100644 clipboard.cpp delete mode 100644 confscreen.cpp delete mode 100644 describescreen.cpp delete mode 100644 draw.cpp delete mode 100644 drawconstraint.cpp delete mode 100644 drawentity.cpp delete mode 100644 export.cpp delete mode 100644 exportstep.cpp delete mode 100644 exportvector.cpp delete mode 100644 exposed/Makefile delete mode 100644 exposed/obj/t delete mode 100644 extlib/libpng.lib delete mode 100644 extlib/png.h delete mode 100644 extlib/pngconf.h delete mode 100644 extlib/si/si.h delete mode 100644 extlib/si/siSync.h delete mode 100644 extlib/si/siSyncPriv.h delete mode 100644 extlib/si/siapp.h delete mode 100644 extlib/si/siapp.lib delete mode 100644 extlib/si/spwdata.h delete mode 100644 extlib/si/spwerror.h delete mode 100644 extlib/si/spwmacro.h delete mode 100644 extlib/zconf.h delete mode 100644 extlib/zlib.h delete mode 100644 extlib/zlib.lib delete mode 100644 file.cpp delete mode 100644 font.table delete mode 100644 generate.cpp delete mode 100644 glhelper.cpp delete mode 100644 graphicswin.cpp delete mode 100644 group.cpp delete mode 100644 groupmesh.cpp delete mode 100644 icon.ico delete mode 100644 icons/angle.png delete mode 100644 icons/arc.png delete mode 100644 icons/assemble.png delete mode 100644 icons/bezier.png delete mode 100644 icons/char-0-check-false.png delete mode 100644 icons/char-1-check-true.png delete mode 100644 icons/char-2-radio-false.png delete mode 100644 icons/char-3-radio-true.png delete mode 100644 icons/circle.png delete mode 100644 icons/constraint.png delete mode 100644 icons/construction.png delete mode 100644 icons/edges.png delete mode 100644 icons/equal.png delete mode 100644 icons/extrude.png delete mode 100644 icons/faces.png delete mode 100644 icons/hidden-lines.png delete mode 100644 icons/horiz.png delete mode 100644 icons/in3d.png delete mode 100644 icons/length.png delete mode 100644 icons/line.png delete mode 100644 icons/mesh.png delete mode 100644 icons/normal.png delete mode 100644 icons/ontoworkplane.png delete mode 100644 icons/other-supp.png delete mode 100644 icons/parallel.png delete mode 100644 icons/perpendicular.png delete mode 100644 icons/point.png delete mode 100644 icons/pointonx.png delete mode 100644 icons/rectangle.png delete mode 100644 icons/ref.png delete mode 100644 icons/same-orientation.png delete mode 100644 icons/shaded.png delete mode 100644 icons/sketch-in-3d.png delete mode 100644 icons/sketch-in-plane.png delete mode 100644 icons/step-rotate.png delete mode 100644 icons/step-translate.png delete mode 100644 icons/symmetric.png delete mode 100644 icons/tangent-arc.png delete mode 100644 icons/text.png delete mode 100644 icons/trim.png delete mode 100644 icons/vert.png delete mode 100644 icons/workplane.png delete mode 100644 mesh.cpp delete mode 100644 modify.cpp delete mode 100644 mouse.cpp delete mode 100644 obj/t delete mode 100644 png2c.pl delete mode 100644 pngchar2c.pl delete mode 100644 polygon.cpp delete mode 100644 request.cpp delete mode 100644 solvespace.cpp rename constraint.cpp => src/constraint.cpp (100%) rename constrainteq.cpp => src/constrainteq.cpp (100%) rename dsc.h => src/dsc.h (100%) rename entity.cpp => src/entity.cpp (100%) rename expr.cpp => src/expr.cpp (100%) rename expr.h => src/expr.h (100%) rename {exposed => src}/lib.cpp (100%) rename {win32 => src/platform}/w32util.cpp (100%) rename polygon.h => src/polygon.h (100%) rename sketch.h => src/sketch.h (100%) rename {exposed => src}/slvs.h (100%) rename solvespace.h => src/solvespace.h (100%) rename {srf => src/srf}/surface.h (100%) rename system.cpp => src/system.cpp (100%) rename ui.h => src/ui.h (100%) rename util.cpp => src/util.cpp (100%) delete mode 100644 srf/boolean.cpp delete mode 100644 srf/curve.cpp delete mode 100644 srf/merge.cpp delete mode 100644 srf/ratpoly.cpp delete mode 100644 srf/raycast.cpp delete mode 100644 srf/surface.cpp delete mode 100644 srf/surfinter.cpp delete mode 100644 srf/triangulate.cpp delete mode 100644 style.cpp delete mode 100644 textscreens.cpp delete mode 100644 textwin.cpp delete mode 100644 toolbar.cpp delete mode 100644 tools/Makefile delete mode 100644 tools/ttf2c.cpp delete mode 100644 ttf.cpp delete mode 100644 undoredo.cpp delete mode 100644 view.cpp delete mode 100644 win32/manifest.xml delete mode 100644 win32/resource.rc delete mode 100644 win32/w32main.cpp delete mode 100644 wishlist.txt 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 c1827917fd2059de79ea78ccc9d937b806ec2b02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316462 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%0Dpi4g->u5xMf}&^vxU6{T^$e7+ly&sXL9 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 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 5dbb5c6f1cb16974ebb50a5fd03941239690f91d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63646 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 LQOP`8w1)maFXr`u 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 307bd088a5b7ed4484d151503888ef1c69d0c785..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100642 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 - } - - 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 e545e2f2c09f809364be7fccca516a14cadd0813..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29926 zcmeI4zi%By5XUDFAp8hP8Waf$Jz0PvMM9CbCrEZpXKJcdI;e0*Qo5r)QKUnW{{ANojcc| zm*w88B0q@8U{IC+cuC}=*F0 zdOSU&9nvF`A1QuW9$h`tNMKx8{I@6qM=Uol~?D^H4I|(Ak zX(P?@**UT~H=cWWL35g>Z5?{KDXIvGSh+5vmz-W^0bFLso$&BEJJz1&Ia{`{r&{ei zA7_vMVtiVr&dX9$(B$af>F^yrH8zp%dw!}q|1-Tl#yH_zaeTj&Be@pn$?tdN-rH3zz{K>t| zvxgCUU&))^^Y<55@GX95OoyNV?19ArT>#4=d{6)n96(UY0zN2!2M(YtrH2m+z-C$; zpg}1ed{6)n9KZ6o24@0(js6j!*?36u<)qDBur# zPyi1cKmdQ>g93Qq0801+ALz#e2WSX?@D3Ef0|#gvf8c`xc;EmA@drLAfCmoX1S;@B z0a$7A2R&}bO6%i2Bdin^^Xsyk|0&1jRVuA<83iUm)=KG*}hU~geBLAfeNpjQOG4M%sC z7frryLNOiy;(ath?h0-%`WmmW^?m)XIT}@^6lYTz4)~In`?6fmKFiq2Teis z0fg=^Q`DY28V#E+MD?pId5erd~T?RFxz ziJx>oFsVNDW72*$*T+JCngW~MZ^cfP&hO0L^BC3bl0vspdRW=&16|nrrMyqc`;@#- z$x6CyoK{)&a6C7-^q+&@4*-I{yeW^c^%8^ zSYF4bS;v}d$E0$YcN=R*WX))<2{V_coA2%KXy)_xH-m7GlD~2Uas+Y&as+Y&x<;UW zFHk>6U!Q&H;x@ilSj2f(KT~okpR)hm#w``suKbG@fBEITGKN$?XXST|b4sphKjypg znsW0hr~K-{5AzK26-~r{TyJvoqdi=WimtW)%{*AQ{JCiUTYoIyt-L52&FCJ?lwaBZ z)m?udKYm>HL28VZKkJcdJC#4b%5P;Kq}n%r_CKDdsrXym>eR+VbE?koa=z^N$9&5E zPd^H=cq%XFPyD)S#%ub>SDMZCteoSYEMJ;5=g<1(Pjd{rl)L0en@q|^pZrO+ z(S_gUTbH_&jqLyW$YE(m|@=HH_HxAv84KkDVS`!ADgb-rqF{M@nLzU?Jq)Y?`$ z>1fDOJe!|tBeiGF?U!;9;u9Np`6mB7^k@D3kpBTuksNmb diff --git a/icons/angle.png b/icons/angle.png deleted file mode 100644 index 7001a9ddc296773cafea0ef5e2902b0a0a1f9a72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27359 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 NcE##)iP@c}{{dA>m_q;n diff --git a/icons/assemble.png b/icons/assemble.png deleted file mode 100644 index 5db2d6b08d74a7eba9571aab2cb5a81ca823a260..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27011 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 diff --git a/icons/bezier.png b/icons/bezier.png deleted file mode 100644 index 73620cdd758e13b96e9e2bbd18e3501b92614154..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27089 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 diff --git a/icons/char-1-check-true.png b/icons/char-1-check-true.png deleted file mode 100644 index 7f6a42c554193d2abbd2b4a8023009fb8864be24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLlS!3>*qfc7zXy85}S Ib4q9e0Hf$QhX4Qo diff --git a/icons/char-3-radio-true.png b/icons/char-3-radio-true.png deleted file mode 100644 index 5322f587264a18b394550c53b8803bc5038d9612..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl#%q#+J&w=`!859mMBwb+G8Fuk* Q570^mPgg&ebxsLQ0A3F|lmGw# diff --git a/icons/circle.png b/icons/circle.png deleted file mode 100644 index aec46a3a1016cb839811aa8a07cab9d4d8e0429d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27376 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 PyIsC$bD`wittbBn>2u8s diff --git a/icons/length.png b/icons/length.png deleted file mode 100644 index 8396bb147fdd75595f19381a9e143c0235aa483e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26577 zcmbq&V{~Rsux^ZrZQHhOClh00+j?U=nb^imtch*gw(aD-`R@I5?(frU*Y4fBs=KPX z*Q&1WCsJ8a3gHLt4-gO#1Q}^@)$bbnKZ1e!e(!h)ynI&>E}}B(FyF-&#x&x)4eKba z?E(ShQ(p$W&`bzrxOUWacH+_8n z>ia({p_%xIJABE+Gbt1}ynkQPhG|5j=MJTK{=E&K>4?K>2}f!1Ku2Wfk6*pB^8O*} z0I%&!q1DTU`)7B+JWi;lS>dH2Kmc#Oz#&|>;5SN^8-(xlB}J(np)d;Z)tzBVhT7bF)F zGMmtcz$cHf-bdYnNOksfesl_t_9^Za;mOCoihT*{CkM7E(#a}6$BFxgwGg7 zgm;BrK|YAc%~<|Q6HBsgjnh8`%4d4=XKwPAOeuSg?hO;l7D^)Tb$=nlh4Wki{$BgI z-H;dNi(1^Qw0=P$b2Gt#T;~BqkgB>niB||Wt}%KIo?_w(9%1L~4PqVzsHB4MMX?{r zP<`O?Qz5;fB@m1!@ue-;f-fX6GiZp);eSl3x$$!e5T{A9yGb?9#4ZQ*LDd6<;ib$4 zV0BWY7O+@U7;5ut9PKk@uy`O0GFX-wYq53l23$o>HL-OkIppH2)j#22sxWXjOKR!j z?9~r`Z;DtYdbEz%AwU0sIPbZxA^xCZd_!Y=Lrr4;Rfh*^bxw(vcE&bnmq`O5rH+a0 zsUuzxxr2O<-2ba4Vs}RzMH=Ef-sl1dV)rt}Z^p~qCXX=ZG^>M;8!+=i2c8sDTm8SUqL&`mC#Y1v6v%y3@}ia|FAeU$(U|P zWdC7}%wf;!AsHw1C`<}i7!jl9K=ABxq-MYBl1br!gtMkH{ee|Mo8Pol1%CdB;YI$H zEBAp(eJe?8L&o}nGM$bIXEdMC_?B5xVg)MEmxKK%@K^#A7s&-L;(00q)BQSt%>hN- zZqX|qE?IV!q6LIJ;VRzla=yk`O`tHBnm3C@lmnzDpt=1phs~tXJ(F_;BX;&Oc{&2= zIzZbxvTI+mdw!9$WQ6_*`UMAwEbhTCdkNrXnWhoMclP7mFBt?ahQ*-4W%}zc66%8= zClehY3!jA(nk0)U={Dp?&nc;8)s+iY1!;NKfFnjJTXzbR64`%HEQS+fN{rvxy7;Q1 zrC=e6n>1ZnVM~T#KO=y`pFuEZO8sxv>*9U8?#^atqj9v>ip7>X1jeYSIx2Rw_Lns=B=o&iL|iTsfshEfNM+by3|+-3sNbIT9?(YTuuH>_(AWK^ zZkYG$BMnS@=nG;a04NVMdpfGk=^nG#+Nd1O8E_(hdwwGq%Qi5^tLqBmV}>4QFn7ZD zgv{a{4cVo$`iD((q0UJ|Pg)&GA{33Kpa-&ElZ4Y6azCAKbyvv7zh&^h`-c zE3+di!~5aSYzW91){a&$mNv54U#*0-wJ&Xo$Os#SrGZnKGo^-wVCTXgp7G+toE`yr zZTB4;NZxqW`lFIG=&ZsOpS0;jt}%YgLPdhgCPu?26gep>6is5Po8A1vLMjC_bb5{> zb-|Fa5TnFxMZ=HELZljZ%tPEVzWjvR%wBqMyR z9;PSIOCp{+aZi@va8MCxR^r_hGl*xK63zDwOd+LL=D+PR=LU4F=pn^n9qa}>EE90dmp zq?mCLMFR>ZtNPf$i{IAe*_~1_foJibz@gMZoe*dz1o>QNI!Qz7NL!R^s#*CfPv!|d z;=q9XsPRrpkP5p$_78H~-DL(50sbd(>seeL6jV7R0aqJVUrKXobkrzSa!lFH5V436 zX8~ubX%yBu*OEz^C({f;%qjTsez93mU*QAPyV21RhW%n7-O zgjd-hFhD{l5u13`a>-?+pi-vhjl#TKj-(LRu;lc-;>prl*iq_*vn<|COOWL^Ce2w< z#K@dLGbT-2^}ptvOWB$YCD2M&;Y=m{Q~mk-7<2JuXwS?)ru|^&BD(_hp;5);t1KT% z;NC(h4V?@C2Ju=N>?)9SHA&Yb!8S?4QKDPpXpR@4h{QvFRP??VOpwQ9`s>bbQ%a?9Vea+ma!#0OWcpI$Nyv-i5-b%UjDM@HX!nj}+AKRV zJB++PJ@iot|C~*>n5R+fCDVXJgtOlR+C2@^U?X=Gv!LS#4aaej;sD6!tRolj%YBXTB_&#zkH@5R6&im|1%U4>~tsJ|#Zi`sILb z`$E6#TEu#BsQzA||l9E6|J3f|edJMRS+&3f|nGhgTys zSX(g-A6RCGP(p>_XpHbtd z=*_b3TeNZ~Fpn>K&D5BLU9xDVkn%H);}9g%UQjrmN0?xY*x=nuVtKpI z>+oFR!InokdlLE(2ih#JTkR%vs|67XFwKo#Pp8OMLk+EGb03H%zffc#ZzA8w56E=v z!e^fpLSh$-lF05^kLhEGxG#e}9v7K^xfeD`w#bI&1aJ36kj@rT;5J0eTniK3CzxS( zMSK5QsU`%$`-o9pZy7wzCp*&s8D+K%Z?&vb$vYdYuV%QMnj?4K60AY-{>eIbEgV1i zuQ<2+={;Xj-H%`K?0q5h%+ZW)t={xH?|#c1ja;@H7HNN?88HZgl`(mL0ZRF5T5Cr%$N6xx|o|IoR*6D5xtj=LV&dFBu6`^!l z{&t(Q7CQR0!xK4**{5U8jY@EODd$B^S>QS(;Xk#>s;a`!IMa4UtdoC=Ws%4J;bhZq zNM){nyq!>KDo%{u){1^ACnsIUPwYAORZa^8b8RuGp(>bB&$3nzPNQBpk3lG*HtG+{ z3IX$R1k>c$>He)^w1L~=OmkSKT$aC)wCRO%7FUz4-I%da2Vzx8`;{k*TG$v z*J2wS6`xM{CuUWxhb%^IL&Eh>pn338%E_y?$4_-bJ+6Pnb`V@+u-i4CEbG+|sl3%q zNfW*p^=P*_M2ie$owg?Nb3aFHwaPj^2}5$NHaq=39>cC?C>M|=ZtXZc%WRM<+dZH(KcbC24ax@v13Vw)94MduH*Vx`&WKvo`8#wa_t`dY2cH_`-tD6-M$tg*$^=Th9_3b-`%; zLN#ZT)hwYPb7Xlwyt|Z`8h(I?vpJ#zn5 zL^Yr?_oY}+#wue7@y3_0(TXR#QP*@w*L|&1z6j_)`S`IvPnKdXKBbBO7cJp{O?t|} z;T7S^O{T*Y@;E5f>BllrgM!TmjS_fFJKm07;R)D9$KdL#fYY*%=g@Fe0bN)%cAN1D zd>AKfSQl2txMB=weiXS?eHx@ zE2hbJO&eFu-L`jPeY|<(?D`-3I4XzEz51I?%J(1p|C#j7E@l0Xy>}7SKsWWx{`BH* zU-QyraUGn&_-`D-)Pis+#KHA=f$jF$DxfFfuG7TjKgOpTh;hHXKFEzyO2!LdD*9{->X zQ?xj-kQ3A>t-${zWv_29?!0iQ_eGpoZx(a-jBMjpvuLms=!;tYFvm0{&G|A`M)QO` zQed7M;pP1(7ew3T>3}N1e48P^a<(#v^lcQRlr_O{QTUT&=#zZ$VE>?8&1J^!Nab3# zc&EW}6{vS7a8iJKW77ACR74_iuQ5wttkES)STNQ|@yCa6W|&4C+7Py36XaRy znz5kzdVSFiHb*RnrC_TBTbe9qUf7-ulZYd-$}wAoONFlNM#%fmPw`{_00YClkwhh+ zFyQAQ*=1rn^@S1!?1tOStB$x~za7rCxbD#KGudtoz~L3#7ZcDTceQRHJZ%wTTkA0U zvwlkesS#ZuT8IHRnW7-^I8ufFUeJv$1jc|*L3lp{J=O?LyW5>yIwTME5kiLARN&ML z49rR?S{A61fu7>OO4|L@$m)iIfS7f ze@prFIdqNnRsg)uyNTeozye663vy?wP)1Zwj}EbAdF0ASs`v4PYQ0hQ?2YB=-oL24 zO0h#}80;&>wBKHvh1R?aQ|EK)y(HRX+fgQ299d;t(!m?~nZCnH5YW?Ffu`zE3-)>s zp!yBQ_)0_{ZK?Hi;AF{d$5vyCeZ{f2CRE!(e=)BzO6|MQ0OGi_&^5W)Td>s_i!dTd zoHm9Ee4!_2tsbqLhp_Kd_BM1dFC8Gw$I5Hw#p8k4jr(wB2+EpWuc-vn>YI!3qMQbU zL%wiR#0}d?{$tuZ6-Fd;eR5`sZ$6g&>p zyZ!K{Esrd(!=`OC-%Xus?UZwK!DSr0VK-KWH3&(c3jCz4FR@<$wGX<{;As&=#knWr zeb&ce3BzA{%%btn%<uiX+{we|1h;U zRISJz4f2+iwidIsDR2GqfjQl&1GR>PX}!9^ylGm~51R4=_;q@E&Rm{MCB85*Edwli z0{Mc&&&n7Qy4yyLW_N-RS3=a#Gc`5rO4;+?=hfQ{j{|#^MA3M3F@4>MK=>!Mmu9*s zQs!WuVehSYqFwu%$m1ly+4G)K{Y_`Q3SdGrzQ)pUIuuAK_F{XR=x+Hj1K9D$I8s>I zYHj7R;xF%%F!SSH@aVJ9+~eCAEtm&#I1MewU6N5iCls|l~ONy2|vU$`VX6kEHPaat~WLhHnKjRQ+x&*k^%jmLLTpKwLpEV zt$6fK|6tDaSL61+u3n4eX=g__-@J1Z$72$2*yi=74lMgQJ!{Ffp;jCZfP(0TUKvlC zNFB|W`LI^=NceU*W9ew8l}V$7yYt`@E0L{jsq2;h-a>0f7AGYWj7eQ(EgWm1?aM$o z9+4hGD-oem+NXa9osSr0$FhcR_z=rzkS)P>LBi+HiEZZQNYj7y+tHCnlF`>ETQSeZ zrVf+U=RWLFSJFTW02d{H?qWO3_mlNuKj8s;O)~ON%h#lrM0@a1^4`&PdVuoAF@=B@ zvx^Rs5y+_0V7wa@N|Y4Z8Hm7sfv{fmePdc9WY}6Q3+%|{cE;z&eY5=xahABqGOY!w z3AwwQ)ZJSd$1VzF(C>5=P2op>5@I|Vb5od>k+ldW6rCsy+bE5jIU*Fu(3=0qc;S^Q~36Q~L zt+jS>6M`WYa-Q!IGC0j?z!v75M-X@8F0@V%)F@QEzUs$+wqB;?sy*8R`?&A1kkw^Z zgdSc5%E^r%TRoUIFd<#xUzUWK+j&pucI+$cN1z=HYZE!>)e<{++tZ=nQ}bGQrLtQu zY7&Y|lYg=Mn2*m9|08<{A#V3zrxi{gv}^_d4f4ExEezPkH=0&rpu|8TeVgfa_~ApK zAm%na)=Ma|B|u8@{8qm4?wQxgrPaAjw80owB=*t-)$fVU?SjjfYz4EHH;T-=l+fru z!0XT27pFVyxTs~ri9b2hIXn8V-BRRsO5d`LYvX(u8{UqWukm%&&%JdT$>*YW7>V!Z z;uo~-)d;1{>5AV5t^oekBo?LmJ6`c?wbkEVUKN{+Fb`Xg&Cp%9t$qdG>*v~{oF|`J zRi>4>h^B?;rUi}elGKJ0T$_s0rjTYI7>G(@=pf{B5fTw2%T;A>i}#2{8-*ILO|`jl z3U;bt*EAy^GJuajiNFI{Lnoz8&5=V*X(JP*&_*vWn5o>!k&!FnMRDazd0=IM4x)O_ z4+XPONjF@+imDawe49T_)g~ntmw_NZmxtuuI#IIcdyK;vWB$5@gqJS!1lG&={Y^i+ zNo zX-m`1PJXP{h-($Ow5ID1GS00^wB4?;XI+RYz)i{~4C!eqXL47XQ)Hl*jnmlMPGx8I zQ!83PJpV{p<1LJo5zc94t&mS^rL2%oY{eCs*e-hQE#Dj}6j&?!H-0i1lItTM_<{qu zt2P!GQ=rEA);R?`xGzOUs~8WvE|xX18|xPH+`Oecd&;)Cc5STREr;~6TFQd+Xp6kf zTe|wUx?|`!eTE@C9m;c3PClxFbR=(zRu)}cjX3FGvs^_RT)KQ7<&q=CS)*0bvQ__| zk@Ucy9`O-~(BRp#ZcvWi@b*STifu;toRjvv@_D>Df^=KTbsiHAz3BBOvjOhj1V0lf z-Q!ke@P;-VI}p`&r!4a@IIHk$-|O}wj!g0Xv~LJhv25C9Oj6G4UuDak*bY!sk)-{B z?!Q}ZYMU}_T!>lbSVC~wIPh1s>9og*)%od&)RMR%HhD~!wuwvWPPb{N44}AmYSqnn zRtB#wW32_~fLAT+Z1lKRgr5FFoA2i=O=D)ATozMEm$D<3r=M?4{d*p`M2D@}%7=rp zDsHc{t7x`W#QL{8-O7q}d`>j1B=ka-ZomC!ZKWIDBCf4P?1YjV3fyM?qPjyI4nHr-C*td#vn@u%mExTM znqJVjE|u^*0=kR}8`>}7XvPUVq_&6J;48c#uooeGP|coeHs6^@g&`N?`HQ7@z^`aD zwsU_1>Tme0@f%V5M!)nG#}R{=kuN5BU3Ma$GEqT)pUC4bn7V=H6E6r6zORA} z8)LP9O#v2h=g-y%C%o3Q;Kitc?Kr>cqUn!XlEl_x>g!cS z7(DenHa@HeIZrZ#Q{eZU)tXxEG6)aEFFt zi-(on33#zMC;ZH6bsaW(7G4dlYHvJ1>VkEDb`3l058@K>x%YN>{Hp+ol*^;VNWIBW zx_|w;kxF2w5O4DQ*7&-9o(Fw+LXD*2sc3+KXV6pPNonNRs_tT-uI`hd3@Y@Gr_(1i z*yyIfr)x9Vqy^l^`F+pafU9oa_2vZ>_3U>`>%V%BaVUC359@tG@inFAjcr4KRLMa+ zn2%kW`=TIa_Zx%utRSHth}XhunAeIc*sclOWUt#^4HJ1qE>>>w;cP(ok>Tl&&4@5u zclSN_(2(%S&mu8-rwGSNu-5w;z)b`jeecORmu4#4g)>zD-riPh_R>CgPQ7*|lL zaXEi6?=$e-8O-sM7_fVJx=Z_!k$!EeLuMo~>mr8x^lJw;lYsCyeCt5xehnp}Sic`DA$t*=JX}7QpfQ>j^yHeBB*3Q0 zazk=cQ>F#9u86T`1@42vk|8zN>q4Wu8pLP|s;S^u65+~@-_KGN=Md6eea<|-3ehju z*Yx3F5Wi4eSgA`{Z&l`via6x6mpKUgdVxZNA;kD&?P}p=khOLxIq{E|XWsN1qC@h8Y7X*;WDTz&(Ehfo?!_F-T1%u?~q~G zye&KOR)59(@j=?cDe@D2nj252v7uU?XZHA6WIId~9|KzdnE}e(?Kb%SsK+Dh3s!k_ zDUYJiXpK%5VZndNh3TlIX_3(Rb#?lS;c5vp5YGyp#8#&5Sp_HnFkPr1 zeY{2Rcbo098;xGi$SnEO?3%WW93wC5<4zDn?lnz-z(54Z zhc_w3v49M`9(e>>4EtY$tn=i1ux|w4a{OR_G21PBE$ZFfwIHVsm4w+Io7xnD&%5hS zf;LnyU$I|OINaPbClTPi3(c`n7t7(#`{Tgj|>xh&?r~uo-xO%zzve$$(wBp2dcTMQJWA`cjs56T5JZ1 z7mH&(aYmHo2v$9=h|sa@G;^K)%2d=ec2GPN?QkoT_S8K=6awY`NGn-6<_W8m{;-n| z5p$5b);L>}nRfBOz9d0rHV%%|nm2)npU-Dt@$J@4ezIY57t;uM@MBmsx=F%43l=}s zKOjh2txU^{Nw7z94Cr9EjR=OThcHsL> z`mtSCRQ9e{(N{p9)6(;Jpz!~IPudxEu7dpAH;WO|qZnkv=&tlny+-i2S0jC_*@}se(g42m}e@X0cFMyaK z(^N8k&r|4n*>Zm1GHXnu+NN5$di`aw3j7f-L+KbI?O#nmFQPILaWHW{STVIEunkz$ z0v>0fxgPg*7UT_sW#d-}a81Q5z?JfM#9MGzTUQHw>R1hAA1>Qa4a_b~EW-)hZ~s-R zzqgJ1?)JeTmGz&?o~O1{l

@JWAC&xB&_Ww@TjEV&vR8AyoCvqIRtwT8jz8p~7@ zB4ISzh$Q-Pd;x`qQ7`#eN6wRVX%0y zXI?~2_VU={vr-i0^`0+~ScJuNZR@d42MbdgKe&G_G$wFdu3$J+c;o{FJhGJfXoZ{&nbL)zmjb}94emrlpl&HU$&sUdTS4W@N= zA}WHAaBJ#%M63@aC71m?kn*w8I{MA_!(SN#Ror?X{GsCVS$?#U;ppkm6H!!pjyy*R zHewrx-rLfUm$A>c6D(qke=+RBUbDa8z!Zt&P+Df+JrK0wpUF;(-R~|f`0DN1sIxyF zm`tWg(eeW~=2Yc)ZUZ{sdv89_ujwQH)UDvq>g{!$RJb%uh@3E%!qklyv~pV^_a95{0=ITM<7eN_IVOa$B%{c)|7v2;uC-&V`hA~__wKCB$IYP-hrsm; z_Pt0+%6`QKanypI*<_g~n&#Po`1S_x zFohchnWlkLcA9^t^e>fq{Zenw?c=x-lu_a>RW3QvdQWu#_7P$0p;wMQXR*kTloI{n zvr6dtiAWGZlgFVjLF|{SXLVLU_FcnjpqNbMA5n+j$8RV#4)kXPa!7#@@`5y*`R9li zb4T)Ur9hv7+gO-Vp@Gz00?WyB)XCIN* zVAbo>QpAvbUN6#SzkB>7o20%@|8NfUMHvVfZbYgaSXgNh$F8|_O zLV~c9+~Fsx&vfBve-eW^+dm7s@QTddK@6Fg9QeM#ppDzFe{z`CSsGhcIm#~{tOCCc z%L(1iW;f;IPH7OEBt4MCRH=rJcqM&E2a66@JM8P97N_p~@SPMraKEtdkHnndm>SDm7EShBUey{=%klK0YS?|CNDBRvcl+J1 zZCb5kk{nOH9{xfsS+-Plj=b-XxmK#&+#}LZZ?diC^W~j~!QL9?s3+VziPPDGwJ*{p zM)0G|3MsCIU2Ko`)y^j0v&E@qay!l5k6SP|pGCR1AVI?aXdG^;T7CkQn=fD_ zTeAX%8^|LgJhIa{HWXI2qH4cq#{N| zKtKWsBxzd(#~`x^Nwg7BhD7F>XfuRKf*^!BNC+X20Yb=}b1wbf+wZ-9zjg1QZ>?JE z)LM1Usj9teKUKTxsZD|4Wo&*0(7xVVIbdCjieRu)+GDi#!bs`Y$T`CbGynVB_~U1k9kRQoZ)j^p2A)Y? z>~MWND02+4CWH4SadX<6@7?oB%F{o5u+v!Q@~L>_hKOFFw(X&yZ*X$LMV?Hg6HyV! z*50H!b+DoEc2Jh$l*r%C4=gR#KHdN6SVr4Hq(}Vf&g$$>*#+`X z_{cB0Sf04E`XXjB==!}-i>@_Se~cdZ{-P@vpl^Y^qP@=8&i&EeC-MQCr*l5B0y^{& zh^@04-46RUY<|qTbm+cj$NP(`)D!Uobdw^?uS9e=!27ogUQj6YwmUwHQho?S$ubyUQA4;@`1jjFkt|9Z>)_oy5cG;?7n-jAx(9|=Z z?n4^R9RIN(fgmpK$#X1!<|y8NA*}`&Xov6??>TN$FI_$-hMQeT2-g8?TReL*$m~$R zt?HN6f(n^&K@_utiJJNsGx>t)##ap$IIylwYclS#Lkt%*t9`|94pd^MaKh=XK{?TtuHoS z%bz=Ut~dnMb{JbgfghVyR$bu{!IV6DTM9x&zY=TxEP)WV;pXvKDk;-%zn|tYH*mnq zQqGllYB&NRPK+;&nh#oC`4zMNLrB-M@mfwzIY%UoVUED+)~q{bfJNe}m&zC@bpKbed7^~RpixYkkusqz@6)l+ zp9C^zr|+^#Tqe$ag|yc48$b{SuXspdneWr79GV0hc!^&!^Hboirl5ju0+iF3;Bfag zvly|!)?D#1C)Iy2RXJtTq|356 z1>pAcA2-&ihZYB&QKzoJ=il7YH+6T`5g_C*C`BNdMhqqaDDKZTvY7Ls-}w6HGbk2* zczt`3xvXKc2X1U*E}7Ps^Zp6%xqm>6{pnrUWvfc$X@_^t>Kz|hm$>3LDx>9HI@C*g zMNnh>5|Ro?d(=wgXzv?CAPbFcyx#-NM}F}1j$>;hypi8brXQI0Ieb9fxATsM)jK+L z?SY8Yys!$%OG-CQ6Xcnx*R}z3c2fNn?o0uYsX*#Xf36<32b}zOuXYiYR3h*geRg8` zty_-CVvUIrC$%mp6n*_Wvq|n6Y2+W!W-Kh!32WfZ{Bm9BgHGmsl^#G&C{pGvxlt2} z--U`4KhzP}HlGzdrU7=4z+I zd3%c6tBaurdyDvbUIB<2xxb{+`8Z?9en(KDQFQE*JfUjA)tfOW2R1Ri3Lu7{w?k=v z`f^gQp;UK0GydhH#JqKWtdjow?P?8B-CJk@JujdQerr4^WY$YMz>oYfaL|5RzRKr{ zzn1qbcLA-OOFJ_w2K)-vRY9U}@SynqV` z{Av&OICYFG&_(Mg#cAklBmcL%il>|es5bceGsOX}*{FXtxYxZuIsf@hRl!l*L=_36 zi4$H@E){hbq5elQ~ZAuAL{y=Q6sTUHVA!mrvZX8pSd#ed6dYZt^7W5o{Z-HY8 z&Z3vClE^nJ_;LKM>plIk)VS^rLG1%C?X#j8A7)g>gGH-S?jV(UHs3)RYm}Y8Fok!leJ%6VWHg3SkIDn1dM-rTprrd!M%kY7h zUN7k?ArSkoBeef9o`ZAa0T3Un&(8U2-Bdk2CCc<#zXq9Z&!Ej8ihjx|HE6Lgq#0`W zOr=L?Zd6|TJ9lJI0U9j$RH>C&HsPWohVcnF<>pY6mJLS^p9wf60ijfP)Kp35_0-zYe7w3c^M?>6rSq$zWFXCPN>g zJAP0tkerU%>9y_`BxrWZW>myJ`b{t8N#`S53Yp>q!Lmm&5s5y?OVIuCK_Lr*^g~V@ z#iMGnMD0_{x6;}z{9-|s5&fx(-Ip#<-w0_+_lmPR(R=Y`Fr|w(^DVK#!eWnVcvf`4 zF)!=q;q$Zu32oo7cdlW*LF5X{D#`Oh%9N|LXMn6WLjt}eS7swxC>onu9yz+SJ+Gw61%tD{O6*D3uo#Hp=-V%4z$)Ul99- z9?_yCh9^Uo2K|((PPcvIyo<`IBy_4aIoO!gDj z)_$Uf!H?G-UD$C~wt9HyAkEF_c*WXqDd0OsVWfMqEPaFhL0(R0apEhI^Wr%j_u7+P zjCDSNk}S1&jG0MA4zKEss&(*bd|%ne>0g=@-ijzPptOSSq8q~c4=_K_-*lxSDQuFz zGvlWviB8;yJUo|NzgA~VKu<89eh?{W(jNEJxlYXt?#A(YsSmmUu{qV)@qolOO9maYe$m}Z`oG?2Tc=uF+nc_yFKdM`mknE80Wf; zFACxuTIv!QnLkwQoQhVKmtXXL@dohG<>pNhBa?R`!QmIfh2+Gt@ao9i>a_;bQPpjyhD<Ssbwkra33D437C`@CM6u~QaS$4!o`g4n#%9_%J4I!}_fRMm0gMmm+z1UbHY!2vSh z0;4c&j#mTA$kR489lgQD5!yxx-+)-K#hy)ECe4pWzp2O-MSV?g?x_fLZ~(ine?w{* zHHqaE#;!!y#z!mDQ!9ssV1t<)_p83b7$DX!6wa>c1 zu;;-Pmv5AeZ z(-cM#OMXJiELwbGCA&UlE&fu}LxQIqO$FCA09$AoEW6wmo)}%~zn@|+N8bQ3{)tJT zNOeUN^9jMdz%*l#4H92e>)NfX&BWi4?|CO zpOT7cw6eECba-QuLJ_C@-9gZ@g{@C`gZwzwy_339bK0ha?LPL_-PR5)0b{Uocy=!L z?RR9Z(r9jT*b^j5`kY%fm{@gw_0F?_mFCGy>Hy(wNu<#Se;=Imj~?OkCUKHi&df(1 z&m(6=5p5t8HE{!&=u!h_+P%C>ezlZ1!P6du$*JW6`w!zji36ReDEr{I&1>LA&F34& zmzX0XN?*yMEaD6~B@9rySIE2OX>UF&ODyOwqrfZOMuU;=L~bZmk};dwG?pTqQY_wc zoyzmH#7A8@337UFYu`{*T4d>Wv&KKIIst=bDj7bMX%GDPZTQwdpSPcJ(Gcq z<&rKy?rz&`Zs6F=raPkUIcxRGZR(5VMa%@oMC|=8VE&j=xY;wcxg3u`+65o1o;Op= zT;@r@Fc5*10tCK4Mh~kKgw1HS!zldes@C5wsXqSg z_Mb1ap}uR@{9N_lE-v-(|DB6FX0_SL^USWZdklYj@$TiXX-*fGUJ& zjKy2v2IrICZQTCnnwL^Zj7HMcf2a7f&1eu90}xtCVxSSisaqf(GOgs^yA1 zXk3@E`K7h_H3;(((D7>y6}Y1=2ph$OSl`Pcs?%lOk8WLmoOCu$+M9BIn)<4;I+1Lni(EmsKf z54-0$x0NZg&@u2NdwW6tka z#IeR&)Mvt2`dG?K>2HwUK*_rE%*c1o15y+n&4tb&A+|Gdn4X_vft{Fnot41zWoNs=n z(xirNk+(ph(1CgR)X=86E#!yH@y`O|6G02hVZl&H5C09(36hszQM?<>oS?!ECngSK z`m~18_T<7dqzmobF(@;JT|+k|-_#pUT{`;<`JQ4NrA|#q=l|J02vKeGv=JOI! zQ+2`nOeyOR^$O5;1Ag!Btb_M{S}if{>txxQ#wV!{{pgKZkq5Ij)L276o#G2U+&o4kdT%ISYG*?4!d=PzG9Prn0@1^#2SfuN=k7bs}d%gnAWt;4Fd-kv6|dNpht33dC2$$MmvLhBGF zz(;w50xKU-|K7Iu8*$ZjKns4Un=q6AIu{VD5e($G}hJF+-CC51iG zd7_Cu>eT+>WsuZRZv}h5nvFQCb9cs>lqpvs(p}hVN`~@!6TT*lbkagQcq+2j&Y9>` zGjkSstdvNJA{OS{iVeS^o}KPMG9e8D6UY+6*9Cqa0foED{zqe5w$7!oh9fkEkXpB#^kBg#Hi>t(J z$>p*1MdK*bEC{V)$(}TCx2?MeRsB3SSzkdLnJooJW*Z!;2S%L2z~^IoqX04DlTvgk z$zv{ZlXZJ*^!r2N$K2Awh*h4Ntv07Ha@I!45bda1YkY-TXgqxEnfyRPuT~k2nCsck zDV&ftI1>M=V$fFbE;Br9y*aLY=nbi}HXZGtYJ-pXV!={l{v7NN1|LK(gx~uV63%Wp z=`a)Z4)g>nU$~}=w~{y7l|z`@?IQk6`9_S`1ODY;FJeyE@f$74Xj=)LTr7x@i{^mw ze@|BlQ}Nx;a{}7XlTRmP^LKUPnbnZP)Mt(A`ruIm_KavO; zzQNI}1p7qQ2O;EWOt#1eMKPyDwz&aWBlxaWH1GthK`QDx=4vtX6_d{$N*?c|h5vfy znWc~&{@EAw2Pe(}j}$Lay)h>A?W619dEda7;SY|CRJ)IJcxs6!bivZ=avWbIHf4%O zlwSc~t{nx&I!PUHU_#9E`nSpd7bgBEG9GR;X}z#B8>RY_9&m7|*UZs5U>Z-F8F%Sg zoZL!_cJT#YRmGU2r^^n|3i!-=%((@2&CYtO49s%_hv_t5$LXxbExjHkG7rVBYxz^7 zQ34E>9~b{bg?&wAm`+;Y)&_UEBxa_h!g|$rPmY^0F@xtDEw_ek48?~Uo0D^S(xVc* zb@s9j#3cxGy8a#Ni8Y^6=D{l?_z|d1byism=xwTQ5C+QD#{QgLHgAjuyftRuTeDQ( zng(|}KB+}!K^uUR0A^Fb51E@{+PBMPK0C(HfEDBdLIY6KOmk$eB?tMvf>iT^XIQ5@ z9uer?Ad8;V3DW8jRoqTSCGD-@Cg|Ort(%sZTgUxwnV~&s+GnDTM;OC6HZmgx8O&cU zKc6t^ldY>rk)89aBVHDjcFkA)d1YVC^Itgq@9tQqJZjv|%;eyzbF>GE6n%G!7s6=n zix&v7#tF|u%#)tn%v}5Smv>VCVtFfEhCuC06f%IAhzm8zDKdZiNr@W zG$zHjlN3gL?pdPe&B>LU4l)~J z&)G-LZ+2FY7VVx5Hn%>iv>o8a@g5eJ z{Z6*dF4=+Tj6wL2GGu26dCTl&@8qlT7T}sUe@J)lO@6U&HQ4%%|6afjE&d-ZU`Jns z&maCZ>FS27sayJw`#wCb@$T8ZtpvAN+Wf#r9n{)CABj@;o9w|E|QJhu^eFBwbG_dPBssrmz!erGO7MW!;v*(JbrDg zG$m_^#(_byF5+WpL=E&jVAP7fL874tm2m67zmNvWKBy-Wkt>}`Rrv6VBMTNOIR%51 z(pdh>r9DlTJM<(P1=bhIUatoMKlH4a+Qc|<2mw--1kYOc$~qHK-yJZfiOlhf)FR%B zAF~H{I!BM-Ov_lvsd0s5+uZP0E)STmCvF9sFTg6qBoYZt(!c1CH!v3RKDF$P+b2^0 zHLR4pWM3*R8D0!>Lf8x8Ztu?`=gUo)7KH5SP0DcIU_kX&?Zq%bXc6vrwY zZ?7^(Oe$Zy_D zbFNT8_0r0^8n2_EqJ~PlXZ~$?-|b^}!tAj?P@gQF9ah2ligRA2dJw6}Y>LHsXCwE-dqgM_>W0mqZND$t)c>bjRIl|Cg+7;FyYrS%ZVVq{l2&yU zG|)5)x0oQAXUoTCtUBzwKB2U}qNoCzXZGcUW(+N4%h4O=S`|V1bQ;-`9|z1G1N(h9 z()S}3+syI#uvWC)jj`;0EhIiLENkdPU<~_C&#f$`i&=113_2j&EG3|@`K}wL0;0!-A}K&KTx!b zd&;|x$f}$eW8jW2X8D|aaSA{21t5nQ0B|{5v8VekLKG2FYH|6Egw+Z!OvKD)Qkbro1aS?VW73B%|ZGL z;OwaBN%~w-O(uz~t)1Zq0Xcdv(L*ZK1^sZu*n@!NPN6o!wll%F3Eefk)fB_9h7~Ic zyX-|CS*t-HVqQ7qg%~mqh0WSLf%bafx|G4veM#T3hdgI{oR;($lGky^XUF(Bb66#( z(WfiqyHC`e_f#U4J~H;@wSw(mPaG#B7Z0(PzCtB)$26FtWhd&q?87>wiFx-c`toRn zov0e7>Ox0G<&eCRyBaI#`2RqhPya`VGxY7rg9}^NUr;IA zrc$<5t8DAvnW(z^_n(aHliX{uUc3NpCl7E50xKEStz-xfkGzL*39^(MVh-3GU51-au;nZeATlkSBa3Yg9~-cxiO>=>UYfEX8MJsAq!=sA z@{&$QasFRpCnZiPmmPj&a&KqAfz{4fojrB@QhZ*>^($Az6vvaFPr0->8*5ZudF=c7 zL`IQ?RqOeUVLH;|M{X+}|J-r-azM)E!?%^b8n0P1v38v)F2||tZ-JaW?RJWA;>sWY E0s8?7x&QzG diff --git a/icons/pointonx.png b/icons/pointonx.png deleted file mode 100644 index ad15c56e8c8750b87ebf5f20a4f6e2c1001cce7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26848 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%0AHQLBV5naJDbdYY@W9ZR6aYWH<0U1@g`+yudhJ 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 Ty?Nzt;axm$cdq!%&By--A5A^C diff --git a/icons/ref.png b/icons/ref.png deleted file mode 100644 index afd64c3fa0c39c7115fdfe9257775e185161a4f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25761 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 Za}DLd8BCI#@E=*8JL7zsaPr!p{|D3G4sZYf diff --git a/icons/same-orientation.png b/icons/same-orientation.png deleted file mode 100644 index c612ee5f4a664e3070b1338b7fada83f5b68d76e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26407 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 diff --git a/icons/sketch-in-3d.png b/icons/sketch-in-3d.png deleted file mode 100644 index 204fd8c96718ef6d66513deb07b2f117f2fedcdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26669 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 B4rl-X diff --git a/icons/sketch-in-plane.png b/icons/sketch-in-plane.png deleted file mode 100644 index 86811e8bc993d1f5fec0d220ef8428613ea2c0d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26671 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 Al>h($ diff --git a/icons/step-rotate.png b/icons/step-rotate.png deleted file mode 100644 index 6c38a16765bee3e88ad5bbaf18af0531a4b7df7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27352 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 KrIHJ`pZ*`svotsW diff --git a/icons/step-translate.png b/icons/step-translate.png deleted file mode 100644 index faaeff0b977198b20138a6aa1e4eece11d7bc01d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26253 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 F{|DDI3FZI* diff --git a/icons/symmetric.png b/icons/symmetric.png deleted file mode 100644 index 6eb9cad9b31cfc18f8a2a9987e7c77cfda2d6e46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26180 zcmbq)V{|4>5N>SS*2cDNZL%>owr$(l*xGPo+qP}ndXv02-^Gu6|K2`R(=$_bs;j%t zsjBX$CsJ8a3K0$u4g>@QQAS!^^}ELTk6@s_@4M~-uiq7fv#5+3%y;pDF^TwohjozF zas~l`NBfUJ=ePVmzCU8QNNBo-QrrqSy!=-Y!wEp;+iYoDYf;Jf?eVYgXNYh5 z|0+L<*$O~FaGh+e)qZ{tg`SrC#&FWG$^L4RDc^KXvS&JW4x9NjzXUdZixgvFS$Yyq zP*NH;v0W{XBSky~-O4~PP*8no%Mje6s*vIs?^hvIN1cnW-jlkks~Er6QoEL0_|?$- zZCDQ>Q|j?zrqcOB=F8t5x3u<`RLmM=zU%qMuI;^u@xAT?M(e*^E{fQ*o4f&ht<{Ra zPLUq}1{=g(jSil6Z7Ze5NY15gYX;jYJxh4ob3!6wkx!(>JTYJ7bEc+@=E|pnC*3UX zf$1>_`wjlT9b7p>^O-AMzfK(L8oQR~=8Sm4!74`dC)P@nvl-#$oV*^(m&}LgXxB$R zpa6RB`~-7tTUfs5NafsSDbpa+IbQ!ztqJNn;t7^L2Cqk=2B@CrCrHktBg(|CL;+x~ zY_`=?c67CMj$qMsuEO#f9opzG7ZGbvH1+=E(VtAxV73B2sg|!{@_PKyz zQdeojIg4U?l6a1j;`Xfq!5jDB8%r$%O)C~jaIuT}5?QDh2u9^E5O6DIqGH!Z!={|$ z6z~`K&_C!Kkx4!^0ad<>-A+Eu6e(}uaZHWT;x-6&WML^8pom+)HX?Ej$uV<4#fHOs zTMxw9NKLBR#}evdwCpI7VoN!Vg%?W^!rw9p@^TNMCxiGCr8akZDX=_#3F~(B4{}fy zIJ&p>@+Ny^nxJ!=oE<2tk;%mJho~+DltvK}y6;d#W%i?JQ=7n@MA90vFIR$JJYi~4 zdjY%N-)wt}qP} zd1FmAe=v`KtoFk{;@N89W58Ae7sNO&fTLl7P*nPJgctd_+0c*Il~!vk^EXFN38 z+WZ!(`WEW+p0$7987w?>(+rOmCXD=pG6~E%dmqHnDEBA9W2<1!ozp0&3zToQMJK_t zGwtSqp?oH^n$hYa%^x*Zt_tUC<2$2}CKZJ+R~U=T2Ocph`MNO}jL3n-V&h+E2esuH zJLexXRYt5NIAk(#RCMuYPyws=fx_LNvVO(VK12vJKdR}u*Cvg$&2j)bQ6>v9(WnSS z@|y{ciGhzWn5=&$Px+reZ!`e{rgqxqtL!+O7^2dvr!7OnW~Hk87LRk zoA!a4Ohrhe^!0F$vq3bol{3!y58T33Cl^y5X7ZsBeHMqDFa|E-5?G(mjPJ11)@{8T z-6vfk+A+35B_8RHbK*>_hMBX!Mtfl1F&r6w=nQt?#L!3LWle_W|5B@rp%uydlV}AV zVJyApNh{Apna=Dru-OX!b6Cm2=O0O|$?V^!$+F*S83|Sl- z@!GeBG)Q27hcZ9FQBM%VHsuh*JIzkTjSVsXDt)d3i5wcuG8_dMw zsPn-_CIusTCMP})-g`(8R~Tht(Ib2TRx3AU4B?y=dZHy0?PUdBT8YSzl@F{&!&oeW zZCbf~!{+V}K&H+rcfNWOO&A!VBV}e4B_a5Qzqv&HYM>e6^OttK#DbHv9k+%n96yOK z)R=IZNF{2h3|fakABj7UyJ|8l-9ZZ4kJBR%>CEoeTj?5juP)v1Y;l_TM67;MhbPFX zTX5WbbAtSp!^9JQ0|{D;Oota537_O7GNgaF?+qn3(Ie^Z;xICz8Uiwp4V?(Cs2%#{YJ(EP_!~QBzhGF zf+J#_K~&YxBj(K;Yz&TL;k&5f<7NgyGk&IQ{B#o!v2)44=B0#bR$E5XPE}8C$Q2kQ z=ZywqsY6f7Pfs*NB_cwAK#NPqLV$d_EvHkBR+)%M&}_RjQmg7WH-a`(6;-*HFG0(} zCM}f3tUQ4sAf=Ph$jmlU`cf&+U6s~t{w~f}dH+{= zDH^$D1#ex3>_~a!+|WKZVS!9O%#Td%exu-r$S-5wz;|m|mEtQ2{cslb`qFyQeD?kN zJIENBQ|RkDE#<=F=_m5~TI(N}#VW{DYkHKG6=_tDry)Km`X`K9iq|aaLAlGclfc8p zeDkl%vmm3x_yCffhCfwOkab3)HKCEsFP2(NzY1V^ zP{4DI{D3C-6yA;q6O-Lt34ZgAq3^Qmac;P*zTFD*{94R=D5@Uc?n}7`Exckch&|Xa zMRUtYbjvW@@>T7=t`++Zp;d@EX)6M^e2-{$98U{{Y=RLhszz_)rMW*0$R);et$KnJ zBPpz2b`b{o?G}QOk@j8<1MF>F!D~!p6v8ZwfcziSeUI4#>>=Cod$8OJZy*(6O5KlB z+eaIJ)lRa_xJz+-4yM9y%4C(JGpy3OHuxc@e64VM*a@$?7Kuc154e6srdv+Is+eU8Ap=!iT9#xO~u+|321gyUd zj;Ji=4KHF}4Ea378cn>&!^3n5El7qi|Et#SZ)~^v0X1v$0O~afYNY~oH$Fqx{)Kca zXJFsoG$fp1CrC5HO#}EyDV}Lm5<&XPb)-=<&;FaMg3vmA3{IV1CfAiNk#3>xkN496 zA@kh;Iumq=?B|)O#U7UUr=`3vi-naxp#{Nx4E&iMSXvEedbSA{Ik@wE+W&enSBvt?kklW(J&voPJEgZlbtW;+LBW;B2OaIp#-l_R9*1unsjx>u;?L^DFm~cz9#$uFk9>1qz z#R3Jm!z!(AcmAmuZsIjLQ65n%m*#H_jOF)QZEFK+5(p#yIb&6%(maA~{(0C(H4cW>d(fj$exgK~h@uH?wo!aeR)oG(Y4x z{EP9`Tn6RaSd5gCq;RNPqi;3Ar^c5tF5a}9t-&^M+ z*d8_#aw2X%%Iq-DO7OkYo59Qmc#p2!Ngp?FWPk4QxLzgvN}oMw+TT0))YoJ7oj8%twH6_@jOb%+ODGa<4KSm4=D$bLC+(@m$yK)FXS135}!`VXbhDzA-WKxk_93xNr;VDL3vs_#^b{rN@94W(aDyqxaG$mi!W%lrNN>a`Z z#|R6A=dGuO>wg;b{!=xEpvQpwu8;FhJw1!GsnZENHeD*=c79ZKc$w57H_gby7uqnD z)NsR5k;>g+a+*{i7siOo8(I*XRIoBS3pX~Am?V zTjLjRw%&Co9d7%7&3_9bPs|c>!vh#%>wqtyxMDS5llOpLt>4XxCy^|x-eL`V=1{6D z#M#tI9DG$A2huhq281IZ9FT00)~m#DdY0ii4tDF;7H^wnO7$RJ<6Wn@v!z=B)rU(0 zb!7blJ6xsKOVZaQdMmRdSv-yZzG5vloU3i?nGmFklv=xgPlJ#JJLNfEYqD2)-ql&W zZWc=~!ky{iM*7JmmL*1uP3R40VMSh7y<*^K&j;u*v-=_M2HE^IIaM&UXfQdoPcPsS z(Du%h281*lWZEwTaOw>!P}-evMUkIl6gqd6g|^k>RzX zw=P`Wt7BdV>fQ^S7FgXH54<54k$OI;?-3fAcFPhLR5VhSdGpPV;EF>Vz%=N=df$bz zD%4lPTU-F#T-Ak7l+A4{+wt>iZfYUm&HS+#)hK&!DiS7p{ZmUFAx3&7 zGh>V*?WL{xj=WA$`dCRy7v+V2A^J0Af-0b0| zxd;wtt-e_I7@UTG&G`3|?`P|M5E^-TvJc3k=;!^6HRrwI{~1#jbifKYL@rxen02dED^>7vF^#-IKiz|v$nC^dAB%m(v2&+Y+sGNnH&4vHs*UE6Pje1o{>;>g`H?mU zGnBw>cOcvye0C`CkW5v8Gl}h1D+p~UR>LwfI-^7rW5?5;v~55)a>o=&ai#4_XM21y7@2KDlFvA z1mOKac+<9fmgiB^4!X~l_Dx3G#j)Uu7XFAUEyFs5q<8s~v^}u8?gj%WdcW(O5PZ-m zu+uvE;mBgo!iZBK<()ocZWrwTyE((}8YvqP>2tSe9!D~Net8rBr+ngO=0`NZE`G z8a8xOv%*PvX;kGu_T5`mgXF?!JJMMIpAYi9m#CHnH&3imSoilBo>Q>P82!1R9UPU| z$edE;^~MJkvlF;~iY7W5J@{ABXGEN zup9W;PgG6NO)a%m$l>pbwa>qN!ufdQ-+THWkPyemR>)GRo&)$XzV1|oHl;fQ{Bjqn zwU_HJZuV)%eMEr9_O(EnWWDRYnLj+bIe=U<7LCX0E!W|$F@L#MDR#c^a0AGwF@Of_ zq#S>W&s}J=La`axXsl{*H^0$CYmB_K36Aq2&&kzpRS$JJn^~NaIwsUY;iGsHOwd zf}5|qR=uCM9`%Yx8^&&ieTW~3f-<9r4Cqf2)pc#yk;7l)ZNQMj z*9uJbI*j)AZ_t+Huo$&oNwrq_oC3nSM4Vk3Y?=VkCGbF(E!Qr)-JjOG4NvH&sP(SV ztL6m?pQ~E8vy6ARwTbR{8?2Yv@~Q1BRzysx==iecb&bcaHA_6Y^oTXCE{&Sg1p()E zD^AY^^Pvmbj7_{ znsPapb)9*Ac-4cuN#7wwereI6gogA^v6nA%ur6fgD%QSXI4G2xFFhrahlzwa#YxAj z#!WuxKYOza3GM6EUlY88H36531n}sO0*yNqc|nObAmlekI1&VK_iucjH{Ski5z61- z@%>nISFAg&Exd85<@S^7I^$!<=t7MZv1e~b^UpR@9RHwBY23Z)I#YGuCQvbu8YqJ8 zK#S%g#OpdB2bjV3U`D31sNU_CAb$cX>Q;6U*x79Ot9!@0F4H}AN+u)W}eIhyG)|l zv`|GvA70AvOLE{0X8Hg20;(Lv2i62|Ns9-Jxvg#FuhwjMpCzg7H%oqty@Twvz)Z3pCcX=cvSHz(jf z=f+~sb2<%dR@QD$9UIpomL4$Dc`wN}=?510Lbup-Suen_?|^oA-20x-e_ysPN1+au z_}&q+6W%oKoDnzchNr!W?#=P_m$)S6Cx05vmQIq53t8Sc_i{_NC0=fVKI#A6u zi%~Y`v|zqi$hgCuynMX;wGx4+ckR(okA3IX0>_EZL(fPxi{N&~gl(Go*Y)K0ad7&t zYs2}KgLm!yQT|j+bTKHe$253ixq`5V#sMql(4Cy;`eQ7)%;ZqearJyPziRx3bbb`fC}TCL^>ZJu{* zMbP&3&CJcr*(xFs{+8g(6+wkaDqbPD<61u=gC5l{pN0yi$dt_ecE!IE`Lp)t3sD3f zYHtxQChF4iN(UBcw`zNo6G3xC(8iFd*_5cKmAVul8l&*WCaJ2rd41 z57geL$G{uY3kp23q7GS>lg6X%ks`v`BHI zL}?1F$QXRJJT^YQh6au0sHlh2Yp)MIVg7C=qCZt5ZnYI4t4{Gc7f(bEeKLEHtL}R- z$N@88TZUTrkeUJD76DlUh&dJfw^}WuZrx#OKB{(@27bf%0V@tXhCv;8qY69TdnMr6 z7odcS1=F=YTFt&`6K=v#C^}3x`FxeiZ=Avgm>MV70TsEXt9?9~pcT&$*Pn|782ji$?ui=8IVtF53q5VvA+ zQtr@j9PzNydjU@tr-WZwtu7;mFT!h~l^uD1!>M z@pO5I1{>ZMcz16F8@GUaI|261^ttL5Ty9@M(auv^THET~C!pw&+^r6X#MhNxHg^pC zrAiLt!MttL+?E8Xdfpkd<^+j!K|B}N!aP@9!1j#cr}|v?YnUj?bFp)akLCixj}6Y? zwj#pt+}!rvLPNr*LNC?3%VY^sgs-al*mLl;0q^g>CDOt;aiu+e@k$O3aXzXD$}Rl# zd|`a1!rt({3!dQj6)`#Fg^|Nl{74aEy+y|k1|rz8W!LULUvkl-DG?}0D3YV#OG_FG z{l#K{%dslN=F#i>vOtKBKA`wnUK1E6yb0Le zpp2pP;V~Qzo%Q7`TzP|L6w17}!c@<=z(9e`2Xe`Yih(}?$9m?^Q7_@zTzE6Y8$mf#^l>~q3TYQMKo70yAHOk5B(Rnlx*~*?T z@r)ztH83x2;>>DAc@F$uq(n-+Q`hY!?Q2KGVNc^=HkVA~w z?f2D|_0qc6DKnawbs58b_O%O}Nl27}&^p+4P(y_zHsH%j#9jn151$VvXoRi_J+-bW z>2KX+u_?KwA=3g{SH#%63je`i!H^p4d8yu04Pv+r)l_gIiFj?x?`xrodj#pGHg6VR ziR7E>WAb=7L{O+EtkkWnyC(BaO%n3i#~g%nvq-7V5MuPPe!ciQ#9F(YoLJ@QkvH=W zxpa4UYLdCUqu(=Jw7g&(C^DD~;4g{m&Q|cAuX`w$mi>8)I!V^)GX?-Q=rMi$^>V ztaR^I9z*@1IW|>qUh|kI78-3ak5?2$V`&Uwz)N#=#c9|*E8$E28#L}cAs{qv> zOb04R4}S?9V7*g%tKR1knI(UgUDKYCW9VtM5*lNfR^MrG;y*hB{@dG3`f--2XI;Zz zU@!vY!;6gaL_h{Xmm&f^hW(#@)Y#l+#kQnqXMdKAFTr68vcjg;9AhsFeg z&#SvCK?|ynuh=&!9De=<6omD4B)Abek zGd6?Nlf|K)BqPdV6uTZzMCe3zhPh5}bvkMWCnz3@cBGX_Yx;pO3X$qyw3WOJ>y%YW zZ^Y4?m^nyIbAqkORI7OKK$0*s8y8n<-HTAf*ZVWD_-^|)KiMF;n`sn0_$e$J!#LrA z1)HC`4G5A}E7S679PFMP13DCLErKn%SsZ1L2cA(+|EDh>=L)jD7oTuUk?Nqp`D7Pr z;MfiAP`(K7?&>MpA;Ioy*1ZOQ)!iPSRqdnrwB>eyx<$C!@{HM+B*C@cJ;oXkM(T@} z9SFEhKe6qO%HH!V`U>cGe7-gvb3kdhL5I-It%ng%ye=hWu3@FQzk14B*7d6}*~6;) z=rEO`_bLs+57612qa_=A-4O3;7g5|sZb`Re?7!G%i%5^|^sZZ6NS=Mw^|?0~T6YL? zo8h#3+uoB6IEP&CGg5&1!k`c~cmdwW&9$!Lo3A)iSk7%V;Fh6Td^EO{b2@S!EQ=lO z2aqsinn=d)dkEdESS$=)WsR#>TURSrZ@ey5f}8*yLfL0&M}*1m=QE~!`rcv5~2_=|2oH+}{_cdiApkCbkz24)u~mf{8; zbo{Q>+uH{Y#szH{FA0bM(_zVrWn{TMG8d0MTznMoTtB?Kk+xjKomMu`zeMIG7JS59YPht2$2(hGJ1faNMd=N~4css0rZ)~}8wuUc|oB))V=PKGw|ROZWgn*-nM zhXfDti`Sr@hiv+{qc1d>ec8=5;Rfnw)-pSHr-=1<)ukK#Xu`ATlp8Kd9RVE)#3v8Q z9`~Dg)@NX&7{gZu*<}B~b@y9XUb5WcWrEqzco`4QG6*ijJ{jOJR? z4bJk^CX3sP3I`>MOFAm@USf#sOHtMV4seig1dE(wGL^0Gr+_LV9*XIK&Sc-l%bU`w zmFjK{mbn?xy~0kUwu1x@P$}k1M7*<9RuxCQQq`bj{t3+GWA_Fy0fgKGX$mLtUnlHqccN<~+G<>_x!D zeyS6Z8_e>0Z_uWYM%j5bnB&aaCGi4<+Y!OF&AT1m-LFrp9Ei)_)=HI!o}Rys^9#2G z){z#&eY(EXFF;&uPB*yx4m17;<0mk~(xp{Ghtyl3L{sh$=u!uU5A0SP3`|C$B9m^) zyBa1yxHvGSzpd`H4YV-F1cH*LKD_gy+DJ3N#+Zv#Kfy-q==)<~YrnAMJfp}dbrl91 z;GN|0UnSA@r%XY_Vo3S=@Y&%~n_~Hf-qMbpk<+lpT3|(q&hV&N_@BfTWWQD`jt(^+ zVk=8Ddli^-Ue1uBN1YZd?d0bpYu4 zJwfCWev#QD-@jvU_Qr#%^*Ou`4rQt@S*Jc$_~Mea9ri|Sffx~ljNE06#UdR;a(z`0 zk_JlCveBp&y82j26?-ns4OMKczetolnr5(Y-!X``KWi;%ndAqX>5R(1#R>kY76{Fu z4rWIo%RBbX(S2O-7&&Y!WJO1kIC%zDI_Wb8RJwWmD)2eMSD5b2TQ7(cEpw(EEhSpa ze*Jbr7((h>$Q0&F-ppJaaWNT7?K3*Hr2^p=hL19?cxYUeIgk9!!NDDNVRix47PJ_3 z%ss^HIJ+Zvy#`GO(L>6)N^{UDXE*CCDG~&j;D`gl?^jO;k^WI>4PVfcEu_Y)3F9$Or?lh&96$3S(3_M;3qTAB=TU`&PltA(cZ_i6^`K2($nL=&z{Z*z4}GX zBHgKanEwdA)od5Aw7Q(q%$XgoJP{Z9uO8^OG*FIV`>G1NDsN1P(4eD2O@j%?Ll@y^ zFoP-(HxqgZ+Jz%9rN;9fdZ%$U*>Jk*`8~(ko=~&J9lJ7(el#l)o+;gE_AopO$v%Eh zjx|(qF`L85`)>tteoNAwANm+>b%u znG-2%qNO2n^?YYV%C%~RCCk}!0Ke4OVveaL0rrk}4bc4G(%8Fqt$NH%j)6oCvUsHH zbAJhiARUE{B2MV69coJZ0&YdQKDYD9iopaEZ|?(f`q!i?e22ah7GU%^(u{8P-3is& z7)s@gnKZL$+TIri2u8sQeCM(Ca6MqYuS`#o`BthkcFCOm`V#FGoP+i`I9(}d^9IpF zGOtB^7d{BF3B^x2u)}T7;LG(ZtB>b&n-bXRBji>#(*4^8xt4bCixFh?bIjp%nf@oF z5P-v~a|ZV+aq1>cxdYYZ)NiA(n;mQmdVoSwVcvFegsokdCj>nwP#4AQSExxD=vvPX zFep+9wHcCD}j={1WRNk(+odt)ZCrA3*>ydFjF%6UKzl_8V5#MH*jbk5~`uPGt;v>VpKvlQgU=vaU9 z_IH=qM$H^<^NvTW&CusT4MQ)ltv0&&KuPxq(&Xp0^YH?to7T)i8`P>NE-UFi+C~lH z?8~GCrY6v@&OzDCpdP=T-*Gz|&*TRN3Sw;_(CH8EUJ^2y=j4P-r)ApUoL2Xsq`Pbz ziyy>;!-5$iqVe11M#h?2KvD{8$}rzT$j~3jT3$F03m#4=~{yOExJ4wTR(FFO4BR&0&3lmz@e7^rq~2*SW1NHqv{s6^`-(neIY0_RsJ1m%}6o zcl3eBIrBDMx6ZKn?s;a{TBCk6#_Fv3?@9W!!=Q?*GEU?qTpN7ZU9i;r_x9DY9j{>DiU92rDX*u zgtQd(%5XA+nt%wNxAbvvxzNze)L>t@t)U5x2_pCwVQ~jOaCD=l7{bUDW5Iu- zrqRHby!44n5QGCm49pwE-s~JpO_WS^6j0BqdFh$qh^nq>Au~&UHqj<0o;Ea3Ye9xf z>^{d3>n6WRH9)EcB)(--`;ttj!Q4Yax@|YlJkJ0*?%vET>RAj5p=IQ8cZ7hVT)IrL z<@L*1(v&|uC!X$taM_(BJ|*zo6e1toh&~A(34z{2;!J-bOSLKiw@565uih5-LX_Pn zv&Cv<9so;yl=sFc z4u0LqGEbakgf@1*yV!FP`)EHcM{O%+ch4-1_jemdFY4U@-oQVM%DQ$0#-!FG@!;aK zhRs_lVTBajFGdb`V9Uhjh{#NNZ^d-lDv4(hvI4ch#Fj(*z){Cec8u@!nEa79$~A%@ z$p)Zsdy6G$;IcQrO`UbFRsrFb?=+S z*3%?zuCl;FpD+}}Z=KQ^x1y$Wx7vq@G&PJA!uhL8Whq=-2xWKJjV~l0v44 zA`~y8_W8>N&_?fhF8Q#gZOvFGW7RhS2KnNg_k_g3D+D8JcDU};>^k2XuWw>@6euYU`VM)txbk2?;$g_7PDKIU#tVk9VK8S z8Xf^OsUWC`++Lqrb1577Rjt1!oMFI(I(5rL%{#>Hn$UhPZ@BbeC!C5W2&Qd+J8t@F z^55|{A3rLrdkbDV^tDI7a1bSK| z4L#Tzo*$s`2HV#dz#XI`%mg5J6LY=udhJ|@%t{dI$I`#GP5<*Cg&f%d^%k52ka9VZ zH@_k;cypo7KN54@PQ37udB5j{Q!({Lfo3tM4g**HLyj(RF`HMHFD;s4GQ z5mU?z3$$oyg7+P`OdVTtpe+ybU0jpY-cZ2bd|KenwpZAY+5?(eB*6})>%Fo_EIoLG&s>jZb~Nia&^QM^@D`85OqasKXI9SDp-8o#=Ict>JPh-po9Vh zTajAG3mi=j6%w~B46~n*l3;O+V zN=xSsyy!ycT;hV!a64d80Wq;bBJS0#H{Xsu!nr3m1p12KdvMYYd_E7*io{MF=oJF; z!iEE#7QR3H?H>L{&^X+buzSqD*4zMqM9Y!32~oLU>vNEo@IF)?OO)FE!vAPOyul$} z{5mQzUKCjx9j}WXHa#P<`nV-uMwG3P;>7Jae_{ePOTRoVoC-;v(sy&KdEaG2qRl_r zfiJTo-9ed$W{OlsFehA#QQo~K)oXJ6>)GzO6XfUn#JSG(>+LbEV`z$C3BDfc{eJTs z3TBrEB&^7##iKxWc26yf{X`cxg1Lyv3P44-AW+2^_d1;$qTqeRTdOCI#C|8shsbp& zZ7=)?e zsWneue?s6@#a^E#k{p6F8qCMfAoY8}cE^u@gy=_FK-h4aBWYz3pz=c^8sZosfX;=exL9R2I>YiL+ch_|*s5Ce`4I}0&x#%4M z;%avGrg6@i7koBNIyo>I&v%`2D9;W?p%Uyf7fmdul_9Awc^)w%XvjAJh?nW3Gt)}R z8K!IsaV>boes%HbuX?9qul^_l2pO#V$aAGEz1i%;*g`cm)rmrNIwT^L?0wg1QckLep24_PwxQ@OIal|& zq48q3JV@5@Z%D8Ag)adrY!-fsC!PCdNe0Z}y#kWf??|ZcMd2-SL7Ip3ckE_e4U%p- zCvCpV)RbD@X8;!~eD?x|(;bHmE@3GKnh&%nkZX~Lq~VO1*tOy~-ECT3Nz{{;uhm3A zFixqpTC9=rg}I%9wGz7JLy zFR_*`K5b&Z+wm>54Y{}rc+9(Ch2NBho1qZ)>>?dLm2ngIxY_xCM?BrKh=MFnfLJCstWx2#4g;t6z#{}o5=WZ0^q@_)bT7Lq|fffSG~g zLYIgoPYN#NfqY_cN5$`xo9UNBA2DcF$Fb4MM=CmWl zYk6bLw{|MFJ|586uJOiBjnKAq{%COzgFJU9%QbJ{{Gx)B5-cAw$3|K>rm#^1@aL@Q z%5Mmj3-^Rh_ulWUCvYUG3Crj5nCRRx)IkCEHPO63;yFiJHyp6B^BDDe9HTC9`8amM zs)YxfeQY9nT|F+9Nx#kurA0C{*dAttuw_ms;C-Vbx%ts@AHp^l=0C9ip>MUP;HEyU z&wfIOR0|`jLS(q4TjnX4G_^b->w3wJKQPkhj*o6T5o)(pkLDs5?#DVhd|U?mC0*X< zC>WY>A~MywEA>nRO$-^O$F4mkW;z>A5xt&kO|EM;2&hU<2l&S$O}3yr&`g^5c!1vk zm;Q2Sv495j-9y+4BpDlFTcbG4sR6M-B0cF#Ihsu)eL@YO=VB<{QRsB{Bm`WdN9d-* zUvxH1Uq3wNV|u_CEM1Qz_M-RwL6)}UYo$QOkxtjhYABWn1)jMz|J0XLsIw8uCB1Qo zQ)+CsV={<$5CFIk`qMH6>R$okyo5&?^lNbNuC0P8;Xu5(jKE9)G8VDbFP=TzU=aMg z2T|c#K&ToqdiU8CS!6~iD}o5&D+Bs)RzA1PQ1y14Q3ns<`yZvJor^J1Xl!0pd>rj3 z{kR5^A2H&^G&7ymsy|0xWaoOt0jB5?-RM`y>kDKCi$6} zjQodGlxQsE+ega#YRaaCl0_wz;nocxHasOZY3c0ZbAyfbR@H(gBg6Ek)g1Vr3i3I1 zqej7c{1taQLtYIH;~G`Dt7WR6F-k{(XPg(n?5i^fPE%i{m=X%3(t;<_3)K`f4Jev| zpO@?pAd8LMR+l(OrMGyogt*TuAb<7xm=P}FlJ+wGmx{*Z!J2U$HQmQ$i_1(_nA>-? zL}V5g)4iw5JR2-X{`=YWJJ1-xa5#ftQQ)X0j=LGLJnzME*FJV8)qxd7k$VSX8NVN} zf8$hi`P1=p|NbH-WEfDN-LguU&c1iqx9#M;ea(!Rf^9Qwu@X?=g@jvDHo#+iAt*Q< z<*q57DQuzM?>+B|6e+?m61NcWP#mI)|K^vtCn@j0#qG`oI{urSEj1A%VYdJ55GOwU zNE|wivpC{SsOmg+c6_5>)8Gx)atpoSlJ+%=vY1phtf_3!C|5En>l>$Ul=Y~?^lVyC z*Ijt_-VyX1uzK>-McZF_@V8m!dvB^n-PRKl?eWtGzgG-0KWf|Wo7wZ@V0sk|2&hA` z4Ty8ipbYNYu)uh+)<+h#JOX{LxOtNuZ=ySWJ#mK@mRumsR)CJ!!=m*xH{ho4_vr$O znBZLwySCLB$UinlU^|hNI&uwMTldRgrN$g^mE;F_x;JSbO$H{DXppt4V#i!4oh@ub z=6N122KqLC#b3DOpICeVCW!@0!vsk2W66wNxYt(hn6&G%TGkg^{heIb6N;oSJ=cN_%#y?@ox!QzmSQrWy%`dMC``)?Lstrz$k&81fd_> zNd=wE0|A;}O3hb~=<)K95p>g4Sh%3Gks$~^3BGEp#Wk6@qcKS@~t>@~K!wKo=N z#;N8-R)11QqxH!up_`z)nrxU7g;i1^|1j|DnwlQR`yigfp+|JVCVqxl{}~&Nsu{f- zg+AYuhb!ASjs!)dcypy2c9gygZExF%u&vNr``*h~L~wGkf$(_+RJ}w5u%PL)P^ci* zn~lqQ3xC!FgBpJksmdy0yT4~2NVRsfmv}M=ff2I&R6BWB@Yf5cvM`g@afT0Hydmi+ z1?4O=WgVw^JLKNtbfPHy8KnDCZ>9RJQ(xg>LWO%XXIGA1LV#f9yNe5^)_y6$*D=-{ zPhwq5tkW76eFF)W*%_N>5u?>FQ4u?d8N_(WkWI~s@B!vH!N7IFWNDVxHR?KP3b_Xz zzheZOnjv(K!^;Xgh`oMaS}QDwUlS{LH-XSROv35Gf~xY26hgLwul^E>McB7##8>we z;?&8=e({XRA$OGAb)J&M#)dHuzTTK(Fnn~v%xsb_HRY4hDA49FL4VSq>OuM7I+iGA zm3};5d@4mx;PaZ@Y>wV9K(`}?_$-mZbd!;Jip(ZrhufrViAPxlT^3?=Zv?+tZ%p+3BJ1RibUf!>c#z|-qHg+vcHkooz3a)_n+IE+om;fr)E z;Rn5W4vcm7o{({QIZ~o||CDXAtCLYk9po_p6~tpt@r)& zt+m%WXRUq8-uvG7wfA-JYu#SG)eo-T^-G6)Y;NYjo<}t)SszW>c4}-lJmpdIl}F)` z(kF`R!O6cqI#GAG)W}`_4GUwWBQjN>&IVYh?Vjh?sK0Nyra5mHdEQl2*Yb79++f9* zwshSxK1g#6W>nG|`ZB0v*UqarGusu4!0rHbA=9a28%);tg{T=!i$OEKwe+1i(gY-s zY-VHFLCB|jdyUB5S;IDIh+(Q{r)y4Z+~LY;{-M;zMc5S=M17xYBk7>h)r#%x(`gs- zod*tnW9XNi{~QQ?>hD9UF9k(X6P>U-Tj#zc!BT&Zgr4~J^zI8w;M46L=+D)|19dT{ zXSBqjcX6?`_ex1WGr|zYmKdCYi`zFtM}loNDSO zCgRBu?9AsCod`b_9qxp1QLoP{?V~)?dOF+v0)+5e%yj<1?UgGpAhBz$WKZwDkM@h# z4r}$kuk;wRE&t{GcEy0E6z|#^;o|kgG&N`MoWA7kk;;GitRJrXZo48!*!k!4;zwj1 z__e1l);R>Dm4WXayNQz{^t^R^7P}enVtY<${yREqPMwoqi42y(auO%&-4^p zVN15qD3`hcWv`jCfOmCOKZFYm$f$J35iW7-k=5DFnR${@T*TLG3}>yg`U-l& z@7Dc5qpl4%evTgd@vj4CA z#yB&NN9Rg^R8KK8brY;0{&5R}n}{68O<(agT^0(M=7bDqnqvC1BM9x8(?1u45crjY zIX30bZPxZUr`7-i?GVn=eVg5i#VeQAU?-j9!_>gq7T2EFL{^~JF2!pKe))`8Kk~`r z3ET6Bo|#G4iX61YZvFD`Ajw%v@wWYKlM^@Bnx&cM3(FwZ*WckhpVs z6_l`y8jX`-agyodD(o#_Lg#9JOzNOkmMP)PlFhLvvsn83)|cCE=FVNZR2YD4JAug~ z!%Svnm27|jTl&{V z<%g{f+$(TIxXp2;qqqE=AcUKlI2GHbtlx!=AvVBzXx0hIO~XlMv>?LH{||1xGC$fjAm8a<)rJW|E}cf!Z$^mu7MxO(z|B0do$U{CB{m)F zv`W}${2eIa`>(o)py?meD2p^9#`h|>XlAqT{wBY??vmie#(3*{cNvB71(pUqgceg* z84<7kRvxyc0KmfnSJ7e|1O*7<=k)reYs=GurntoVPX==5%$wAiM&|(RQSOt*I>o?3 zzYB_#RoMKyJDLVgc4|Dhj$Gr;t-+Z?bD{m&sPg+;LQAbi|>I-;`EA+A;zW2^TzDRap~b^%?b#-&p3m5 zX5e%09mNg~p9tYPNa)JFCM5@_jaW|pE{HyyOw1~0K0=wM`c^%Q=`FIt=E>g=$?JG7 zUc3ASOljvF)Iu0+T5V6dRo@q7DD3nno)sm5obpc+^UTjYX1&@!)5P3VkU5U(*!^CaJjd=P)r4VWKMzzs#dN&fbkR5DjP6~;MO*j`;2e4JFk z)o}BH*GRmDm3F7;LsonJ@^zwOmL>633l8q|K?$&(;g$!{2HmZT`_onuN!lXyEsVHV zj}vk>c`=LnAG9kp1gqac@uqWp+F*Ca`~qg(L<8K2p99COcIV1{sra^XaHR`qUA($4 zy<)&CZ&MW{Du^2lfL-cu_liAoGNXsOn%q@K!i7IpkK^1&G}xX(P5N(EV2^Fbm=sm8 ziCmb9%GB|GzrS$OmWOPEZMjhB1Sv|Hg^nDeT7UprkiWi1EiGnF@gC|a| zBsp05f}1Gq@75bmkODV#X}bp+X~mYjW~2n$1!tkHIG2DDzyJ8=FRjf_ggJl490+;i zrdwdYH=3#jFw}=rPW=(C?loJYE7;1o=@}cp1GQyJqlgeYbaY&_nk7~?DsiE_ez4|P zwB!;9rD$wV_LcpFZ?CE6e7hBEt36GreXo}J0-IsMTH%j8`|t(YbfZ&z?* zxm~w<`eP`u-5tEzhim%ZV=q^G%-1a_AHpi@@JwLp}U2m{BA{9pA=2*2e55Z z6a=OQRNkYArrnJeir2mQp*49 z+Y@)eKhS#Ci9d1o6PRl{nRiYBg=691AK0ITBwTR)wO+3T%hi*Pu(qOv^Cm!u=)Za* zD21$+41sWiknAyv^^E0PGHCUZl!9=?_0k)Z9*`aju`<1-+v3P z`j7J*pBwXmc$j{%%T4X3XlTfgr#5;uh}C-r&Ht44lg}vx7xDv|gDs!S_3#aK%4`2% zj|@sdtp$%Ng)-wNY-IQ_E*>izH5bD;6kBWAe>H?h($7-o`Mitzklq|lS8P}=gn zxOOMEkXNNM{Y=jCYZs`ggD{wOi#0t%x^ml}+{Kysp3q=qbWlDlBg)6b&Fn?kJndL~ z+jq=8Oja{AaE!h&wvXv8E)vDz7k1ix;#BpB2xyuwNGpQ`Tde9b!fE|Yn?@sIP3wJ2 zD*7qu0G>>{i4uSv>}iOI=;Y39v=Y`{8}VNuwS#KnTulNrPy+cm2dyJH`g^p4mzifvFqMUqw3-L4EF!Vb4(*Xit zwH|7x`3;(tf;jZT>q6$nl-awquKTU4ELFKkxtA5@vbhBEd8@u(;YWrZ(;|i1X9Jc8 zy=1D+w|!sy5Sdk3(gnL>C)Ine6SL!(yuFXS_;!)=z;3O)vEzQj@5>2(cou#+a?~np zj}{QftMM8-H%@hQ zVedWh`r)0mDEoEC8|K!lK0nY>9rd$iXF5Z}RXJW+=p= zPwH1>ajM?QoB5x|^_6{|`n5^wE{!AtGOMVrX>Dl#F~+CqcU>t6GK=7CN8h|GREzzT zgJToxH>y#2)A4#!4w<- z-}+-~eLj&GOewBTa|N8L4uqN&%kI&b0j$C``jr8xv-(n7x)O=Vj*TLirE%WMn zVtq$IlYbj|t>N8*k}r8IM15rO;)|G9%{qbk4!(tZIHaLJIWiHa{gN4_Yzr&9EAJ~H zIUATz7QSrXkcFjS9}o6t&4Gx0$%ofdx2P~GpIDjG@|@Z3(+V=!7+kJpCGmje(LxDf z-pIZXw)gDkS|tNT;qMu_s!>GkJ62V|af1XB+V7Qbw@aO4ALe8v+OBTf%e+|Ymb!R) z`cFAa+k(}Vm6xQi?f@>T+^{KpWFj;I9DX^RPfREatB%O7-l#Pdd6QMV_}9SmZgv#r zdLW859~e{yDkRSt|8he1Qz$$Y7kzrdzr3+fsgy0>A7@fF#Hz_X&UQn*WEWp4IkKXx z(^N}1Te7!ELZJFHw%3f}kQaTVE_@2IECeOl3j0jf^sfQ$M#v)1!QANbUrPK6MA+-6 zz+CLT7xl9Aed3Thc2YzY#QcNmV0VeM^DJ>^RUJEaq*E4EBEfYpSVIQvp=7%G>1tpF zao)V9gXCWruBsFN9f$#2tXTLJ!u(j&yNYZjPX-z_=?@w@O ze7rg}xq5;RHk_0Ie{WIQk3*$3&jSCb;gtx{!RC)kDD}(L&oJ2Q9u$GDQMc1Hq_8PP z#y7Khi;tPu`N0~6jqLR<%?)t^w~0N3cUX1-3dnZt>1}GQdY0Iou^$`trO8H-J&1Jq zX)H<$S`f1od)U1!wJ3al+IQ$r?y>Ty^7?OJZOJJ#$MAF06ITn5+UH$h$P0h6{de-~ zm15H~2D0N{F}H@35Szc)JnSnrKcSImx zQ1=8|T*$p5JytW!Z0Y_vvE3b@1$ObTT~&R*@aXq(u?FV`>egSyfrhdZsZyQrWiLK? z7PWR}HM2f>BkpSCqY_sMiUMwG0Cv(|GcB`QIBTe4@1tZZ2`UIgdnY7<0{K-zbSa$e z%+OZ0o1@Q-d@Vr4U!y!G85LG!g`2e91r+azJyr;$*dY3SKBx9PA(kwpc@vu`77qoF z%fRcQkbh9~pGEF(KCvYDxjX**h?tl16}cBI;x|1o(kyw0_)P8IM_H{oZ(hW5qP};sump?1Xp97wnazIx1CcE=n%x}o z6oC}IV3!RhR9#L@^O?hO$y$|0Y4HV3 zY##=ySc{Q*Kj*4fEjgC=tOu@tZY9s^)0juXKqoTN%Kv@y23SG!<%Y3k#>j|FFHw+z zpCKlP05bRUIX7Lc3}?j&dHrQ%Sf%5rKf(#m4x|WQ&!#j{llha8j76MUU>D319k3Q5PU_vS>Ec zvT0^b+aT}TsgNY*^@Z!{1mfxAF%PV4f`?0)n^U+M}QP{I}tG{2Ce{%TlU*{j*dc0x7 zFIE4I;vV?lqqtPl9k#9)_Fp`xefZ^vSHGp&UbztdDm!+CEq9^Q3$I3vGr|teCwiJix<@zzMm@X+8TfDMMCL_8*i7C~#4O1SiHl;|(MVilgN zecXUVS7M|%N_|R3$z`Jv!Zawx`_480!Ll3VOC$_nU@k-f5+TKS)d|$Ad%fe8nc+w&s;egw`LVK3#o5_YZ5mvje;mGX+FfYWVhg=7eeF`v;4)y}Ty0M4MVN}Ye@q=sBMt2C#V6*md*{qQ)zb;w~@6+^y5^z(@?WW1*FbEnE z@B5-pAK8KEgRVz9FIgbvYUU<=R-ty*&xlCW*$fFG4T7Lq z+YrT`xGyBy8?Kg~WW&rrCwQ#MBLza{i?Ykh8lUspsEQ2H1am9zDRp~&TfGt#Xn##0 zFU&aQbY=0b067$43jk$#+e5rhBk~}6(pdf?z3NBU_rVk?!qToIfNuXNop2KIq%l8+ zz8p8}0H64%uI>i3oY?VqaSrfiWlJor3Bf*A{6CZf2pZLh<%-MA&*hpFP%V;{;Naka zdCBC^_PL$JM~tyAJiRl13o9Z1!H^#AJG?CtW}x3z;iqR?W(( zxhG7J;n|7G13jQiyu5rTH5Ki}L*lZ46C~AcsMloLaGTr?ka3O6NkC54`5!SLZ_y?3 zQ1^WP=A_$`|&9Bbq1-?%PL&9Jv&`F4_XRZ0lP}EByYe-L-yD+Kiz#3S!|A z)*iKb3L{aAmnxe9xk4k0S~#rPp<+Ia z^qBFBS`%dA&lFTLLaF*aJ!ikCkP*AsR-G~m?SatfrL@mWf^$pj!ffF+YTA-sq(KIR zRB-Gi)tk)5O|rH#xMgCnyI*3|Y+$r4PGh}RZMW1i`Z_~-Wv5$ zckKzgI6r({r)HtLm>)4~E~AaIk+0RePRZ9BHhC^N7Ee+sqv5k%`xo=aMGZFiZ&h^K zD$ag}W2Q03mJGckbk?S!tmVyd;a`oI3XEU;y+QxusD-fmr2%2Amb2C~ksm-;pz@_d z+S)GSHp_AdV~=I{U&-I`;RnHQ)>Z<>xFxsIn253vP7@1x(GtNNF!rD2Dt z9@|R^5|)AHNDbV_?tzh9Kbg>XcjVi`8K?G};VV8XVm*jtWQ~u-Z&U~r2uEiwHt-p# z#(_Oid9?3R!iw8XS0q%J59IpQ3EXx#nK&I|CRpeQUCB&4p5*rg$Z*i2MkUxMs6GxM zMxiqW9!RnwIik%G$QZ$Ot)qb}UuBC}xv?zN|@J&^; zA!@4Z7%h*>s7GH~VAbraH+_wMp=CXl>S;5T(YTZ3QY3bf?!TEkIU31BW4N(#PvuxQ z1=?wZ1$M1}mwiHdatf5Bcwl1eBm+HoxzTu6$hJUSpq?QynswP{j;AM7h|J&y zU_F=AcAuYON9mMjm;HQZw6+%0&mIi+K~6CY5!uFzh#wV%nx7o)I`y$|U#A9f)P$O! zN{^u8ZW1!_Pz^g?j%9(2)MtRp3Yxhv(D<0m{a)uqYe zOI~&OYl7mg`KrIJAE|lq>tg?hP_yL6je8jBi`eQc)j>R2(~0Z`*O~k320|>@(!IiH zG-K@6TckR4dD5{aJLw#TVRBsGi&3be12rFj1zPadOuG>ukxXzr;c-p5PD{$fV@JRY zl{-291(6n6tz ze*lC7oN4SGnx@pQKs*v^JV3u^s!E%fXdXhXG(~HqQ@jg?BWkD|Zf%SxIb)f&2!&)^ z!Nt(n-6#0oiDJI|%tDVbLxUh=f7mSj#@&?JpG2BS zXb6?^%&rjK-VOp@sM$426a9=X93(IEp0^$ncP1czSfdRR7-Lr`1)NncMi2IUmPUyk z?e#)djRPdpJjh+P+-JTXzYA=3hE}W*2m};C^NMxO05#xaO4&QdQbPYtjEJ~wRV*qR zUh=brTk&CzA1@;2%k>#XOC!?|qvxn<43N{4x2xqivA|>`KQ(C-%gi5ZuQEjD!gJQ~ z%0tvzIuuOafYwZI1ZS#)%Z>t14X0uD7UbAXU0A~&C?+heI+L?CxA_pwu0k5Dky_T( zcncXUXsEP&?%js-+(QiwvBCg;ed07$NX23=)^1(ufu|&~$VQh%IeteGotDyB+Ud-C zH;H|^OYwM-kH9hPYg4 zE6Os6n%S>{!1;z`41MyAW`*|L$za%@^v{S!`DC6<_Q`L)=ZLNdR_mtYnVuy3{yxrM9ZiR*B(V0scU^AV(Ohpz)05<~QA+$f|`s9b~ehcgfA7< zM1sJ~%no)Okf09XT||6!&cK%1c(GHd=cnB;=(k_C&8B>uqYy3Y34E^E+*Z(~o-Qn^?|Xysy~M_0fPkI2xE z6g*{mg!=WZl;vHIA0r}`beYS&!NNHcC5B+dmNGB?vng8R`zXg!d8xNY?PkXsek!4Q*H^h1Ozl1nL-=98o-nGS9u57nl*)El`U4P$1 z);)OebmWNeev8?Xb8tJp(22bFzy0qI)K61%G)<5RzsqdO21C$dZ_27FaXIHBGtvR= z)s&I-ws<``oo@0%(G4XeBC)n+#xpg-(4iz9hWjkBCrjY+f=m`1d!!1`r^IjQ11DoM;@2w8eESt=MEn-zpk8BS(*CE z$MH5SuDtxNH2U7X$g)zuU>O;Y96e+7_wC4vhY`((#61=oxWvSiwo?q`Z6D& diff --git a/icons/tangent-arc.png b/icons/tangent-arc.png deleted file mode 100644 index 0db8ac3a7fba8d5063089855bf182c076068aa07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27250 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 hX@@G9j-!`e$~ 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 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 - -

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 Vgne?E_}d^?F4166|?_}*v#6rh# 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 diff --git a/icons/mesh.png b/icons/mesh.png deleted file mode 100644 index dde867b613bb9a50e03e0ca387e6f8816ca158c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28445 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 z 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 k`Gi7QOJ|meX0*#&UhBd|HlWN>mm9FYLsjlv; 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�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

BB1vyr>=hu`W6H;Qj6k3ah3<#C*ivdm-4$Cu7O z+-Mf2>r1$itlcv+>Bw)|<@R;ZT2*vxiSVaED13>gNcJIx)KvyVD14NH>?jnWA)adpJ_-aEaUvGf280`{ zvz(@|nEG!T5?Az_6sX!)M99<3*Nt^Do<(lgWC&}J#ZOO^G^{P;+2MkCd?A1(q1`be z82t}@e(){Nex}l`6uh<*O38?$#czKSW7%T6RC)`mi^j1TyLJ1;Xk94sB?%*{HP~q8)Q9Yu?fNypVp)|R02WY?g z%u9_v<8QDiLx&)6y@_$xaS%t^K3m9rkMAZO0T`MxwQZsWL!QzaeJ)p&Dul|<{HPu= z5}7+6+iW5%rkHIz7H7u#Y?YzDQlqZAoLJ}P34ipP(j>SUpz7?>rna6h@6*7T`}5Jd zBRrPT%R2*|5GE|@&;uugC!3WjK;eBlX;ibpg5D=G9gKA>hlWsWClzRo;BK+Bk`l+7wb5yOVql2=TK_Jpi}O)Do~IY(^~{|DSSM@cg#u;9`0&# zkQ?>q{$4waau`*l-{mTi4;leI<{jfgc;PmDQ4%~|)%o+$qA7DWtPQf`T9M1mbl87a z7-u~YoAd6m6#F7_q6&jJf?U5Np1+QfUJRHzni7hRwDXcF<&XayM5Vx0QNvV~iW;23 z5t&L_kNFnzp^8ca{jfjzOCz0F&Wwu2Yim241f+b)o|pW0?X@Oxp4+-q96Ys^LrYUA ziQX3883-fB4N0dwX0%^SY6g zBlIUt*_^i#^mKNQX`VKc%DxU~>Be9mS1?1FU5|POLI|PV9qihd_Hx%bbB>h2l$R^I zCG;Tz<$8{>c(!VK&Z-HSmLl~mgDq#xJ&Qaxv`+;H_ zDZ6I7YtH~zV?%!#Sm?rQKcX0P7U_GN>2=)Bj_+VIA}t(Ysp)h==#e6=q9p^SSGb`m zHX)Jp)|*Cir@|?|`?$RPZW&0Q5P=^MA=_pum`3RAidXg-Lu>L7Z6o9{Ss>M!t>eDT zuNwojUG0VL-EtN^4h=XqeDVKdC9R#x=g1D8R$QPia?47Erd`` zgw`4k-0@-K)H4*=E}w$FrZ)SNWv!?9j3HxtTEd3|FppTJJD{9Z)zT#-WStSwxVdTL zT*5iG>_BsQjk|4pux`EjaDn%MX@WL%xubSwtF;XmPD_x4`JbA7Y%e}(abZtBW#f!H zKRVby(h+*c`*fI_#}YgFocXdax*E%s{@&7GQoDy)#I91EeJf%3vkOpCg=);&hw7T) zZa;R%cQNMrBOA1}k4yQb1-V?Z*L&x;NIDbI)}Q?>JEIxwIZoah+DCQf7xTi3cQvjM?0N3HH+#TD2>5WxDMi$+iStiMUC&$B zXmS#P67d!K;yD=nRinN?!%q!IlLEOampxX8oVHY5ZR=nD8kkX8+6B37AyoUU8X|c{ z%F;!O|G3y@aG!GS_*u7+&*d05G!4BPIA-Lx-v|fx7dK%Ws5GRpD}3 z@=l|(jEv5r$PXBcl^gQbwU@id+Z1qzOsUuKbwRO!a%HO z%No3ERs}q8hVpIpQ&%DsPs2D{khiaL@BF>9J|BzqCX`es+lj0{&x!~4&?cWI zOf1$jPTh-lhPij6A5n)(Yrd|Qt7c~nud?fULS09JOaBt`M#HD2!>;&oABEB7>+eF| zH>-N)-*he7&%h18U~CC6bxWXpryCeq-2Pkv$`Yf3Y3Q;;!v=<&Lu|;4nqvWn5>8st zHDxK4uZ{Fcxi)l%S?Mjv5LAv~CH9!%=^_DUQQNWza^P}7?O_cW?uE1*xge~{6Qe58 zS&c{>!tK3lw{4wOU$$l-!lG_hd~T?5OI-vxby~vEyl{=hipTwQ5TSy~wVM1#Cw&8e zk@%5(Y-E{VbwFnIR^^$%hqRLAFN1O2^q}nfo^aZtr&k#uov@(u$9eH@zTruzVADzW z^2VaW-|12VVVBB=X*D@#>Gsfgddcn5Q>-JZO|_WIr3Z=y7@|96cg+|IcHKp2yH0?W z`M?D`Aq99P_Zr}7fH>?5#GWkmqtvaChrE9g$U#1PS1(RJ#P_MA#|Bh^^uNjtb(ab| zFJt#q)zL#oJH_Fp0#x^sF=)^dj3?`xR*P8BtNJw^IQJrdIn{`MQ3%jtL_@PMi{n9` zDl$2Ny~)iz6|Tm{K=-45=wa2S4FR5fH1cL+^xDkS+WB>$K~o_5dx}br_7&E=_1xSO zTFOOu>qnOo>Q}4ZW+U%A5;*6yyRBzIMNNrYT+^F(xaje1qBaP0Sg-T<93N|77vGWl zgl6F(4Qp4K-6h|uW{BJqn%Af5$BH_y5!jP5FlMZD`#r{W=`@z%9Gvz_$ z^}m4H(u)X&RzcHiI|IksxGuowojcyLAD_Z1(KXW$pZJ;mn7eQlKlDj4T{%u~F%$&1 zG#CieXNJkH)bB7%qeYrjOA$RPTJcS#(y{qm2i_E9rd?XR!DAD|Oo(X+JB83*T{CgH zRAyjl3mtIf=g1f(P_@L_z6oZ7o;nUt&vj1SYh+0g^S1Cz%?P!ndwO!WgNWqWwSMo8 z+~=iN7bf_Mj0f;XUrIc+#3z%4s{X4^>-c&2hS^$reZp4MoxoS6b^oNRyfo9BKk-9?8Kk2 z13>Y5Qw4T_^km=tfSc=(SH>X$1udi}E`e`omaIr9u!|u!AjQ9g?6d=i==EA~mRAdC zXpG0NW~KE}s&UOZ{L_Gy5@u+`+E5HnVQ=iP>_mC&P5ySSQIVhH!XmRnE5!ZldVDdU zJAzIlQ&CR?kf4S!*>WHFWcL-`28mSmc^&T87$X#hivKodx9p)Qm41R25W5c%)@rWm z7t^eXpRElHfnp#yTL4MVq<{Vm&D=7U+3fQM3gf+_mkmW$-AamKDC-`%d`Cu9`nfnj z^_#OJlJ}x#{aw>WjD5yj0fS+iu_g>sUgtE?T`2GpWlGZXtUFG%_)zxSo^bUmtX!jS z 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 diff --git a/icons/constraint.png b/icons/constraint.png deleted file mode 100644 index a0e1889ae80a08b643a0749fd5ec55fc085b68dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26545 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 diff --git a/icons/edges.png b/icons/edges.png deleted file mode 100644 index d89c046fe6e97889a10ff9fdc7205d9f9c38b40c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27374 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 diff --git a/icons/extrude.png b/icons/extrude.png deleted file mode 100644 index 6d4a44a138f74c755490e5e349bf0216dccb8f0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27030 zcmbq(V~{4nvi8`vtsUF;j&0kvZQI@*+qQOW+qUt3bME=~#r=0PI=i|nGOIGHBNFj+ zq=K9TJPZyD5D*ajZ%I+5pAqXn34MbnU#T&^A|$p$BzW@eBovfXSSm_zhDJWfkgtr`=)YLt4u9jpGf{Bm>4Z||? zB6!-Lxpn?RTC_>qD|{G}vg)#;voPItr;_bM8toss^@K6SEU>03IZY2TmZX5pC316x z$Nz8CtG?4TwZ&muyXD)wNdzj#T%xo9Qh|7QB~ ztf=uEfar;!3#PjMNVk|E>Pg=}$(F<)+wrNjFZ}jtrXQX{d}@y~U3~rcuKxPI zz5CyY&xeGBgj>%Y3>9c8F;f!oyP+yGl^73H79 z)75s~7)ctT7_BFtaLZ{-^Vw&~8jh0q0V>^<9;xU*~TYa2V+ zi)gb83-(sbDLs4k`~e6#L`>OMS)Cpi0&5;w?sLv2 zE;Gt4ejOe?R>&%czH5F5O~d%Dc-y$=y3}a6BL3)zbBp{Y1_!$$+2-*I5L{f!x>Ie; zp~t2R)%WGw%Ykm|Dypi)O84}En$=Fj#D~WlFR|@ClAFM`bRrE@KY!po0f#w4$GP+b zGU({3gp%rY869eQ^j15_p(W6LEi;AP-vpsxyTjcj+;$)8Hju8KxQ371 zZe7BLj0kwYLujw4l0lsEC6U~ub5yQKjl8+fg#*x*UpfuW4)<0L%nKt{@-c8K7CRdZ zdFo{BdCG@IRZJaw#dsZez+bLhW|IN5>=?mBP8nHlGjb*b-0ZjN>G%O^3eX_pq!NRM z`wTsx2s%Qj!;RWQiKwBI{@a}We?h@hw0}*P+k5{=vQM>P_3uuP!D3@#j}aFNhb3X> zhW7yQt$P2&$X>u@LkxxqLqX&Es@m9u!7GE3bEXoDj829j@Q@aQ&dKt-5WGP=0<%ec z(8ZsbDCODgWodbj1*7BI7DUv>jG9AUuYFgKnDii(!;qz~a?{?HRaN;eBUA*I&fTNy6H zJDwXmYz3}Ki|YD7wT2s})Qm^yjYh=8lhE*xyh{7%j3rz%XcJ(y-(j!{32`NKw zz)&F>_6#Cr9-wPQK$@{Z%R4QVwW!R)scUPo2KI%RT2MgX?eL5fr{6~a;lsHqgKu4n z8HC5L2OXG*46;}E+^_2!0vHfJq)}C*7i+Aan{w#p+DzE|g?Hif9HB%j&Zn_|Z=3%R zInlC)gnE72Hv=&Ro)^`Cs;Mc!?jZ$z3;})2;=NBfsgw*A z2E7)MLf$DUi4buP`?mPCamaL>>qMP=(>Q))E*D1sz{Z+~#xosx*x(}GwUpt(Tw=AB zIGNVLctLGzEON)TPsNOxCi$ky+$Xt=VV?gcIL-q0;8Ab@4vKG|0h~lTm`Ebxh@r56 zB@aYi@F=7L{d`tTff+#yq|uQtoIFrUulcf{a81WP49+00*ID%P;WATE$w?hIMryS^mIhGLUxiejVM>wH(MXDaqLz%o;u2CzXuNZb zxS8vuesm@^fP5oR5$_ynivm zF(8bk0upIEk}?~sYfu|-&%Bt_m>Zc?@8T3?p7D2t{0oLrIRE8{r;Uaj)ndpN;H*(1 zej2P+nB{iyqA6yta-s2?Bf`SGxljakW1I(%m-p;=ldt{BRsUY1uOQN^~|_y8NDya!s_n5C4pyM9H; zTxv;yTP_Cwc9S2kI4_6h<0jJoyV5S5mh3H)TJWbC=0Sjq?KD!r>s^%T9#Sx&R#R%3 z^k{xtn5HWkPf54k300y6#l@_Tn2B%;oyX3lDCVO$cTo85au}h@ZYP3;_HS%uInW}_@!d$ zoCq``C(WmLukc<7tvDZ+bI>rC~pT>>?Je!Hom+z{90lw#jg1UvE%Tt z=Y46t7Q^fywA@S98ZqODw5s>BuW>5*W z-7y3_cyZ#h5cyLEQbYfz;@vNUHA`Vlrbu4Xnclz91?9EA@jiINZwna@!&*#RKE(ow*yYd!%4P61gdLS6R{QF*-}pYto(w`)cPF&u;`lz1uZ z{^VyY)yfIMy_9$`OX#KL>S&-f^>2O96z2*w6vz}>c>=Lq`fyqA`H)%s!z9LMrWSh` zL;tgBLHU z=^EH@gJoKSCA=daCI`(mLNrQP7nifmQ3Gkt2Q05yKbzDd`p-!ofQKT>+=Y zD%1+?>U(L|lLm89t97lVtXbQZYBRHj>!oA6p_#9y6?<5-LAkDGT3p3m$5Kf}b4Z1F zn;7`qYSL_90fGdoG@K1OL~NEWn=qgG8y`p3;%KUY;TA)6+*G-u>OZC@isCyHQ*EmS zFwCQOi|gY6v#~-ot(mM2HiWF`&Bl>3MHX>KK5H4ucT(0@qSsvrc>OC?((^Et(E zRK=IaB3MeTR7BFFSTpw}B7v#xyW6<!$V`>C|XJkH1R$!G&N z8pdih@gRA%@XRZCNXhaPVN*PE%D(!F+)zj8^e^iF2D*DK+H$u)uKr^G_7a0x3iH@H z__tV$*%H^ZT-59j@dr>n)r^y>#s8N36L zvWv{B=XKuLrySqMQpd&kIgKWBT2~5)t$blsc==Ur@fp0Zl(MYzrrU3AX9*bA9^g+q z4$YfUg9k&3-aI1SGzFD;82!wT*ZPVpC4yrz+I+zzc^lf$ z5`kFhi|_q}>J|USE#}R8hPsr;4#=vcd371CI zvy!Std>T=*UU>I^Ytwjm=^b4`K249^@!##$7@HKC zAL*Eyf~^0gx|8$NPEH#})2+R2n4UNt=@Pi<-H%dV>eO`6{8yoo$))}N`Wm}K$5Y+O zWqgiY!u;se>1b=q`DebyNNG9pLK3$4dSmJBVXgUTnjHpChS)tB%@~4*OY{!{NuRcwp%Trn3i8I`)a^O5TdF=%K)j+bJU_dR zzkpT-hd%oAAJZNw{KASR@-lB8q7^WbP=>{MU1;w+NYm#CLVh95mDYz!5bkhpW07_V z$1)*q^MFk^GVy9uLL-4WPYM5)Bfjc>sFimDEv-n4!G^yUnva%ySTt0MJ(S;A!4m7rtsoJ* z&B<7-c~o-=)@AF>#eyWSDGrAO;lk_5c_Ty#k4<%tH_eK2XX$JW(hLIRosZ-4QR)2O zmaVD%Di2|whXAlVi-B%FFXm{W?S2;eGcUYi!;RW6w?c+D0RS{&1c%$5d&J(+HXKVJ zvUQ2b^xV#gQ@3ba? z^cov!{|uY-;=B3;flA$`Ez)lmrY$o{CgoFR(ht;5|>oe~6FZsxC z!q%Kx+6MykJqr0dun62-AMEsd53)xeaWyI#nmd{!-?#Y%*@vjW>o5FszwyC1{A~s%-h(e@|jVAd&gQ2 z9@}*DfahJid?rX;i;*hePb(!+HZnJY!R5cH@ytBx7H%XP>tP#nRW4^Y1Lor1rrC#G zyAK66wjzmeJrWlZF6W5x3NHq|BvUwNi>RD6Gv}1{){|99uk^EkYyrSfojw6(|3(0F zyv@S1xy;a5r-qIbj|an_F2tDDAts%zVs=bT5p>awO@k)Y`)$Z;0V~Ma8V1&ZoH^Q) znw^HHq5TfRXlSe*13@y}`G?yDbDZwlkwl(qh)Vz-< zHb>Tu3_7LtizzO#jx06bIN)vVQpWVrM#`U4agG~AgnpPE8DuYipXM;u#>ulMx>|cO zXWKg?m5cA~wDNgLV}`2+=*J=&_E2wX{T@WCE05lxRd=fE(`ukQY-uJ)bWDZW}M zfoKGt`7`smvk#lE2q&@2Y?C@l%A8t4Npm?;^PJ^z3xpSJR`WJXcZ-8(GiTH|h{pE% zQ%<^$AxDihVmplBhvO+7-o1c$H2Q+ATD#-gkCR4EU46X!t3F>?j-X?NxPtcJVhlV@ zwc$orG*^dh&4pZytB3EYt_55J*q6@5ZNK!8(;MWxEn~Oo!p z4fAXaTb;HEv>F@3&|rGLz4Y(eMo;iodLCZGZ2ke)S+0R826{b~5Zi2ggj=`XHrb%2 zjna*FUEcGK(l*O_*%k7|_#Fl7j+$sCJOnR|JqN}%6nz*La`N}!7S_2g&Km{iY4645 zzy;j;tTrzDtG(kk^PS1P6&pR>*%4#hVB5!8h!*@Vf|TNh0(gWCuTh5v_syWc0W8-& zzyY1!_JaEQ_y!a5_!Q`fI32S+wK(2ilY&np1Ka#U+uS6?FP&wRwp}ydG_-c^-W(6# zyGVYiVF?pRS#tSJetkH8%za@zqYg`}%;LtzOq^V!p#yck2UGegm|tHAZbCbzr62SH zTgRieew%xv_oHX>BdT5j6)u0_xs@Su&w{2^gv_%FD3epj*Se=% z9>qfWd^ww{(f&p(Gdiz~N&IRtQJL^2$3=%mX&%R0QcX!J?ub%^Vbkk}%2X+U!aR+E zdGw&D!$vmHCzvB<>W`Kh2W!P?*+fAd+kUkv*e?2ox)2dj{yQ=?ll__UNOZs?U5O2I zJq0zTA_P^{IX-N2iVv$;YvH|Ujhkfa^2xzx>$3lhSi=-o&a1DKK9(&zrZH=Zexs{s z>YJ$-oV5F|NXgtK>slChh3BMPM<30y$D9f48eY@dh-oOFn?MU!5%T@AQS`;;-O~JF3$C`v~sckEp=+;``w9#h=HM#r-M1BPfh?vR{+N+m%+k-8g zG*_D1&L1ybQLp9*yP;btTdM!j1;3QCR1>O%!BMr#W>#nVo(HzLF?Z+ubAm6!0eA67SsEa z@5NIodu7OfH|8gm^s*%_CsS#uQ`gq0Y~dwCH1uQh7L;oa3SZJL=?CV?F7!$R`=wd! zzzE@55@Q6W56o)XC5g|O;qamOMdIZiVOWyoBF4L(bfc+gXa!x=QcT672AoA)uz;jfe!~s1ML)5PR`~Bs{Vv}sBB0Ah z{Iw9^i`8J=wOH9qB3p6gAjEN2GE?jjv?h^1&f!!1xD#Ewwd;jiC z6{-HJmDTPi^`{;R+a_E7<34j=Nf8U?c}%rhI@4XqKX4*7?vD#9@6-Rjfm!qge_R6o z;r|o;K$E^SePlFAcK=c=mH*0QVq~I34v{4Os+Tm8qD6@mHA<8u!-$N*Rmo@J;c0AC zUyh1;IKB4z)D`6IVIWAU8g;9y09tj5*S>fnaOnTN2fpgQ7lRlu1F~hHi3_d~0BRAC zJqVv$!F#LOI`#t$tN19{q3ipN;0CNXa2W)3;*81ec<=oI&A9-^mn)pE^U-YaO`mWR zgh0|}xGCVNRCr?-)JIo8xemAhZBBEtPQ67env?+JqvP#+`M7SwHR2L4-+Ct|PGsJ3 z>LF^>$ACr(YR|`WAXM!j^ttI^N>FdU+_>0j_Pp8(st0l_5hdmf4aX7qWgPr^R}6WuP+;K z{PN!PwsDaUd2~vJsOTZ5hmNb)_s4_6z@bgWSx-g9J3#?hpq;DRJ2cqfw$Qt0E7-Ud z)Z6J}-%O9Ae!=DT6&U3_wY9Ci!F>XP7Qx->fIxIz{$+DV-(TX-VLXVpZMxeMKSl35 zo#q@rfew)8;#!#Jstd@TG3->o>wYZ*S$Q62Udhp1K=`r#8O&Bh7>=9UzFTNW_*Cen zYEPLIUaH_#RX=MkuGYuBER&4vgZr^GX>^`_g(M=udk5F zAvcsXy4+`~0P`&>W-tKGjwPpV_xX~87DXOUR!oi*30G3wK;SP19ZarO5n508_at%A zixoUtus0^S6U}MPAU?U!0#;9_W0eJ5eDndCa(QiFoZ#li_6B(jxeu4YNa(CDd(p}p zB)ve^y%oA@<^>uObOC@vT37_6;XvP;?s(&3rH1oy`OH8WH3+>T(=9@fTUTl@qI z{GILyoSL>nAYkBi^g7(m1@<9CUBw@(&$Md}2q;#k=XEga^KwE~zSjBT)oeh|XLmT) zS8T}k_4z&>CbNJ?B2*morGN1u&TdXm8rUc|zgPRwL})8#{*PxIL7%>PX*>5kp9LEq zDYXwM3xwA|p?Lo&DVaaPWU+RFp5`a?nV9^qyG6q9wSZhg^d7(O_UxCoy{_M5iP@Ji zoM+#=&{_Bdsc>yW-3PT42qJ^N%ml2(urja(ApAzC8jw@#8sh%e%@&*DTk5}Cf$NLu z`&MB;=`85df;}%)duxCUwjr7ePsHJ`ZFzkyl(3J$-Bjky;wuq+^L$Jm4~OxJR0QRF z6m-^pzf%&0eDyO1Vcjf}tI~xSeXd_Gz78|jEhi^dd3xl}yn`>@9iEzGE$`^{jubC1 z7zYXsC4cb#iR;Nz^b)Se&+UU{*kAxX7S>%c1Ts4Cvg>p%*%Cz;W%JuXbfWTEj*}~E z0*$rW6RcqjHt=OF>%Tcde)(k}bTwKEaqaQ-{LVEhMyDNLZTA5lhQ-~wD`QzL;)e&+ z5l&u^=-twEHiHGx`ZBxE%OurhlK2$R)^6%w=4QLeQ>79Qf52bq-lH&%{7Ykesu&aQ zTRKcjF2PXtFpkdA1M{{owrxGOtS8;cHw%I`HQJ0OK(*$nm_+~XfppA2MJ zK%L+wW{EIESTnE%0LAzZ`{I)8xyrGeE#cx}yg)yYUMQ|H8>n3CgunOKF0>J0!h0+V zZ6`f0JA-fZX=6xSZHUfaaUEjEMW5JZhEQ+Run{6llg{iyWPcECh#+0uCD0G+ozh#? zevim(nX{bQj?7#GPpg&C7|ZmAF8ve#*%?qnZ!^iqS%%(qb$`C02%t|d67mzi-*7r) z5vVb&)q2?%$qyi0B}5hzhX+eJt~u*bA8sy%x%DW-jCNR5CSW{XJyi*s5dAzQzDeP* z^Dn?a%&((SckgsAA+?^n1F{o`d|zay1nGl@N!92$r+qb$ZS z8*qdKPNZfS>vdPBqh_#z;vuL<+ZZ&bAMm5#DGtWkNXsxznI&{b9lZ$|gH$voSei{W zONI`_@w0NUu_e~M@P&N6zXD6{wr>lP^^=%GaG1A`U|kGz&rYI@yF#T4ho%5b|D6j-B1o?igE6)o}wKR z?5<|rYjIcI?Qxk^K1)tpZwDz`1#2wN7=4KnTnF4^tUtnteNl1(KW;NlYC=e0_6BU4zP>BON0L87Kapvk9ao^`b zUQn3UzD52nX&8k#5`GT2i*Cvr%7M>aYk{nzrJG8DIYo)3*ntO~hIP7o`+%Xipbg_C zJ|RE`G-=6iDNc{9#bXZ_AGtf%PwyUtEf-Ozl?~Kyp}E&z3jX#(bSes!)7h9|Gipn^ z!A>6_dGe42hsh{PfB6LUYGO&NmK^8_FCCJTAx%6LcrxGSKsN`#K|}oF)hXw}o2z&9 z1SWGXdpIWCfc?x`XXozZFdnbEb)p|lxE7uA!o?{gAR_^|q#-%~{3f3D=vc_cag~5J zSwFEIzV|%*jYl_AjK?TEc?dP)#K|7t#gQgt67GN6e7{v)YjzWe?pGKDV*$S|SW%z1 zAVWCdKI@Fx6_$tIHlaZQBUZXh`bxNOrh@72Alnny;7-WTU>>x!_h%~tAkTb1Hek;~ z!-__QIN2}4K7W3$7;$$G8nPfG15UH2q>;o*o5dJ6zDcfWkOTRLo54ElS|HjLk{Hm4GZY_qn8Nt2mPNbHD z7#I0x#%WKJ%fgXy-G2NZM5O|zya)Q4iXqx!9a; zaQGc&rU>FDFhbL$S3-s~SRh4{?+@xw28IvrRvZjYMj;~-Z_2nDB!Ia%Fl4-~?z9iK zGR6b~6Q@1Ab0gbG(m}_V3)MV9N9^eNV_<5%Fy%fYNy~Q^1?%ISKTF*V%$SCSX@VYb5d;j-YJ&Qw4v<$<)4w(@1eQ8KXa+oiq!ZLg>=EzZ(b)Uq zfmQk)-iL;>RF|yN9xHsYN!kzlBDR2x@IprKGRI>Pj=?#;EAWW}C8=4cREk`EtRzZ2 z7v_d5Hr8Ln${tNKnK7^b18#akx257eRFjl7d%D} z+l!b{5yVcOf#py7jXx^gJk$z(PH<(X`|{TdJ}oI`I0vy z2U}cB=2FLuc3ml7xP`&vZ&w@?j>_Ce-j?9tPP;I>fEo*Gw0g!~LRPHZ(YrqV=7Z>A zg*^E=$kel&b*5A?Jaka_LBaQ{r-MlUsPx8f$VpNIE^*ymZf_B|!5fs;4HwGiV7~I| z4OTa<1uV3g^?V2To+_73ECNm)l+^x44j(T_#_Mh;r3JXVw+}v&1ny&U2D5Qk7LyaK@AT3vBG~r_s z^cVU;{&u2SNBui0b2{yrfS1HOy0rReW%}2}TP=(?AR)liUwS(EmEm-}(HC7l;#T9k z5@?pFQVsNE#+gV41k*WbcQM-gc(%e3R8n&KpW)fl8NSzmuvw%#MK5Ct|6A>LAyb>n zDb<|W;mQ+XvH$9UPHQ9iIHs?XpsT{hgb)=fGQ>29U_4|oZYCqJEMW`27r$LN979?> z_n~(>N3#vPtFGU3ob3rEOWd(5!`NqwJpP&djYcosqkz=o&&RQbC@EoaIC&qJl~bFs zbB$|K#^nbGI`4NRb3x*dl~X=22l-q}pzX#Wi18dVtMSSlu|_sy2faqt`})tS+6-`g;ZiWKTp zivBF;%zgN!#g?#5E%C8-zN-Tl7)oO9-nHp6GB^ejG)mzRuh0F(7XWt@IEpx-v395} z?GLyW=J?t!ASnUiPrSVk#2Q$WsPG;BDOdo}{}5($YVJ-b*2a-4XUrrSP1EB6aqWl z_VmA9zq9+fPPfScT|NSCWurZYHi&i9d*Adxt6$>|r^~d;-~u0PR$VjLSBXE&5uF}?3J2X(v zSM$0S{kP8o;6U7{PnHcs9TAyy8A}9VdJeOiNGCR!R=vo#CDx{MSU5scFy+g|JfUO* zEK93?Q77DwGFfrclJGabd*b~azDN_*uV~4KK1_{X2f&OBO}r1}feqfrOvPG#xx$ET zg^%G|`)J=Yuaky9Mj|{{K7^VB$_iBrt*=injX+Dfb)n?S2iu6?A|_(OTa!dF^j82A zfmZlzxmJkeuI2rDD{Y5x2G{byM6Dr2=3K^N@Pd3$>B3F%lEAW~kb^cM*2jVncw=PH ztA#It8)?aR1o)(u!u#_7FZYs{HAD!ucl(uA$p2tXguQkG z_2sgcVZz=a=ug=^*`KG#siEe{#9iSx3*gLh=vb!3VJ0Sg2;X;;`XwRZ-Rq~@u%T^w zJv)-RV<;7y`FMX>4JbcK7aSo^*lc1NF9r^|gr`tvf(PXCeaM*I$m}aVd}K-KO9Pw! zyCVFjEz=q-mYqyBB!;@8THO$NIrzyKM^e|I1YXj|YB6i11nYzU(euSqVtF8l`qXI# z<*z~uQRrJAp|K+X9w1S7A?&Zfu_kndNh*nL&0K2I;@-Serdl*mRNA!)+cMydp2U(K zWg7E(6uB$y0X|%YNbC|*7c{UO=BW)|A0QZsQ`N%Pr0rXOctCc!r~fqHcg%wh)d z`2GBj-PLp^GdNfnYXgQ#dvN!Xki|GBEm%7JyB)@9bq_+a+qS9XK{Pllm@Xn3w?le# zytx%9wWziX{Vjw9^^v6YwX~0;QSf+#(9aUvSuHYdy6It|v}NU-0ozlzIOtZviYDJTwl*z|V`YlCC~Duvt#e8>j?<}*vv z7G!Y?bEcYNSLMG22#i1J#7LkCdh{|t5=C@t$(-}k7qK}hmOn?tdj0ydNye{92qS!H z4DM+T?GwE0ROFyLWzW6NX>GBQ(T6E}lpn}&7plI0ey6t_CWgPG2RP21x9PrhhAwc= zH@nsx^P@6WZBw3-J!3!MCOAx_L*dJ^V%&=b_be_sy{8)yGM^2Q1CwJeGB3L!zM{rvG2~>g2UMb;jp`xgZdcxW89gR{+1=eKd5so>8=r&l9^h z8?#+qeKDAtT8#60vNF5KcV4JAF(uEBp6e~%^ZSVxh)V+Rdnx!UyyZNJVT6HJMZW#M zyn~RSRd>CGw2YE?w~D!_}?Nd?m!2RZdBw#=ow-xcu!Q+8(ETyCjBDF-!j-c0svtS9fsKQhUIKY@?V}4Pj^As ztj-Z%Vz_Rykq>SJUwDuB0PkT@hQHvYnw1~72uwq--WK-)|B`Rg4!=a=`U~hf^ zz>~!h9ZCa;UCc^&&1wC;&bp^nd=ofGH`n3^RHNk@5}5=7>3%er!2sX1J~GkY)+nrR zoUBj8(0BonWRV6eg7~D>$*`WlRA&YgNv_P7H^4!6=G1o7-{Um zEfbp~0yBlZ71L>}B(5Rw3gki)TQ;o&M{PT)ah}&>(nszn*9g2MAArc-1EEMV-KHne zZr22UO>KT}tbAeR=|Q)BE^%R&+(~Q~2ak)UdfO1Ms2(H4y>B*4Z?mYm;sO(G!f+6; zb!u1Kii-S|KdlddjkW!%nk6Q7jIDd_^WxK7UydJJJEfw0_OaC3*aQkTDOp=k;RE(P zvGFT7(RYD(CcyZ2sLge5Tf|k%n#smwh8oLH|OLdDt;N(&wnedC7@7FWd(AN%* z{ZOcT(JH+i8-+nKDr1W4XoPsL|s(pVuZu)BF0ATYd@3lHK+aK&R z1ZLy0dErRB7krThes2?5WSXhKzqOqVH-WTq8O%!fXoC!yA$)_hofqx9GI5x3oh(~yR-80|Um#S;!c>7Zq z+5UdJ2HLphE^%bgW42QC#mYZTy$WDt^;92$Ary=FEEQU3+}{7TU}ySr2GOq#gBwS2#Ia>P8gY>x4exd6?$z;_RBrVr+Jigl6;4t;l%JZg3?ySB=ql$V&x z^EH~-`ZF^3ar%PZo7HwW_kBO%A_ z#0w9J_j`Ug1w(%na5iJw2mth{S3R0wH)k;a^`@~vNZWI2CiDWw*4lRmY3m2&7onBY zf<-|5__Hnheh$hdc!{2BAmSq0N5VTo=nRLq7His$(i-R!qZKmPgE*~CcPHjG=eAT6W%+wOFSeYqSlf6 z<6i@g^uwJM0^NM=i+`fRbBv$XWY%+)jRRQen5P^^W9tnMu3?sth+I}!phasltnc7u z+W3+Kb$O8Q;+nYDhAi&p(*kFXz3hg>9>CNh33@m+SDoSs~z$oIo3P3=3-;tPRuu?u>G z?SMsD_{2uBxL3En0z1|S=ia;!$SYp&p-DT?`TUPI1XjX8uMnUY7A(m0@cogW@bEW+ z%Hie@tH(U^&t@Au8{CeGdE*)`!AliCk+yu$n5w8x-utud@>EMUJV-@w)h7 z(=#HwpHuv0RKW@%PSl=V86BWe`t51qR7CWYv71-J{Vo*}ZT{H-c$ppT3CcP&lcO+# zI^kH1^6oRKS(D~n&vD0|AU)qF%yVv7?}%v|N09?b@byp~@SER|HM`U&Vn!@283VAg zdTLtiC%U-d%|%RBd{p%C0~C$1uQNEo3g1V)HGAWT?02$#2wZnUE^9A}?poPne*Ry} zprXs!=@^SG+9vIS#jsn>${d&dF%zseNcY4aUUtBNbaej`3T|p-YR%KvA0Kd4vDdGG zAPwh?0`;jJqAr3QmIP&`qDOxv6~4oRU(LzcG|pY~g3Y1I zAO$4j_^y)==i5QaR)T!xp@`(R(IxdK&%Ln1;+-gliQr6qc4>5SW?8jH`8^K_mYn=W?CgQOh)hV*$~ z_~Ic$XXB=N(ztK_$%Hz*mqpMtjD+}U3U3h$(>)}AayRp8kYwvQam!tnhQ#td9jHjr zyB8pw<~VF<2}3T>e6UrPRFgC$9c$FYt_{oSZqw>Ytbw?Etu_LTeoCdyVvU3+%B=gx6fQ#s4apRmkl|(yf2I4brseZ(ThG|G zpY3b?6Mp;Mu!r-o2PJkv)^vo0Sx^!qR2WL=6_`LdiIKNJKVl8fx)C8m@rEZtDs=!q z+u2oA$% z+`{+Q;7K>vRI}&GC3vT@EgwOdN4Owo^OTEWyBuK+9Pvd0W(JRoTq2e{$v6-P3kX3S zp%0Ct-O>w`Pm&9r%QB;CBHoX8OwzXXXSC% z*UkXc;H3CZZYQ(XSFI3*2%8{O%pai{Ov<p%=Rl)<#J~!dLt{#`l zBwyzR(j(~_Z4Wa;ShA)QaDLE{yn<-yPeGdt^Iw?Ns9PPW*lADevtN)QHG=R;V3{r% zmie+K&8<&}I$qNM9_Xob{*7%r;cKg?My#hC#{=&0idV1k8pVNcJ zAQ`$uu@`;s4^q^BzE=u=JJRSFSq;Y$Ai*-W6`cCA3v@L>xMVa9vrCN6c1{Ly4}E-G z2&A-50sB_~IWOUm2dNDW-L+RRBpisgl;N52fyctP`Ngw_>komR_rl9w^9j_zNAEtn zA_~n2WJeIdeP==*&dTJK87STUqu0iPLp;8C+PN4PhQ#Dn!o|{h(u->p`V}KuLN(J> zqoh3cA~n~y=1P2sYxCSS`r31zA4X9O#xR|ohlBt}VEioW+lCa{q}>*8455KzD4J_!OfVku6C-7O zHKfu*NurXm}#O$mfiXu=ZchHCJe1{6=h&WjHOkiL<&ItbDkn}RHPD5evV9vabn(pVY#bzWa%Im*cA}|Y!>Dkj^oDJqD zHGFpcNi;@KY|cO!WLPSR|J;n2p7-K7>mIw3>OqR5NWB9wjNcDfe{d?Ag6a6V>c8*_ znfjDxx6ERuv+v#Z?K}Bz-!r48Alr0X%y?utA>o$fjj(9naI#KEd24cKvRlaad(XQ< z#d1*egspg7WQWM2hP-0;MCAjw*gbgw$Lh)1QWFs(M*FW$QNq*D#NpF8izDuYs;*;a z$2ZzFb?$IYx6lg?Nnf)li%A88+R8@ta(Sb&{(rPhQXci_p3MuYIt$O0dcOGDbWXA#d$wG-J7+JCIgd+)JfWuFk>#{&la}9^F0q21ASY*<1bta zPAon@CJFgV!}y4CV@Zr%IM-J07_{oMTh|xc{GD9a6N)8VG{Qy90odm+Y+8h6Mpziq zTJuJLj!q+OJvGn;jNqXYn6;9W=Zarqk9;)5($gscI^`m4ZHyh$#OEGreiWsTv?+ew zfD00wqGl%k@0nPz>Lp0MA_n}{DQi#@ksI5$3*nqWqXbqWxB*NjMN|?GI7nUz6<=L~ z$IC-{;7wOS!M>8$kD)PHUrcAk(%q+r8r8#4!{v?Bx2NwFSDpu({mIK;S1eQfXrfWX z+12VOl$)I>8@`Iu@t)n4`ItE*!XTI)!9Le1N!cHmYb^C^Z%mT(Q!R_k{=|+(>yuRi zH$iu`|F635j%sRY*SDj>5gP*1R8&NofYKpg0TrZ(G-)aVDosEF2_!jKKm~(-&(WQ%v!T&@0oXIo_XJyXWpH? zJMYHGaS$SII}L-uhBBTS&bP4+nN^x-^dsee=h5!}WXoLcq(p)===L0DK77MTZs zp$PVU4?ashdQBqs<#m(OMD&U{ZE*4W?w$bT9j3_W`TMj_-?Z^x>le{P<>m!IPb~=EG4m64 z_R*0Y&fSDHdaRhCmihZa=tDzK z9gKFoULCF3x}_&?;zjSk2yWz^?KL!Q<j_W+0jIYB!o4}n4ON_ePQANH2Hbc&F8_P!k5yUo0ETiu^T_o zQZ0VtFbYCWgJsBp?X^%~%YzBG$ItJ)t-NFvdc~1f+tNI2q{Z|kuHG?;_EVmK=$5nw zzVYkWzwZ{>(2_~uSRET*&ams)4H45zQSPJ=<5Rsf?N<(F*;rlX;IEDY zxam>SBSn-07pQA__2tZbj}ImJpk+_dhup=ni@V&27X{SU8d9y0RZDP~L+zlf+kC0d z$J)x@++{LoOeitEZhwjLgf1}n>o-03d5ou^$9Ri(twcB&;p)791*mNiXAO1odepYr zj~4faEUK1kyT=}2Ok5Q+%jlZ*la~weyc)mSVe@i`Z|-A=17f+6i}Dgrp18ziE1x^t zsiAQDaujS=a35RVSl#Ov1Q&gi&gUoumwQs>_sCA4tl+Yl`{*DApr*yK=N*pf>$YF|wv<;sInoQSeKuxK{_tx<;YN{-%EbM> z&yM%mt4KevexQHu-bRx!!-y<39_S~la&Pje+eS?>Bt9VOa$Y$hD7{)qJ19ypcUF$J z6Nu?t&yPqMQpq&HUR*Ul^Kt=2`qcVn_r2W3>(>i?;BDt}^6(J-1u@BWItEC}o@+~j ziYr&-7$!uQ1nhcXu|UA4xt(^C)wcx(o%94WuA_{($j6e@+#vg`)rN5s3JW$rD|7n3 zmmLUuF9R)S*_e5r+Yn$b+T5D8gELamf_W7XxAXslq+2>5b|bRjC~V-u=&E8EZ^ZpSu!Kk`(HE1<3+BRN00pL zAMQW6yZ;#~Bcu7wIT?vv&;E&~K?2_TGKq{wBVuNaI0fX{@Vufez8jooz=6VjEz*tI zv{Fff`F8)EtrNp#kcfEACK#V>O#iO$yMx8Ivi!}jK&7GV*0J04eu+xh-s=uLaN3tt z!U~a_<9UlwG{3|1XrotNcZvT>L$ulBN9022GF6EbK#ZuZ2#HqwAPL!7Ai_X=*O7cQ z2qEG`FKP@3H`eAjjZtxRU$w+98#O9YbT5lgrx`CBYNdS(y{<|V)*(wDA1Z6vSt+of zf;adA07t}lV?}Vy??9cr2ik|515pH_r zXb#h$0(Qmhvz2tmH_9#A#z*n7vMvR}Eu{i~4fGm}Ama5%6$uc|e!~C-k<$>{BU%di z>gXIvm4`aRere7=)9N?-3Xe5$4g=Sjn{^!raipAbgxt6IZlWwe*Osnn6D^wXl-FtV zd7?BSOkws%)rhIc(*4+G6X9`1EQhg#i_A~9nVPHBnrbV_wO+o6`@hMJ0-F#ucaJs= zcD_PDJ$?R<`)n6PJiV891~w^7TGFG1P6|&pDVKww{d(z?i-X^KA4+%7*_-#QYy>3P zUj35ebGlSJZwFXEdTYcweJMniDE~n*lj6=ZMde983CQb6;BVac0VKE6kE_76wy(Fx z->>Tr)8Te{<1X>y0eab2fl3U38E7!i!`jACKYd88 zd9L{Tp|R2ToJ32bQt08(_enp$^_48v%m&eq7q(Xw`k(D9U?@3xK&u7r+zKlT(y-|v zuY9$zh&4fU<+6=4X-FX2Lw3posRGVs#RI9#xIR^$;!bkZ+ZQp}+ubNd1F`K=_5O+v z5v;jvk2c7|2``@oC*B|49AFALT!p$C+KC)|FdF(#i+j^0rrk!u%NiBA`q0 z!y&#)c1HMcSEIB1XfXHp`dPH|s1nVUt4um<3iMcZOb8JL+wp}dhzvFNPe%)fg23ndBRh z!HRARCF-1A(GrNAvr0wS$l=qIy!A}JtZs<|;a%*8ZPBWOFPNaTC&^RnE2h1= zj*ap6xr*U+qsX$fhmAR$*U_{LR*%IOT@;0N4Z$>s!#yZxL^8VW_Y6c3BD*_SHP4*n zuW{yG$f56^tr`^5h6v#C7kb|^^#!#suhqmK`@&) z`G5yZ-}EKJY|*`>dYRb17hKd5QbEhLjSn`c(;WWhe`toF3tQ=^ncZe*&qYv^B;diP<{mhS zOjv7_>w@6W#VJ=W$ME6WDixv@Noq z;?6JPMPZirS$(Bf_VBPsq9;Hs1^FAVtCNF?1C86qLLv?7JWI+42x%e=o_G(z0Xk@N zARf-nnbKg&t+_qw&FpIj6!^KySg@}EG_we`8E zAUCaq8lTi6B+f`$dr0yh6xk0RP|2G(>oxMJ4C{rVV%9>(OoI-ZLSn3$j16#Gp|9Z8 zxQFn4SjX#}^II#?I3-hL)>^d7QVnlXdw|d1kz;H+SxsU@$&W1`dd%NEyffovz3X^S z*>%g~H&Upkc&RjXm+4u0W@ll{d#u&!bp^YcOI@VxE}q9^TG8imfnP@HJ%I-+Y%6+Cc;{B`bQS27Gw#kyOOcC2v~BEM#%X}G#=3gfJ~XpPxt zA*@FE2}gzdgf#DN6upn|tV^_^L;9^?{P5OkbmUoLUnDQSIy2R1*Y90*`8b?Ep|~p5 zQDpaNPAt5KI`t@Na;c7f>h>FVxOX?^K4rM1`tw?uT2A)x8mqP^(sLBh|DR6YsQIv_eQSt?R}%R<&DhlH=BIK{_7)15iYYxZY;3|g z!h$@lJ{EE$>7*@9TaH}u($t8UXHRpUliG@kKVurfzT9mxW$xrSwo@Bcbeotlz!H>%>S$_yM)F z_>fAF(P#Og?h;|=CEUKsT3Y02rx>C{fbL#40}Wb(@gyUQDiITQ#i+WY&$}>KUM>2U zC<16PrDB-arHQbQ%MlVVTb!%0xb5M6&2po0YZo4^9VPo=E&y1#>9)^7T*bM>= z-s}E7H^2_q&3EBGq+0n%!P`~mb}O`Mn4tDWereDYCh3Ouz|th=PzV)pLBwk0N$1j( zqTr*czrZt$Gi70Abw7cck_$+>&bP*wj>axE30*)yqBq|97yccy*x;g;*kp6g1McD# z{Ln|G43z}KrAQFM+IS$!h!G{XTDQ|Aof>0aB{}X>-imK5k&4gfI`gI>v+Yu1^*);* zW%# z6JGS`7M5oQ>$g1;Nk8VhFtL4+exxUCdd(GVB%Vuj$2Ag+pK)H40(SyI|DfhSi`?J# z#42azK=iLsJ}bEyeh4UHH-^ay=LwPwb^fZg;h1Co4w zRHq|2!m!thv$FP$ioyE)Y*J>AQp~E=@sC1Qiy4v8>qBuo#r^Taa+788HvAo2)50K^ z#U)0EPK5Vo_M0NWU<8v+qM#pzpg{Fwa%BOCsqV|X4I;7h6B`lK5GNEyiv2cYwd|uR zmwbeM8}B|sSg*cfR7ABK|72%u0u%w^IRZ#V7VXn-Sk~6DtfqihFgP!fRyq_@c_Sr` zu3{j2>6Wyp zd?f324^;CqGtczvgiFj|Cp^^D`%}{vNI}z$`iV92=;+p7948$!k4p*=ZGDnYzvpPG zv%rtZ8z{v?Ds0ERVRjgrFM<1RA-Qopi9Ib`ePT16?Wl(iy>kgz>3JmYC#=v-SMH`v zeBU$zhP1^2%F5LhjL4JoA(i|}xbAUc#ihGv%9F$_>Jka;jGJ^+%$ull=I|$0a(x1$ zxEKDUcgaz5Cd=vY9ZQfTs3$%TiXI48$`K}ajuUkI z+dRSKBgwRaiOHzQ0TgS}Xe`5Q4Dx!j*FUXEzC8KpuPhb0yIZ#Gt^8jTSN6w0X6k3g|lpsy;NfjU>mW&U@=HHZY6xc zOTmHbj*q`uPb7Hv1@pao9?L_MRdC}`{$ zG)}v7>BIlXGU|#9j+kooosbVOL$v5_j7grN$5KF@-g-&Y^X|o!M`HK||EoEx>FlQZ zbPh8|fFD^)iiv}uI1x>)nvH1!AWp$RVE-J(7eSP3q%XE5xz93`Vumti!l@7ZI+f6b@2_z3Nl1Jth~}L?vgl#7F#H zk112ZJIM~@3dvNqf3?Dx1m_|u^9X}R9nJLWB1W*j&c&7h+an${PB#UCkg#aa#C}b9 z2dp2wxzl;wcsYt3(&K-fz07j+OqnWQMwnMowrf%gRWoNoLJ^nJ1=v&&jA&+#oQeKJL^kfr%N`-@}ysr>qZFsS37MexoTtyip3#B53jV_a9smOb_o_ z+=qKkp7_DixahUa4Dj{`^)NnSEJ4}1<%P$9v`GT^TujUevR`flVT#MYinVX2jr)_s zsnv5@xCcrj$!pjCz&#O8z@-V%$qX_t*ef3vTSdRUupwCoxr|!QVB)FO%nOSzDFE5E z6OjkFfLB?0`F09&sw*e>r9S8S>$6wQgpn3Sjj_hMp`+PcX4O+Wq_)WMpSX-}5p(G;NX31|fl z`pCK85!#Khx&6VgMgp1f;dvolJlU#?xQr~_^?}d|ee(7|jTe|WucL|p8M_3vhpk`8 z5h#Sr`>>VD(V}ZmDu~zgJ}KFb#(_q05~FKRkKm5;U-;HSHAUY9^}-y%p!Dyy6TdK< zrUQcYNA0Ax;P5bWzIn~cp8%{xs|_S=uTDV_6a)E@CR>H3FssQ%>X>Ew*SB6gRi*Wu z*eWXYn!@9GD{PuT95ZK4-BL81-51>(J=#h1?VyYEKUk$9lC^axxbb2PJ`|Im`7k2L zPdX#j46BJ95>3L2N_v+W*(Gq)UFttQ_Wgr865gc07LZ>ba7qcOKFUWfp z8bMP}lMzWUnW|5u?4zz+a^y-|Rq`0P2Sg-&Cw^PyTwdKIW^-?kr><&*YNdmS=7+t70s~Q}&3;+_xPlSp#px?KK6=NM1L-rW9w}yRD-*`zY z&JW(?sTs?!OV()IA>?a}=qCuyMEA*+5;0kh11tHHyn1uY&q@+;9c?{N zH&j}r35GvnJ8M!AW|Bte;AUNl6nU?=JK%j5u^jZ|yH60c<&xQa=x4xDRPn|pbz?tn zw@Dd@e9$ELucTj?;N!qgGgA(E(uC2VheH^1=WvCrZ~RSN>mX)J z&k*w%05y6j$@v^oD(X4n=$i;n91!(_^75WDX-T>XkV49!E75 zyG*O`?y`n0iWaiZvx2KC97wYt_6h^@tx9FF2&FS;Z` z?(vx6+X`o0yR4To!c|o$UeGK*WGF{ zw>iaKOO=1!IaQtb$I8IxK*OXL4F}0-E2yeW`5_Em*$(dnRav-6@NL0tbbP1ja(?F1~wXm_!_e|47y=o;1pmo=0$axT1)cO3tN#PB6oW7 z2P`f6VmnIt$u11#Jt2zrc5bLRJjt~a8-Pi4EK+h_$r5M{k?SL_n}#e&SP8MKCJ96J zHYz}F8PleSVI(Xl-*Qd$KC`jojV+Nch}wI zeFGM*&n;v=Cp_6-VjDqR8vLdJ-}cuFPV$M1aCsaK{IqxN%(ax0*HTzSGMT(KH|JCL zTZBDEpF=$)DhsU&_@jY(gQUj>^2DjBreOrLFT>8=WJ9gk+4iC3xTFfgN zS@p7nnzA9bU#`KH$~4KktD|!u-2_Bcjwrh)Z-2{KT!H>*eoFipijqIkUa14mg=TN! zl?$)+`>;QL3sO0$0hq7yFFh@Kr85VyHpWM8??UPHfOCRUt1{@ja+^*Nt;&V|N-3pX z4fo;xocaor1ot+y>%sBB0Mi_iS3f_M8c@E{i?Z6JdSD3gRJ`sDUbfe1Sf`0_foPFY z=OnOBb7T)CI>91*9-h!idA0|TzR=T5_f2XxT6 z;8ui*-+0D=91QIlkUsp?Gn^XO^Dv!kt?iv2j_}CPPV&fadTfg<_gCnwbs3gz@pWhK z>6t7VC3YJzGv{7azFG5Y|FB6XU7>Sa$90O;>v=uzOCJ{tkZ_y34Jj+1yFEBJQJ7zI zDeFPMMX~|8MVU!-%T20r2D7HJ-%^I`UJm?*IlxEB>ww<8$&UBkU-9yVJ!M_z`IVNW zao|w1cJ4~6=a< z!rz-^`>2xD0~U;4`S&@Xy2OCuQ*pm@)EyUkEZ3As+z*i!3*!uw4!B~a!KKURw@YZ? z7XpSbH#*+@K}dD4!%W~{tLl`sUVrYQz6_bev?MI?zix+V>U6)KV~&-ZaH_zP)^j_d z@OJONXXbw#26l?JYE1e~GuygP^|&tnmVmpC&f&Qm7aT{nJ1m*xGBuRYnLrU@%uj3vva=;6^ETk^y5FAqazRwcbEfL);0X=tNa zuS=zKp>Oc9Zk?8yw~s2XpFC)h@;QD-a6tVevSe9)pV;-hl-N^SK0W-f;n7{Dp5C4J zzSr->;k}b=Ix`@tWcePJnBc1J*KZ#UTcfjNetEWa<+=CsoEuLyC*w@~sEi9e{>bm#eWA)k<+sh>v?>zl4-=B@O diff --git a/icons/faces.png b/icons/faces.png deleted file mode 100644 index 8fd4e363a97f892618b7fdcfe229f6aa0b38a61a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 609 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAv7|ftIx;YR|DNig)WpGT%PfAtr%uP&B4N6T+sVqF1Y6Da>C%`AfRasfNu~AD{SlG?Yt+KK* zH#b*WT6+3)>Ez_(sHi9d1B1Lgv(1~iTwGk7ot^FM>>M2(wY0RNqodQ))8pggi;9Zu z?d|#b`JJ4cJUu;KU0uDsy(1!AGcz;!`1mX=EOd2sEiEl;YjtB|9hWQ-mXnk7^788G zQTFig;Ns$PaBxUVvvGHK4-XHwv9U2XH=jM5@z5cL`ucjH1HHO03o$S-dV9J!hE&{2 zGH7FBWm8K|VGwZO3Gni7?vHd2Vpn7oa7f@Zvz4u_t*w)dRcv8#U@&ZGW&QH?Ye`9K zZ;7A~1LKheoMK@q*Pa>}IZb2WP+((Am6c6PixpH>4&iKINV;HbVh|FTZs;@}Bx%dM zpl6$48_<}>Qi+6#K%qHS3``u$`5G7wtz=ecm{7W*!{hafmxYc@yg*(_!HZ9?nw{7r zGORuoF)*@RKe+Jp8h++IyPO^Yc~=$JtY#K}cKKmSVk$F-LI=7Ft5=!bFVdQ&MBb@0M#DENB{r; diff --git a/icons/hidden-lines.png b/icons/hidden-lines.png deleted file mode 100644 index cf43afbde50f5aa87a343ad69bcca0905557ecd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26946 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 Z?dtdKFi^Gr{C9u3a>?dm@%h_N{|_q!YU}_2 diff --git a/icons/horiz.png b/icons/horiz.png deleted file mode 100644 index 5706c691dc65bdcc7e5f31648b6ab8243518da1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26274 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 WLQ`h$%F^GmyLjI2T=AJ3kN*#1E;ls* diff --git a/icons/in3d.png b/icons/in3d.png deleted file mode 100644 index 0d6be0760e134b6f34e2af3ac13cb9654a03b916..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26608 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