commit 67139236fc0b63e06eaa1efa8fe0022df5612bd2 Author: Jonathan Westhues Date: Tue Mar 25 02:02:13 2008 -0800 This is my initial checkin for solvespace, a second attempt at constraint solver drawing. I've started work on the user inteface, which will be based around two windows: one with the graphical sketch, and one command line. I've started to implement the command line, no other work. [git-p4: depot-paths = "//depot/solvespace/": change = 1652] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5d125d1 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +DEFINES = /D_WIN32_WINNT=0x400 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN32_LEAN_AND_MEAN /DWIN32 +CFLAGS = /W3 /nologo -I..\common\win32 /O2 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /Zi /I. + +HEADERS = ..\common\win32\freeze.h ui.h solvespace.h dsc.h + +OBJDIR = obj + +FREEZE = $(OBJDIR)\freeze.obj + +W32OBJS = $(OBJDIR)\w32main.obj \ + +SSOBJS = $(OBJDIR)\solvespace.obj \ + $(OBJDIR)\cmdline.obj \ + + +LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib + +all: $(OBJDIR)/solvespace.exe + @cp $(OBJDIR)/solvespace.exe . + solvespace + +clean: + rm -f obj/* + +$(OBJDIR)/solvespace.exe: $(SSOBJS) $(W32OBJS) $(FREEZE) + @$(CC) $(DEFINES) $(CFLAGS) -Fe$(OBJDIR)/solvespace.exe $(SSOBJS) $(W32OBJS) $(FREEZE) $(LIBS) + @echo solvespace.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 + +$(FREEZE): ..\common\win32\$(@B).cpp $(HEADERS) + @$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\common\win32\$(@B).cpp diff --git a/cmdline.cpp b/cmdline.cpp new file mode 100644 index 0000000..0bae73a --- /dev/null +++ b/cmdline.cpp @@ -0,0 +1,85 @@ +#include "solvespace.h" +#include + +void TextWindow::Init(void) { + int i, j; + for(i = 0; i < MAX_ROWS; i++) { + for(j = 0; j < MAX_COLS; j++) { + text[i][j] = ' '; + meta[i][j].color = COLOR_NORMAL; + meta[i][j].link = NOT_A_LINK; + } + } + ClearCommand(); +} + +void TextWindow::Printf(char *fmt, ...) { + va_list vl; + va_start(vl, fmt); + + int r, c; + if(rows < MAX_ROWS) { + r = rows; + rows++; + } else { + r = row0; + row0++; + } + for(c = 0; c < MAX_COLS; c++) { + text[r][c] = ' '; + meta[r][c].link = NOT_A_LINK; + } + + int color = COLOR_NORMAL; + + c = 0; + while(*fmt) { + if(*fmt == '%') { + } else { + if(c >= MAX_COLS) goto done; + text[r][c++] = *fmt; + } + fmt++; + } + +done: + va_end(vl); +} + +void TextWindow::ClearCommand(void) { + int j; + for(j = 0; j < MAX_COLS; j++) { + cmd[j] = ' '; + } + memcpy(cmd, "+> ", 3); + cmdLen = 0; + cmdInsert = 3; + row0 = 0; + rows = 0; +} + +void TextWindow::KeyPressed(int c) { + if(cmdLen >= MAX_COLS - 10) { + ClearCommand(); + return; + } + + if(c == '\n' || c == '\r') { + // process the command, and then + ClearCommand(); + return; + } else if(c == 27) { + ClearCommand(); + } else if(c == '\b') { + // backspace, delete from insertion point + if(cmdInsert <= 3) return; + memmove(cmd+cmdInsert-1, cmd+cmdInsert, MAX_COLS-cmdInsert); + cmdLen--; + cmdInsert--; + } else { + cmd[cmdInsert] = c; + cmdInsert++; + cmdLen++; + } +} + diff --git a/dsc.h b/dsc.h new file mode 100644 index 0000000..5b59c93 --- /dev/null +++ b/dsc.h @@ -0,0 +1,12 @@ + +#ifndef __DSC_H +#define __DSC_H + +typedef unsigned long DWORD; +typedef unsigned char BYTE; + +typedef struct { + double x, y, z; +} Vector; + +#endif diff --git a/obj/t b/obj/t new file mode 100644 index 0000000..e69de29 diff --git a/solvespace.cpp b/solvespace.cpp new file mode 100644 index 0000000..a56cc81 --- /dev/null +++ b/solvespace.cpp @@ -0,0 +1,11 @@ +#include "solvespace.h" + +SolveSpace SS; + +void SolveSpace::Init(void) { + TW.Init(); + + TW.Printf("She walks in beauty"); + TW.Printf("like the night"); +} + diff --git a/solvespace.h b/solvespace.h new file mode 100644 index 0000000..058fe07 --- /dev/null +++ b/solvespace.h @@ -0,0 +1,23 @@ + +#ifndef __SOLVESPACE_H +#define __SOLVESPACE_H + +#include +#include +#include "dsc.h" +#include "ui.h" + +// Debugging functions +#define oops() exit(-1) +void dbp(char *str, ...); + +typedef struct { + TextWindow TW; + GraphicsWindow GW; + + void Init(void); +} SolveSpace; + +extern SolveSpace SS; + +#endif diff --git a/ui.h b/ui.h new file mode 100644 index 0000000..603a2a3 --- /dev/null +++ b/ui.h @@ -0,0 +1,59 @@ + +#ifndef __UI_H +#define __UI_H + +typedef struct { + static const int MAX_COLS = 200; + static const int MAX_ROWS = 500; + + // The line with the user-typed command, that is currently being edited. + char cmd[MAX_COLS]; + int cmdInsert; + int cmdLen; + + // The rest of the window, text displayed in response to typed commands; + // some of this might do something if you click on it. + + static const int NOT_A_LINK = 0; + + static const int COLOR_NORMAL = 0; + + BYTE text[MAX_ROWS][MAX_COLS]; + struct { + int color; + int link; + DWORD data; + } meta[MAX_ROWS][MAX_COLS]; + + int row0, rows; + + void Init(void); + void Printf(char *fmt, ...); + void ClearScreen(void); + + void ClearCommand(void); + + // These are called by the platform-specific code. + void KeyPressed(int c); + bool IsHyperlink(int width, int height); +} TextWindow; + +typedef struct { + // These parameters define the map from 2d screen coordinates to the + // coordinates of the 3d sketch points. We will use an axonometric + // projection. + Vector offset; + double scale; + Vector projRight; + Vector projDown; + + // These are called by the platform-specific code. + void Paint(void); + void MouseMoved(double x, double y, bool leftDown, bool middleDown, + bool rightDown); + void MouseLeftClick(double x, double y); + void MouseLeftDoubleClick(double x, double y); + void MouseScroll(int delta); +} GraphicsWindow; + +#endif diff --git a/win32/w32main.cpp b/win32/w32main.cpp new file mode 100644 index 0000000..d04dca7 --- /dev/null +++ b/win32/w32main.cpp @@ -0,0 +1,237 @@ +#include "solvespace.h" +#include +#include +#include +#include +#include + +#define TEXT_HEIGHT 18 +#define TEXT_WIDTH 10 + +HINSTANCE Instance; + +HWND TextWnd, GraphicsWnd; +HWND TextWndScrollBar; +int TextWndScrollPos; + +int ClientIsSmallerBy; + +HFONT FixedFont; + +void dbp(char *str, ...) +{ + va_list f; + char buf[1024]; + va_start(f, str); + vsprintf(buf, str, f); + OutputDebugString(buf); + OutputDebugString("\n"); +} + +static void PaintTextWnd(HDC hdc) +{ + RECT rect; + GetClientRect(TextWnd, &rect); + FillRect(hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH)); + + SelectObject(hdc, FixedFont); + SetTextColor(hdc, RGB(255, 255, 255)); + SetBkColor(hdc, RGB(0, 0, 0)); + + int h = rect.bottom - rect.top; + int rows = h / TEXT_HEIGHT; + rows--; + + // Let's set up the scroll bar first + SCROLLINFO si; + memset(&si, 0, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_DISABLENOSCROLL | SIF_ALL; + si.nMin = 0; + si.nMax = SS.TW.rows - 1; + si.nPos = TextWndScrollPos; + si.nPage = rows; + SetScrollInfo(TextWndScrollBar, SB_CTL, &si, TRUE); + + int r, c; + for(r = TextWndScrollPos; r < (TextWndScrollPos+rows); r++) { + if(r < 0) continue; + if(r >= SS.TW.MAX_ROWS) continue; + int rr = (r + SS.TW.row0); + while(rr >= SS.TW.MAX_ROWS) rr -= SS.TW.MAX_ROWS; + + for(c = 0; c < SS.TW.MAX_COLS; c++) { + char v = '0' + (c % 10); + TextOut(hdc, 4 + c*TEXT_WIDTH, r*TEXT_HEIGHT, + (char *)&(SS.TW.text[rr][c]), 1); + } + } + + TextOut(hdc, 4, rows*TEXT_HEIGHT, SS.TW.cmd, SS.TW.MAX_COLS); +} + +LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_CLOSE: + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + PaintTextWnd(hdc); + EndPaint(hwnd, &ps); + break; + } + + case WM_SIZING: { + RECT *r = (RECT *)lParam; + int hc = (r->bottom - r->top) - ClientIsSmallerBy; + int extra = hc % TEXT_HEIGHT; + 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; + } + hc = (r->bottom - r->top) - ClientIsSmallerBy; + extra = hc % TEXT_HEIGHT; + dbp("extra=%d", extra); + break; + } + + case WM_CHAR: + SS.TW.KeyPressed(wParam); + InvalidateRect(TextWnd, NULL, FALSE); + 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) - TEXT_HEIGHT, TRUE); + InvalidateRect(TextWnd, NULL, FALSE); + break; + } + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + return 1; +} + +LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, + LPARAM lParam) +{ + switch (msg) { + case WM_CLOSE: + case WM_DESTROY: + PostQuitMessage(0); + return 1; + + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + return 1; +} + +static void CreateMainWindows(void) +{ + WNDCLASSEX wc; + + // The text window, with a comand line and some textual information + // about the sketch. + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); + wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC | + CS_DBLCLKS; + wc.lpfnWndProc = (WNDPROC)TextWndProc; + wc.hInstance = Instance; + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszClassName = "TextWnd"; + wc.lpszMenuName = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = NULL; + wc.hIconSm = NULL; + if(!RegisterClassEx(&wc)) oops(); + + TextWnd = CreateWindowEx(0, "TextWnd", "SolveSpace (Command Line)", + WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | + WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX, + 10, 10, 600, 300, NULL, (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); + + ShowWindow(TextWnd, SW_SHOW); + + // The graphics window, where the sketch is drawn and shown. + wc.lpfnWndProc = (WNDPROC)GraphicsWndProc; + wc.hInstance = Instance; + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszClassName = "GraphicsWnd"; + if(!RegisterClassEx(&wc)) oops(); + + GraphicsWnd = CreateWindowEx(0, "GraphicsWnd", "SolveSpace (View Sketch)", + WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | + WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX, + 600, 300, 400, 400, NULL, (HMENU)NULL, Instance, NULL); + if(!GraphicsWnd) oops(); + + ShowWindow(GraphicsWnd, SW_SHOW); + + RECT r, rc; + GetWindowRect(TextWnd, &r); + GetClientRect(TextWnd, &rc); + ClientIsSmallerBy = (r.bottom - r.top) - (rc.bottom - rc.top); +} + +//----------------------------------------------------------------------------- +// Entry point into the program. +//----------------------------------------------------------------------------- +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, INT nCmdShow) +{ + Instance = hInstance; + + // Create the root windows: one for control, with text, and one for + // the graphics + CreateMainWindows(); + + // A monospaced font + FixedFont = CreateFont(TEXT_HEIGHT-1, TEXT_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); + + // Call in to the platform-independent code, and let them do their init + SS.Init(); + + // 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)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +}