#include "VectorDes.h" #pragma hdrstop void DrawHotRect(Draw& draw, int x, int y, int cx, int cy, bool hot, Color color) { if(hot) draw.DrawRect(x, y, cx, cy, color); else DrawFrame(draw, x, y, cx, cy, color); } Size ScaleSize(Size sz, int size) { if(sz.cx == 0 && sz.cy == 0) sz.cx = 1; return iscale(sz, size, max((int)Length(sz), 1)); } static void VectorSerializePacked(Stream& stream, int& i) { if(stream.IsStoring()) { if(IsNull(i)) stream.Put(0xFF); else if(i >= -0x40 && i < 0x40) stream.Put(i + 0x40); else if(i >= -0x3EFF && i <= 0x3EFF) { stream.Put((byte)(i >> 8) + 0xBE); stream.Put((byte)i); } else { stream.Put(0xFE); stream.Put32le(i); } } else { int x = stream.Get(); if(x < 0) return; if(x == 0xFF) i = Null; else if(x == 0xFE) { if(stream.IsEof()) { stream.SetError(); return; } i = stream.Get32le(); } else if(x >= 0x80) { int y = stream.Get(); if(y < 0) { stream.SetError(); return; } i = (x - 0xBE) * 256 + y; } else i = x - 0x40; } } static void VectorSerializeFixed(Stream& stream, double& d, double maxval) { if(stream.IsStoring()) { int i = Null; if(!IsNull(d)) i = fround(minmax(d, -maxval, maxval) * 0x7FFFFFFF / maxval); stream.Put32le(i); } else if(!stream.IsEof()) { int i = stream.Get32le(); if(IsNull(i)) d = Null; else d = i * maxval / 0x7FFFFFFF; } } static void VectorSerialize(Stream& stream, String& str) { if(stream.IsStoring()) { int len = str.GetLength(); if(str.IsVoid()) len = -1; VectorSerializePacked(stream, len); stream.Put(str.Begin(), str.GetLength()); } else { int len; enum { MAX_STRING_LENGTH = 100000000 }; VectorSerializePacked(stream, len); if(len < 0) str = String::GetVoid(); else if(len == 0) str = Null; else if(len < MAX_STRING_LENGTH) { Buffer data(len); if(!stream.GetAll(data, len)) { stream.SetError(); return; } str = String(data, len); } else { stream.SetError(); return; } } } static void VectorSerialize(Stream& stream, Point& pt) { VectorSerializePacked(stream, pt.x); VectorSerializePacked(stream, pt.y); } static void VectorSerialize(Stream& stream, Size& sz) { VectorSerializePacked(stream, sz.cx); VectorSerializePacked(stream, sz.cy); } static void VectorSerialize(Stream& stream, Rect& rc) { VectorSerializePacked(stream, rc.left); VectorSerializePacked(stream, rc.top); VectorSerializePacked(stream, rc.right); VectorSerializePacked(stream, rc.bottom); } static void VectorSerialize(Stream& stream, Time& t) { if(stream.IsStoring()) { if(IsNull(t)) stream.Put(0xFF); else { bool dt = (t.hour == 0 && t.minute == 0 && t.second == 0); stream.Put(dt ? t.day : t.day + 0x80); stream.Put(t.month); stream.Put16le(t.year); if(!dt) { stream.Put(t.hour); stream.Put(t.minute); stream.Put(t.second); } } } else { int i = stream.Get(); if(i < 0) return; if(i == 0xFF) t = Null; else { int day = i; int month = stream.Get(); int year = stream.Get16le(); if(year < 0) { stream.SetError(); return; } year = (short)year; int hour = 0, minute = 0, second = 0; if(day >= 0x80) { day -= 0x80; hour = stream.Get(); minute = stream.Get(); second = stream.Get(); if(second < 0) { stream.SetError(); return; } } t = Time(year, month, day, hour, minute, second); } } } static void VectorSerialize(Stream& stream, Color& c) { if(stream.IsStoring()) { if(IsNull(c)) stream.Put(0xFF); else { int rgb[3] = { c.GetR(), c.GetG(), c.GetB() }; int rgb4[3]; for(int i = 0; i < 3; i++) rgb4[i] = (rgb[i] == 0x00 ? 0 : rgb[i] == 0x80 ? 1 : rgb[i] == 0xC0 ? 2 : rgb[i] == 0xFF ? 3 : -1); if(rgb4[0] >= 0 && rgb4[1] >= 0 && rgb4[2] >= 0) stream.Put(rgb4[0] + 4 * rgb4[1] + 16 * rgb4[2]); else { stream.Put(0xFE); stream.Put(rgb[0]); stream.Put(rgb[1]); stream.Put(rgb[1]); } } } else { int v = stream.Get(); if(v == 0xFF) c = Null; else if(v == 0xFE) { int r = stream.Get(), g = stream.Get(), b = stream.Get(); if(b < 0) { stream.SetError(); return; } c = Color(r, g, b); } else if(v < 0x40) { static const byte table[] = { 0x00, 0x80, 0xC0, 0xFF }; c = Color(table[v & 3], table[(v >> 2) & 3], table[(v >> 4) & 3]); } else stream.SetError(); } } static void VectorSerialize(Stream& stream, Font& font) { if(stream.IsStoring()) { if(IsNull(font)) { stream.Put(0xFF); return; } if(font.GetFace() < Font::OTHER && font.GetFace() < 0x40) stream.Put(font.GetFace()); else { stream.Put(0xFE); String s = font.GetFaceName(); VectorSerialize(stream, s); } int ht = font.GetHeight(); VectorSerializePacked(stream, ht); int wd = font.GetWidth(); VectorSerializePacked(stream, wd); byte bitmask = 0; if(font.IsBold()) bitmask |= 1; if(font.IsItalic()) bitmask |= 2; if(font.IsUnderline()) bitmask |= 4; if(font.IsStrikeout()) bitmask |= 8; if(font.IsNonAntiAliased()) bitmask |= 0x10; if(font.IsTrueTypeOnly()) bitmask |= 0x20; stream.Put(bitmask); } else { int x = stream.Get(); if(x < 0) return; if(x == 0xFF) { font = Null; return; } font = StdFont(); if(x == 0xFE) { String name; VectorSerialize(stream, name); if(stream.IsError()) return; font.FaceName(name); } else if(x < 0x40) font.Face(x); else { stream.SetError(); return; } int ht, wd; VectorSerializePacked(stream, ht); VectorSerializePacked(stream, wd); if(stream.IsError()) return; font.Height(ht).Width(wd); int bits = stream.Get(); if(bits < 0) { stream.SetError(); return; } if(bits & 0x01) font.Bold(); if(bits & 0x02) font.Italic(); if(bits & 0x04) font.Underline(); if(bits & 0x08) font.Strikeout(); if(bits & 0x10) font.NonAntiAliased(); if(bits & 0x20) font.TrueTypeOnly(); } } static void VectorSerialize(Stream& stream, Value& value, int vtype) { switch(vtype) { case INT_V: { int t; if(stream.IsStoring()) t = value; VectorSerializePacked(stream, t); if(stream.IsLoading()) value = t; break; } case DOUBLE_V: { double t; if(stream.IsStoring()) t = value; VectorSerializeFixed(stream, t, 1e6); if(stream.IsLoading()) value = t; break; } case STRING_V: { String t; if(stream.IsStoring()) t = value; VectorSerialize(stream, t); if(stream.IsLoading()) value = t; break; } case WSTRING_V: { String t; if(stream.IsStoring()) t = ToUtf8(WString(value)); VectorSerialize(stream, t); if(stream.IsLoading()) value = FromUtf8(t); break; } case DATE_V: case TIME_V: { Time t; if(stream.IsStoring()) t = value; VectorSerialize(stream, t); if(stream.IsLoading()) value = t; break; } case COLOR_V: { Color t; if(stream.IsStoring()) t = value; VectorSerialize(stream, t); if(stream.IsLoading()) value = t; break; } case POINT_V: { Point t; if(stream.IsStoring()) t = value; VectorSerialize(stream, t); if(stream.IsLoading()) value = t; break; } case SIZE_V: { Size t; if(stream.IsStoring()) t = value; VectorSerialize(stream, t); if(stream.IsLoading()) value = t; break; } case RECT_V: { Rect t; if(stream.IsStoring()) t = value; VectorSerialize(stream, t); if(stream.IsLoading()) value = t; break; } case FONT_V: { Font t; if(stream.IsStoring()) t = value; VectorSerialize(stream, t); if(stream.IsLoading()) value = t; break; } default: NEVER(); } } static int GetEllipseDistanceSquared(const Rect& rc, Point pt) { pt -= rc.CenterPoint(); double a = max(1, rc.Width()) / 2.0; double b = max(1, rc.Height()) / 2.0; double nx = pt.x / a; double ny = pt.y / b; if(!nx && !ny) nx = 1; double hyp = hypot(nx, ny); if(hyp < 1) return -fround(sqr((1 - hyp) * a * b)); nx *= a / hyp; ny *= b / hyp; return fround(sqr(pt.x - nx) + sqr(pt.y - ny)); } static int GetLineDistanceSquared(Point from, Point to, Point pt) { Size ft = to - from, fp = pt - from; if(int tp = Squared(ft)) { int scalar = ScalarProduct(fp, ft); from += iscale(ft, minmax(scalar, 0, tp), tp); } Size diff = pt - from; return Squared(diff); } String FormatVectorFont(Font f) { String out; switch(f.GetFace()) { case Font::STDFONT: out << NFormat("StdFont(%d)", f.GetHeight()); break; case Font::SCREEN_SANS: out << NFormat("ScreenSans(%d)", f.GetHeight()); break; case Font::SCREEN_SERIF: out << NFormat("ScreenSerif(%d)", f.GetHeight()); break; case Font::SCREEN_FIXED: out << NFormat("ScreenFixed(%d)", f.GetHeight()); break; case Font::ROMAN: out << NFormat("Roman(%d)", f.GetHeight()); break; case Font::ARIAL: out << NFormat("Arial(%d)", f.GetHeight()); break; case Font::COURIER: out << NFormat("Courier(%d)", f.GetHeight()); break; case Font::SYMBOL: out << NFormat("Symbol(%d)", f.GetHeight()); break; #ifdef PLATFORM_WIN32 case Font::WINGDINGS: out << NFormat("Wingdings(%d)", f.GetHeight()); break; case Font::TAHOMA: out << NFormat("Tahoma(%d)", f.GetHeight()); break; #endif default: out << NFormat("Font(0, %d).FaceName(%s)", f.GetHeight(), AsCString(f.GetFaceName())); break; } // out << ".Height(" << f.GetHeight() << ")"; if(f.IsBold()) out << ".Bold()"; if(f.IsItalic()) out << ".Italic()"; if(f.IsUnderline()) out << ".Underline()"; if(f.IsStrikeout()) out << ".Strikeout()"; return out; } Font ParseVectorFont(CParser& parser) { Font f = StdFont(); bool font = false; if(parser.Id("StdFont")) ; else if(parser.Id("ScreenSans")) f = ScreenSans(); else if(parser.Id("ScreenSerif")) f = ScreenSerif(); else if(parser.Id("ScreenFixed")) f = ScreenFixed(); else if(parser.Id("Roman")) f = Roman(0); else if(parser.Id("Arial")) f = Arial(0); else if(parser.Id("Courier")) f = Courier(0); else if(parser.Id("Symbol")) f = Symbol(0); #ifdef PLATFORM_WIN32 else if(parser.Id("Wingdings")) f = WingDings(0); else if(parser.Id("Tahoma")) f = Tahoma(0); #endif else if(parser.Id("Font")) { f = Font(); font = true; } else parser.ThrowError("font spec expected"); parser.PassChar('('); if(!parser.Char(')')) { if(font) { f.Face(parser.ReadInt()); parser.PassChar(','); } f.Height(parser.ReadInt()); parser.PassChar(')'); } while(parser.Char('.')) { /**/ if(parser.Id("Height")) { parser.PassChar('('); f.Height(parser.ReadInt()); parser.PassChar(')'); } else if(parser.Id("Bold")) { f.Bold(); parser.PassChar('('); parser.PassChar(')'); } else if(parser.Id("Italic")) { f.Italic(); parser.PassChar('('); parser.PassChar(')'); } else if(parser.Id("Underline")) { f.Underline(); parser.PassChar('('); parser.PassChar(')'); } else if(parser.Id("Strikeout")) { f.Strikeout(); parser.PassChar('('); parser.PassChar(')'); } else if(parser.Id("SetFaceName")) { f.FaceName(parser.ReadOneString()); } else parser.ThrowError("unknown font attribute"); } return f; } void DrawLineRect(Draw& draw, const Rect& rc, int width, Color color) { Point vertices[] = { rc.TopLeft(), rc.TopRight(), rc.BottomRight(), rc.BottomLeft(), rc.TopLeft() }; draw.DrawPolyline(vertices, 5, width, color); } Rect operator * (Rect rc, const Scaling& sc) { return Rect(rc.TopLeft() * sc, rc.BottomRight() * sc); } Rect operator / (Rect rc, const Scaling& sc) { return Rect(rc.TopLeft() / sc, rc.BottomRight() / sc); } One VectorArg::CreateEditor(int vtype, int editor) { switch(editor) { case EDIT_VALUE: { switch(vtype) { case INT_V: return &(*new EditIntSpin).LeftPos(0, 80).TopPosZ(0, 19); case DOUBLE_V: return &(*new EditDouble).LeftPos(0, 100).TopPosZ(0, 19); case STRING_V: return &(*new EditString).LeftPos(0, 100).TopPosZ(0, 19); case WSTRING_V: return &(*new EditString).LeftPos(0, 100).TopPosZ(0, 19); case DATE_V: return &(*new EditDate).LeftPos(0, 80).TopPos(0, 19); case TIME_V: return &(*new EditTime).LeftPos(0, 150).TopPos(0, 19); case COLOR_V: return &(*new ColorPusher)/*.NoNotNull()*/.LeftPos(0, 100).TopPosZ(0, 19); case FONT_V: return &(*-CreateVectorFontCtrl()).LeftPos(0, 150).TopPosZ(0, 40); default: return NULL; } } case EDIT_DOC: { return &(*new DocEdit).LeftPosZ(0, 100).TopPosZ(0, 80); } case EDIT_ALIGN_HORZ: { One droplist = new DropList; droplist->Add(ALIGN_LEFT, "left"); droplist->Add(ALIGN_CENTER, "center"); droplist->Add(ALIGN_RIGHT, "right"); return &(*-droplist).LeftPosZ(0, 100).TopPosZ(0, 19); } case EDIT_ALIGN_VERT: { One droplist = new DropList; droplist->Add(ALIGN_TOP, "top"); droplist->Add(ALIGN_CENTER, "center"); droplist->Add(ALIGN_BOTTOM, "bottom"); return &(*-droplist).LeftPosZ(0, 100).TopPosZ(0, 19); } case EDIT_RECT_STYLE: { One droplist = new DropList; droplist->Add(VectorRectData::STYLE_RECT, "rectangle"); droplist->Add(VectorRectData::STYLE_ROUNDED_RECT, "rounded rectangle"); droplist->Add(VectorRectData::STYLE_ELLIPSE, "ellipse"); return &(*-droplist).LeftPosZ(0, 150).TopPosZ(0, 19); } case EDIT_LINE_CAP: { One droplist = new DropList; droplist->SetDisplay(VectorLineCapDisplay()); for(int i = 0; i < VectorLineData::CAP_COUNT; i++) droplist->Add(i); return &(*-droplist).LeftPosZ(150).TopPosZ(0, 19); } default: NEVER(); return NULL; } } VectorInfo::VectorInfo(String type, String vm_type, One (*newfunc)(), One< VectorGeomArg > rect_args_, One< VectorGeomArg > point_args_, Array args_) : type(type), vm_type(vm_type), newfunc(newfunc), rects(rect_args_), points(point_args_), args(args_) { vm_index.Reserve(args.GetCount()); GetFieldContainer(vm_index, args, &VectorArg::vm_name); } int VectorInfo::FindArg(String name) const { return FindFieldIndex(args, &VectorArg::name, name); } String VectorInfo::GetName(int lang) const { int f = name.Find(lang); return (f >= 0 ? name[f] : Nvl(default_name, type)); } String VectorObject::FormatCoords() const { String out; const VectorInfo& info = Info(); if(const VectorGeomArg *ra = info.RectArgs()) { for(int i = 0, n = ra->GetCount(this); i < n; i++) out << (out.IsEmpty() ? "" : "; ") << ra->Get(this, i); } if(const VectorGeomArg *pa = info.PointArgs()) { for(int i = 0, n = pa->GetCount(this); i < n; i++) out << (out.IsEmpty() ? "" : "; ") << pa->Get(this, i); } return out; } void VectorObject::DesignPaint(ScalingDraw& draw) const { Paint(draw); } void VectorObject::DragPaint(ScalingDraw& draw) const { Paint(draw); } void VectorObject::SelPaint(ScalingDraw& draw, const Index& sel, bool is_hot) const { const VectorInfo& vi = Info(); Color c = (is_hot ? LtMagenta() : LtBlue()); Vector mrc, mpt; for(int i = 0; i < sel.GetCount(); i++) if(sel[i] & ARG_MASK) if(sel[i] & RECT_MASK) mrc.At(sel[i] >> INDEX_SHIFT, false) = true; else mpt.At(sel[i] >> INDEX_SHIFT, false) = true; if(const VectorGeomArg *ra = vi.RectArgs()) { for(int i = 0, n = ra->GetCount(this); i < n; i++) { Rect rc = ra->Get(this, i) * draw; Point mid = rc.CenterPoint(); bool hot = (i < mrc.GetCount() && mrc[i]); DrawHotRect(*draw.draw, rc.left - 5, rc.top - 5, 5, 5, hot, c); DrawHotRect(*draw.draw, mid.x - 2, rc.top - 5, 5, 5, hot, c); DrawHotRect(*draw.draw, rc.right, rc.top - 5, 5, 5, hot, c); DrawHotRect(*draw.draw, rc.left - 5, mid.y - 2, 5, 5, hot, c); DrawHotRect(*draw.draw, rc.right, mid.y - 2, 5, 5, hot, c); DrawHotRect(*draw.draw, rc.left - 5, rc.bottom, 5, 5, hot, c); DrawHotRect(*draw.draw, mid.x - 2, rc.bottom, 5, 5, hot, c); DrawHotRect(*draw.draw, rc.right, rc.bottom, 5, 5, hot, c); } } if(const VectorGeomArg *pa = vi.PointArgs()) { for(int i = 0, n = pa->GetCount(this); i < n; i++) { Point pt = pa->Get(this, i) * draw; bool hot = (i < mpt.GetCount() && mpt[i]); DrawHotRect(*draw.draw, pt.x - 2, pt.y - 2, 5, 5, hot, c); } } } Rect VectorObject::GetBox() const { const VectorInfo& vi = Info(); Rect box = Null; if(const VectorGeomArg *ra = vi.RectArgs()) for(int i = 0; i < ra->GetCount(this); i++) box |= ra->Get(this, i); if(const VectorGeomArg *pa = vi.PointArgs()) for(int i = 0; i < pa->GetCount(this); i++) box |= pa->Get(this, i); return box; } One VectorObject::Copy() const { const VectorInfo& vi = Info(); One newobj = vi.New(); if(const VectorGeomArg *ra = vi.RectArgs()) { int count = ra->GetCount(this); if(ra->CanSetCount()) ra->SetCount(~newobj, count); for(int i = 0; i < count; i++) ra->Set(~newobj, i, ra->Get(this, i)); } if(const VectorGeomArg *pa = vi.PointArgs()) { int count = pa->GetCount(this); if(pa->CanSetCount()) pa->SetCount(~newobj, count); for(int i = 0; i < count; i++) pa->Set(~newobj, i, pa->Get(this, i)); } for(int i = 0; i < vi.GetCount(); i++) vi[i].Set(~newobj, vi[i].Get(this)); return newobj; } Index VectorObject::Contains(const Scaling& scaling, Rect rc) const { const VectorInfo& vi = Info(); Index out; if(const VectorGeomArg *ra = vi.RectArgs()) for(int i = 0, n = ra->GetCount(this); i < n; i++) if(rc.Contains(ra->Get(this, i) * scaling)) out.Add((i << INDEX_SHIFT) | RECT_MASK | ARG_MASK | TRACK_RECT); if(const VectorGeomArg *pa = vi.PointArgs()) for(int i = 0, n = pa->GetCount(this); i < n; i++) if(rc.Contains(pa->Get(this, i) * scaling)) out.Add((i << INDEX_SHIFT) | ARG_MASK | TRACK_POINT); return out; } int VectorObject::Nearest(const Scaling& scaling, Point pt, int& maxdistsquared) const { const VectorInfo& vi = Info(); int nearest = -1; if(const VectorGeomArg *ra = vi.RectArgs()) for(int i = 0, n = ra->GetCount(this); i < n; i++) { Rect u = ra->Get(this, i) * scaling; if(IsNull(u)) continue; int s2 = min(min(pt.x - u.left, u.right - pt.x), min(pt.y - u.top, u.bottom - pt.y)); if(s2 >= 0) s2 = -s2 * s2; else { Size d = u.Bind(pt) - pt; s2 = d.cx * d.cx + d.cy * d.cy; } if(tabs(s2) < tabs(maxdistsquared)) { nearest = (i << INDEX_SHIFT) | ARG_MASK | RECT_MASK | TRACK_RECT; maxdistsquared = s2; } } if(const VectorGeomArg *pa = vi.PointArgs()) for(int i = 0, n = pa->GetCount(this); i < n; i++) { Point u = pa->Get(this, i) * scaling; Size d = u - pt; int s2 = d.cx * d.cx + d.cy * d.cy; if(s2 < tabs(maxdistsquared)) { nearest = (i << INDEX_SHIFT) | ARG_MASK | TRACK_POINT; maxdistsquared = s2; } } return nearest; } Point VectorObject::TrackPoint(int track) const { const VectorInfo& vi = Info(); if(track & ARG_MASK) if(track & RECT_MASK) { Rect rc = vi.RectArgs()->Get(this, track >> INDEX_SHIFT); Point pt = rc.TopLeft(); if(~(track & (TRACK_MASK & ~TRACK_RECT_SIDES))) { if((track & (TRACK_RECT_LEFT | TRACK_RECT_RIGHT)) == TRACK_RECT_RIGHT) pt.x = rc.right; if((track & (TRACK_RECT_TOP | TRACK_RECT_BOTTOM)) == TRACK_RECT_BOTTOM) pt.y = rc.bottom; } return pt; } else return vi.PointArgs()->Get(this, track >> INDEX_SHIFT); return Null; } void VectorObject::Create(Point from, Point to, dword keyflags) { const VectorInfo& vi = Info(); Point rest = (from + to) >> 1; Rect rc(from, to); rc.Normalize(); bool is_rc = false; if(const VectorGeomArg *ra = vi.RectArgs()) { is_rc = ra->GetCount(this) > 0; for(int i = 0, n = ra->GetCount(this); i < n; i++) ra->Set(this, i, rc); } if(const VectorGeomArg *pa = vi.PointArgs()) { for(int i = 0, n = pa->GetCount(this); i < n; i++) pa->Set(this, i, is_rc ? rest : n == 1 ? to : i == 0 ? from : i == 1 ? to : rest); } } void VectorObject::Track(int track, Point from, Point to, dword keyflags) { Size delta = to - from; const VectorInfo& vi = Info(); int tm = track & TRACK_MASK; if(const VectorGeomArg *ra = vi.RectArgs()) { if((tm & ~TRACK_RECT_SIDES) == 0) { // sides for(int i = 0, n = ra->GetCount(this); i < n; i++) { Rect rc = ra->Get(this, i); if(track & TRACK_RECT_LEFT) rc.left += delta.cx; if(track & TRACK_RECT_TOP) rc.top += delta.cy; if(track & TRACK_RECT_RIGHT) rc.right += delta.cx; if(track & TRACK_RECT_BOTTOM) rc.bottom += delta.cy; rc.Normalize(); ra->Set(this, i, rc); } } else if(tm == TRACK_MOVE) { for(int i = 0, n = ra->GetCount(this); i < n; i++) ra->Set(this, i, ra->Get(this, i) + delta); } else if(tm == TRACK_SIZE) { for(int i = 0, n = ra->GetCount(this); i < n; i++) ra->Set(this, i, ra->Get(this, i).Inflated(Rect(delta)).Normalized()); } } if(const VectorGeomArg *pa = vi.PointArgs()) { if(tm == TRACK_POINT && track & ARG_MASK) { int i = track >> INDEX_SHIFT; pa->Set(this, i, pa->Get(this, i) + delta); } else if(tm == TRACK_MOVE) for(int i = 0, n = pa->GetCount(this); i < n; i++) pa->Set(this, i, pa->Get(this, i) + delta); } } Size GetAlignOffset(const Rect& rc, Alignment halign, Alignment valign, Point pt) { Point a = pt; switch(halign) { case ALIGN_LEFT: a.x = rc.left; break; case ALIGN_CENTER: a.x = (rc.left + rc.right) >> 1; break; case ALIGN_RIGHT: a.x = rc.right; break; } switch(valign) { case ALIGN_TOP: a.y = rc.top; break; case ALIGN_CENTER: a.y = (rc.top + rc.bottom) >> 1; break; case ALIGN_BOTTOM: a.y = rc.bottom; break; } return pt - a; } void VectorObject::Align(Alignment halign, Alignment valign, Point anchor) { const VectorInfo& vi = Info(); if(const VectorGeomArg *ra = vi.RectArgs()) for(int i = 0; i < ra->GetCount(this); i++) { Rect rc = ra->Get(this, i); rc.Offset(GetAlignOffset(rc, halign, valign, anchor)); ra->Set(this, i, rc); } if(const VectorGeomArg *pa = vi.PointArgs()) { Rect rc = Null; for(int i = 0; i < pa->GetCount(this); i++) rc |= pa->Get(this, i); Size delta = GetAlignOffset(rc, halign, valign, anchor); for(int i = 0; i < pa->GetCount(this); i++) pa->Set(this, i, pa->Get(this, i) + delta); } } void VectorObject::EqualSize(Size sz) { const VectorInfo& vi = Info(); if(const VectorGeomArg * ra = vi.RectArgs()) for(int i = 0; i < ra->GetCount(this); i++) { Rect rc = ra->Get(this, i); if(!IsNull(sz.cx)) rc.right = rc.left + sz.cx; if(!IsNull(sz.cy)) rc.bottom = rc.top + sz.cy; ra->Set(this, i, rc); } } Image VectorObject::Cursor(int track, dword keyflags) const { if(!((track & TRACK_MASK) & ~TRACK_RECT_SIDES)) { switch(track & TRACK_RECT_SIDES) { case TRACK_RECT_LEFT: case TRACK_RECT_RIGHT: return Image::SizeHorz(); case TRACK_RECT_TOP: case TRACK_RECT_BOTTOM: return Image::SizeVert(); case TRACK_RECT_LEFT | TRACK_RECT_TOP: return Image::SizeTopLeft(); case TRACK_RECT_RIGHT | TRACK_RECT_BOTTOM: return Image::SizeBottomRight(); case TRACK_RECT_LEFT | TRACK_RECT_BOTTOM: return Image::SizeBottomLeft(); case TRACK_RECT_RIGHT | TRACK_RECT_TOP: return Image::SizeTopRight(); } } switch(track & TRACK_MASK) { case TRACK_SELECT: return CtrlImg::HandCursor(); case TRACK_MOVE: return VecImg::SizeAll(); case TRACK_POINT: return VecImg::track_point(); } return Null; } Value VectorObject::GetLink(Point pt, Size sz) const { const VectorInfo& vi = Info(); int l = vi.FindArg("link"); if(l >= 0) return vi[l].Get(this); return Null; } String VectorObject::ToString() const { String out; const VectorInfo& vi = Info(); out << vi.GetType() << ", "; if(const VectorGeomArg *ra = vi.RectArgs()) { int n = ra->GetCount(this); if(n) { out << ".SetRect("; bool sc = ra->CanSetCount(); if(sc) out << "Vector()"; for(int i = 0; i < n; i++) { Rect rc = ra->Get(this, i); out << NFormat("%s`Rect(%d, %d, %d, %d)", sc ? " << " : i ? ", " : "", rc.left, rc.top, rc.right, rc.bottom); } out << ')'; } } if(const VectorGeomArg *pa = vi.PointArgs()) { int n = pa->GetCount(this); if(n) { out << ".SetPoint("; bool sc = pa->CanSetCount(); if(sc) out << "Vector()"; for(int i = 0; i < n; i++) { Point pt = pa->Get(this, i); out << NFormat("%s`Point(%d, %d)", sc ? " << " : i ? ", " : "", pt.x, pt.y); } out << ")"; } } int npr = 0; for(int i = vi.GetCount(); --i >= 0;) { const VectorArg& viarg = vi[i]; Value v = viarg.Get(this); bool usearg = false; if(viarg.dflt != v) { String fmtarg; if(IsNull(v)) fmtarg = "Null"; else if(v.GetType() == COLOR_V) { Color c(v); fmtarg = NFormat("Color(%d, %d, %d)", c.GetR(), c.GetG(), c.GetB()); } else if(v.GetType() == STRING_V || v.GetType() == WSTRING_V) fmtarg = AsCString(String(v)); else if(v.GetType() == FONT_V) fmtarg = FormatVectorFont(v); else fmtarg = StdFormat(v); out << '.' << viarg.name << '(' << fmtarg << ')'; } } return out; } One VectorObject::Load(CParser& parser) { String type = parser.ReadId(); parser.PassChar(','); const VectorInfo *vi = RegisteredObjects().Get(type, NULL); if(!vi) return NULL; One obj = vi->New(); for(int i = 0; i < vi->GetCount(); i++) (*vi)[i].Set(~obj, (*vi)[i].dflt); while(parser.Char('.')) { String id = parser.ReadId(); if(id == "SetPoint") { if(const VectorGeomArg *pa = vi->PointArgs()) { Vector pts; parser.PassChar('('); bool lsh = false; bool first = true; if(parser.Id("Vector")) { parser.PassChar('<'); parser.PassId("Point"); parser.PassChar('>'); parser.PassChar('('); parser.PassChar(')'); lsh = true; } while(!parser.Char(')')) { if(lsh) parser.PassChar2('<', '<'); else if(!first) parser.PassChar(','); first = false; Point pt; parser.PassId("Point"); parser.PassChar('('); pt.x = parser.ReadInt(); parser.PassChar(','); pt.y = parser.ReadInt(); parser.PassChar(')'); pts.Add(pt); } if(pts.GetCount() < pa->GetCount(~obj)) pts.SetCountR(pa->GetCount(~obj), Point(0, 0)); if(pa->CanSetCount()) pa->SetCount(~obj, pts.GetCount()); for(int i = 0, n = pa->GetCount(~obj); i < n; i++) pa->Set(~obj, i, pts[i]); } } else if(id == "SetRect") { if(const VectorGeomArg *ra = vi->RectArgs()) { Vector rcs; parser.PassChar('('); bool lsh = false; bool first = true; if(parser.Id("Vector")) { parser.PassChar('<'); parser.PassId("Rect"); parser.PassChar('>'); parser.PassChar('('); parser.PassChar(')'); lsh = true; } while(!parser.Char(')')) { if(lsh) parser.PassChar2('<', '<'); else if(!first) parser.PassChar(','); first = false; Rect rc; parser.PassId("Rect"); parser.PassChar('('); rc.left = parser.ReadInt(); parser.PassChar(','); rc.top = parser.ReadInt(); parser.PassChar(','); rc.right = parser.ReadInt(); parser.PassChar(','); rc.bottom = parser.ReadInt(); parser.PassChar(')'); rcs.Add(rc); } if(rcs.GetCount() < ra->GetCount(~obj)) rcs.SetCountR(ra->GetCount(~obj), Rect(0, 0, 0, 0)); if(ra->CanSetCount()) ra->SetCount(~obj, rcs.GetCount()); for(int i = 0, n = ra->GetCount(~obj); i < n; i++) ra->Set(~obj, i, rcs[i]); } } else { int t = vi->FindArg(id); parser.PassChar('('); if(t >= 0) { const VectorArg& viarg = (*vi)[t]; Value value; if(!parser.Id("Null")) switch(viarg.vtype) { case INT_V: { value = parser.ReadInt(); break; } case DOUBLE_V: { value = parser.ReadDouble(); break; } case STRING_V: case WSTRING_V: { value = parser.ReadString(); break; } case COLOR_V: { Color c = Null; parser.PassId("Color"); parser.PassChar('('); if(!parser.IsChar(')')) { int r = parser.ReadInt(); parser.PassChar(','); int g = parser.ReadInt(); parser.PassChar(','); int b = parser.ReadInt(); c = Color(r, g, b); } parser.PassChar(')'); value = c; break; } case FONT_V: { value = ParseVectorFont(parser); break; } default: NEVER(); } viarg.Set(~obj, value); } parser.PassChar(')'); } } return obj; } typedef VectorMap VectorEntryMap; GLOBAL_VAR(VectorEntryMap, VectorObject::RegisteredObjects) GLOBAL_VAR(VectorEntryMap, VectorObject::RegisteredVMIndex) void VectorObject::RegisterInfo(const VectorInfo& vi) { RegisteredObjects().GetAdd(vi.GetType()) = &vi; RegisteredVMIndex().GetAdd(vi.GetVMType()) = &vi; } VectorImage::VectorImage() : data(new Data) { } void VectorImage::Clone() { VectorImage old_image = *this; data = new Data; data->size = old_image.data->size; data->name = old_image.data->name; Reserve(old_image.GetCount()); for(int i = 0; i < old_image.GetCount(); i++) Add(old_image[i].Copy()); } VectorImage::VectorImage(const Value& value) { if(IsTypeRaw(value)) *this = ValueTo(value); else if(IsNull(value)) data = new Data; else { NEVER(); } } void VectorImage::Paint(ScalingDraw& draw) const { for(int i = 0; i < data->objects.GetCount(); i++) data->objects[i].Paint(draw); } void VectorImage::DesignPaint(ScalingDraw& draw) const { for(int i = 0; i < data->objects.GetCount(); i++) data->objects[i].DesignPaint(draw); } void VectorImage::DragPaint(ScalingDraw& draw) const { for(int i = 0; i < data->objects.GetCount(); i++) data->objects[i].DragPaint(draw); } VectorMap< int, Index > VectorImage::Contains(const Scaling& scaling, Rect rc) const { VectorMap< int, Index > list; for(int pass = 0; list.IsEmpty() && pass <= 1; pass++) for(int i = 0; i < data->objects.GetCount(); i++) if(pass || rc.Contains(data->objects[i].GetBox() * scaling)) { Index sel = data->objects[i].Contains(scaling, rc); if(!sel.IsEmpty()) list.Add(i) = sel; } return list; } Point VectorImage::Nearest(const Scaling& scaling, Point pt, int maxdistsquared) const { int nobj = -1, ntrack = -1; int mx = 1000000000; for(int i = 0; i < data->objects.GetCount(); i++) { int sel = data->objects[i].Nearest(scaling, pt, mx); if(sel >= 0) { nobj = i; ntrack = sel; } } if(mx > maxdistsquared) nobj = ntrack = -1; return Point(nobj, ntrack); } Point VectorImage::Nearest(const Scaling& scaling, Point pt, int maxdistsquared, const Vector& selection) const { int nobj = -1, ntrack = -1; int mx = 1000000000; for(int i = 0; i < selection.GetCount(); i++) { int sel = data->objects[selection[i]].Nearest(scaling, pt, mx); if(sel >= 0) { nobj = selection[i]; ntrack = sel; } } if(mx > maxdistsquared) nobj = ntrack = -1; return Point(nobj, ntrack); } Point VectorImage::TrackPoint(Point track) const { if(track.x < 0 || track.x >= data->objects.GetCount()) return Null; return data->objects[track.x].TrackPoint(track.y); } Image VectorImage::Cursor(Point track, dword keyflags) const { if(track.y < 0) return Null; if(track.x >= 0) return data->objects[track.x].Cursor(track.y, keyflags); switch((track.y & VectorObject::TRACK_MASK)) { case VectorObject::TRACK_IMAGE_SIZE: { switch(track.y >> VectorObject::INDEX_SHIFT) { case 1: return Image::SizeHorz(); case 2: return Image::SizeVert(); case 3: return Image::SizeBottomRight(); } break; } } return Null; } /* Point VectorImage::FindNearest(Point pt, int maxdistsquared, const VectorMap& selection) const { int best = -1, sel = 0, dist = 1000000000; for(int i = 0; i < selection.GetCount(); i++) { int x = selection.GetKey(i); int temp = dist; int osel = data->objects[x].NearestMask(pt, temp) & selection[i]; if(osel && temp <= dist) { best = x; sel = osel; dist = temp; } } if(dist > maxdistsquared) return Point(-1, 0); return Point(best, sel); } */ String VectorImage::SaveVec(int index) const { String out; String fn = data->name; if(IsNull(fn)) fn = NFormat("vi__%d", index); out << NFormat("VECTOR_BEGIN(%s, %d, %d)\n", fn, data->size.cx, data->size.cy); for(int i = 0; i < data->objects.GetCount(); i++) out << "\tVECTOR_OBJ(" << data->objects[i] << ")\n"; out << "VECTOR_END\n"; return out; } void VectorImage::LoadVec(CParser& parser) { Clear(); parser.PassId("VECTOR_BEGIN"); parser.PassChar('('); data->name = parser.ReadId(); parser.PassChar(','); data->size.cx = parser.ReadInt(); parser.PassChar(','); data->size.cy = parser.ReadInt(); parser.PassChar(')'); while(parser.Id("VECTOR_OBJ")) { parser.PassChar('('); One object = VectorObject::Load(parser); parser.PassChar(')'); if(object) Add(object); } parser.PassId("VECTOR_END"); } void VectorImage::SaveVM(Stream& out) const { out.Put("VIMa"); out.Put("n"); String n = data->name; VectorSerialize(out, n); out.Put("s"); Size s = data->size; VectorSerialize(out, s); out.Put("o"); int count = data->objects.GetCount(); VectorSerializePacked(out, count); for(int i = 0; i < data->objects.GetCount(); i++) { const VectorObject& obj = data->objects[i]; const VectorInfo& info = obj.Info(); StringStream data; data.Put(info.vm_type); if(const VectorGeomArg *ra = info.RectArgs()) { int count = ra->GetCount(&obj); if(ra->CanSetCount()) VectorSerializePacked(data, count); for(int i = 0; i < count; i++) { Rect rc = ra->Get(&obj, i); VectorSerialize(data, rc); } } if(const VectorGeomArg *pa = info.PointArgs()) { int count = pa->GetCount(&obj); if(pa->CanSetCount()) VectorSerializePacked(data, count); for(int i = 0; i < count; i++) { Point pt = pa->Get(&obj, i); VectorSerialize(data, pt); } } for(int a = 0; a < info.GetCount(); a++) { const VectorArg& arg = info[a]; Value v = arg.Get(&obj); if(v != arg.dflt) { data.Put(arg.vm_name); VectorSerialize(data, v, arg.vtype); } } String res = data.GetResult(); VectorSerialize(out, res); } out.Put("."); } static String VMLoadTag(Stream& in) { String out; for(;;) { int i = in.Get(); if(i < 0) break; out.Cat(i); if(i < 'A' || i > 'Z') break; } return out; } void VectorImage::LoadVM(Stream& in) { Clear(); char tag[4]; if(!in.GetAll(tag, 4) || memcmp(tag, "VIMa", 4)) { in.SetError(); return; } for(;;) { int i = in.Get(); if(i < 0 || i == '.') return; if(i == 'n') VectorSerialize(in, data->name); else if(i == 's') VectorSerialize(in, data->size); else if(i == 'o') { int c; VectorSerializePacked(in, c); Reserve(c); for(int i = 0; i < c; i++) { String odata; VectorSerialize(in, odata); StringStream indata(odata); String argtag = VMLoadTag(indata); if(const VectorInfo *info = VectorObject::RegisteredVMIndex().Get(argtag, NULL)) { One object = info->New(); if(const VectorGeomArg *ra = info->RectArgs()) { int count = ra->GetCount(~object); if(ra->CanSetCount()) { VectorSerializePacked(indata, count); if(count >= 0 && count <= 100000) ra->SetCount(~object, count); else indata.SetError(); } int i = 0; while(i < count) { Rect rc; VectorSerialize(indata, rc); if(indata.IsError()) break; ra->Set(~object, i++, rc); } while(i < count) ra->Set(~object, i++, Rect(0, 0, 0, 0)); } if(const VectorGeomArg *pa = info->PointArgs()) { int count = pa->GetCount(~object); if(pa->CanSetCount()) { VectorSerializePacked(indata, count); if(count >= 0 && count <= 100000) pa->SetCount(~object, count); else indata.SetError(); } int i = 0; while(i < count) { Point pt; VectorSerialize(indata, pt); if(indata.IsError()) break; pa->Set(~object, i++, pt); } while(i < count) pa->Set(~object, i++, Point(0, 0)); } for(int a = 0; a < info->GetCount(); a++) { const VectorArg& arg = (*info)[a]; arg.Set(~object, arg.dflt); } while(!indata.IsEof() && !indata.IsError()) { argtag = VMLoadTag(indata); int f = info->FindVMArg(argtag); if(f >= 0) { Value v; VectorSerialize(indata, v, (*info)[f].vtype); (*info)[f].Set(~object, v); } else indata.SetError(); } if(!indata.IsError()) Add(object); } } } } } void VectorImage::SerializeVM(Stream& stream) { if(stream.IsStoring()) SaveVM(stream); else LoadVM(stream); } Value VectorImage::GetLink(Point pt, Size sz) const { if(sz.cx > 1 && sz.cy > 1) { VectorMap< int, Index > cont = Contains(Scaling(), Rect(pt, sz)); for(int i = 0; i < cont.GetCount(); i++) { Value v = data->objects[cont.GetKey(i)].GetLink(pt, sz); if(!IsNull(v)) return v; } } return Value(); } void VectorLineData::PaintCap(ScalingDraw& draw, int cap, int size, Color c, Point from, Point to) const { Size dir = to - from; double len = Length(dir); if(!len) return; switch(cap) { case CAP_ARROW: case CAP_THIN_ARROW: { Point shift = from + iscale(dir, size, fceil(len)); Size dif = iscale(Size(dir.cy, -dir.cx), size >> (cap == CAP_ARROW ? 1 : 2), fceil(len)); Point poly[3] = { from, shift + dif, shift - dif }; draw.draw->DrawPolygon(poly, 3, c, Null, Null); break; } case CAP_CIRCLE: { int r = size >> 1; Rect rc(from + iscale(dir, r, fround(Length(dir))), Size(1, 1)); rc.Inflate(r); draw.draw->DrawEllipse(rc, c, Null, Null); break; } case CAP_END_CIRCLE: { Rect rc(from, Size(1, 1)); rc.Inflate(size >> 1, size >> 1); draw.draw->DrawEllipse(rc, c, Null, Null); break; } } } Point VectorLineData::ShrinkCap(ScalingDraw& draw, int cap, int size, Point from, Point to) const { double len = Length(from - to); if(!len) return from; switch(cap) { case CAP_CIRCLE: { return from + iscale(to - from, size, fceil(2 * len)); } case CAP_ARROW: case CAP_THIN_ARROW: { return from + iscale(to - from, size, fceil(len)); } } return from; } void VectorLineData::Paint(ScalingDraw& draw) const { if(points.GetCount() < 2) return; Vector out; out.SetCount(points.GetCount()); for(int i = 0; i < points.GetCount(); i++) out[i] = points[i] * draw; Point from = out[0], to = out.Top(); int cfrom = draw.X(begin_size), cto = draw.X(end_size); out[0] = ShrinkCap(draw, begin_cap, cfrom, from, out[1]); out.Top() = ShrinkCap(draw, end_cap, cto, to, out[out.GetCount() - 2]); if(out.GetCount() == 2) draw.draw->DrawLine(out[0], out[1], line_width, line_color); else draw.draw->DrawPolyline(out, line_width, line_color); if(begin_cap) PaintCap(draw, begin_cap, cfrom, begin_color, from, out[1]); if(end_cap) PaintCap(draw, end_cap, cto, end_color, to, out[out.GetCount() - 2]); } class VectorLineCapDisplayCls : public Display { public: virtual void Paint(Draw& draw, const Rect& rc, const Value& v, Color ink, Color paper, dword style) const; }; void VectorLineCapDisplayCls::Paint(Draw& draw, const Rect& rc, const Value& v, Color ink, Color paper, dword style) const { draw.DrawRect(rc, paper); Size size = rc.Size(); int sz = min(size.cx, size.cy); Point p1(size.cy >> 1, size.cy >> 1); Point p2(size.cx - (size.cy >> 1), size.cy >> 1); draw.Clipoff(rc); ScalingDraw sdraw(&draw); VectorLineData data; data.SetPoint(Vector() << p1 << p2).LineWidth(2).LineColor(ink) .BeginCap(v).BeginSize(sz).BeginColor(ink).Paint(sdraw); draw.End(); } const Display& VectorLineCapDisplay() { static VectorLineCapDisplayCls d; return d; } const VectorInfo& VectorLine::ClassInfo() { static VectorInfo i = VectorInfoFor( "VectorLine", "l", NULL, VectorGeomArgDef(&VectorLine::points), Array() << VectorArgDef(&VectorLine::line_color, "LineColor", "l", Black()) << VectorArgDef(&VectorLine::line_width, "LineWidth", "w", 0) << VectorArgDef(&VectorLine::begin_cap, "BeginCap", "b", CAP_NONE, VectorArg::EDIT_LINE_CAP) << VectorArgDef(&VectorLine::begin_size, "BeginSize", "s", 10) << VectorArgDef(&VectorLine::begin_color, "BeginColor", "m", Black()) << VectorArgDef(&VectorLine::end_cap, "EndCap", "e", CAP_NONE, VectorArg::EDIT_LINE_CAP) << VectorArgDef(&VectorLine::end_size, "EndSize", "t", 10) << VectorArgDef(&VectorLine::end_color, "EndColor", "n", Black()) << VectorArgDef(&VectorLine::link, "Link", "z", Null) ).DefaultName("Line") .Icon(VecImg::line_object); return i; } void VectorLine::Track(int track, Point start, Point end, dword keyflags) { switch(track & TRACK_MASK) { case TRACK_LINE_INSERT: { int i = minmax(track >> INDEX_SHIFT, 0, points.GetCount()); points.Insert(i, end); break; } case TRACK_LINE_REMOVE: { int i = minmax(track >> INDEX_SHIFT, 0, points.GetCount() - 1); if(points.GetCount() > 2 && i >= 0) points.Remove(i); break; } default: VectorObject::Track(track, start, end, keyflags); } } void VectorLine::DragPaint(ScalingDraw& draw) const { if(points.GetCount() < 2) return; Vector out; out.SetCount(points.GetCount()); for(int i = 0; i < points.GetCount(); i++) out[i] = points[i] * draw; if(out.GetCount() == 2) draw.draw->DrawLine(out[0], out[1], 0, Black()); else draw.draw->DrawPolyline(out, 0, Black()); } int VectorLine::Nearest(const Scaling& scaling, Point pt, int& maxdistsquared) const { int s2, track = -1, md = 10000; for(int i = 0; i < points.GetCount(); i++) if((s2 = Squared(pt - points[i] * scaling)) < tabs(maxdistsquared)) { maxdistsquared = s2; md = s2; track = (i << INDEX_SHIFT) | TRACK_POINT | ARG_MASK; } if(md <= 100) return track; for(int i = 1; i < points.GetCount(); i++) if((s2 = GetLineDistanceSquared(points[i - 1] * scaling, points[i] * scaling, pt) + 10) < tabs(maxdistsquared)) { maxdistsquared = s2; track = TRACK_MOVE; } for(int i = 0; i <= points.GetCount(); i++) { Point addpt, rmpt; GetAddRmPoint(scaling, i, addpt, rmpt); int s2; if(!IsNull(addpt) && (s2 = Squared(addpt - pt)) < tabs(maxdistsquared)) { maxdistsquared = s2; track = (i << INDEX_SHIFT) | TRACK_LINE_INSERT; } if(!IsNull(rmpt) && (s2 = Squared(rmpt - pt)) < tabs(maxdistsquared)) { maxdistsquared = s2; track = (i << INDEX_SHIFT) | TRACK_LINE_REMOVE; } } return track; } Image VectorLine::Cursor(int track, dword keyflags) const { switch(track & TRACK_MASK) { case TRACK_LINE_INSERT: return VecImg::line_insert_cursor(); case TRACK_LINE_REMOVE: return VecImg::line_remove_cursor(); default: return VectorObject::Cursor(track, keyflags); } } void VectorLine::Create(Point from, Point to, dword keyflags) { points.SetCount(2); points[0] = from; points[1] = to; } static void PaintAddBox(Draw& draw, Point pt, bool is_hot) { Color c = (is_hot ? Color(0, 192, 0) : Green()); draw.DrawRect(pt.x - 4, pt.y - 4, 9, 1, c); draw.DrawRect(pt.x - 4, pt.y - 4, 1, 9, c); draw.DrawRect(pt.x - 4, pt.y + 4, 9, 1, c); draw.DrawRect(pt.x + 4, pt.y - 4, 1, 9, c); draw.DrawRect(pt.x - 2, pt.y, 5, 1, c); draw.DrawRect(pt.x, pt.y - 2, 1, 5, c); } static void PaintRemoveBox(Draw& draw, Point pt, bool is_hot) { Color c = (is_hot ? Color(192, 0, 0) : Red()); draw.DrawRect(pt.x - 4, pt.y - 4, 9, 1, c); draw.DrawRect(pt.x - 4, pt.y - 4, 1, 9, c); draw.DrawRect(pt.x - 4, pt.y + 4, 9, 1, c); draw.DrawRect(pt.x + 4, pt.y - 4, 1, 9, c); draw.DrawRect(pt.x - 2, pt.y, 5, 1, c); } Size VectorLine::GetOutVector(const Scaling& scaling, int i, int factor) const { if(points.GetCount() <= 1) return Size(factor, 0); Size invec; if(i == 0) invec = points[i + 1] - points[i]; else if(i >= points.GetCount() - 1) invec = points[points.GetCount() - 2] - points.Top(); else invec = ScaleSize(points[i + 1] - points[i], 1000) + ScaleSize(points[i - 1] - points[i], 1000); return ScaleSize(-invec * scaling, factor); } void VectorLine::SelPaint(ScalingDraw& draw, const Index& sel, bool is_hot) const { VectorObject::SelPaint(draw, sel, is_hot); for(int i = 0; i <= points.GetCount(); i++) { Point addpt, rmpt; GetAddRmPoint(draw, i, addpt, rmpt); PaintAddBox(*draw.draw, addpt, is_hot); if(!IsNull(rmpt)) PaintRemoveBox(*draw.draw, rmpt, is_hot); } } void VectorLine::GetAddRmPoint(const Scaling& scaling, int i, Point& addpt, Point& rmpt) const { int fac = 12 + (scaling.X(line_width) >> 1); addpt = rmpt = Null; switch(points.GetCount()) { case 0: return; case 1: addpt = points[0] * scaling + Size(fac, 0); return; } Size outvec = GetOutVector(scaling, i, fac); Size side = (points.GetCount() <= 2 ? Size(0, 0) : ScaleSize(Size(outvec.cy, -outvec.cx), 8)); Point pt = points[min(i, points.GetCount() - 1)] * scaling; if(i == 0) { addpt = pt + outvec + side; if(points.GetCount() > 2) rmpt = pt + outvec - side; } else if(i < points.GetCount()) { Size dir = pt - points[i - 1] * scaling; addpt = ((points[i - 1] * scaling + pt) >> 1) + ScaleSize(Size(dir.cy, -dir.cx), fac); if(points.GetCount() > 2) { rmpt = pt + outvec; if(i + 1 >= points.GetCount()) rmpt -= side; } } else addpt = pt + outvec + side; } Vector VectorRectData::GetPoly(ScalingDraw& draw, int lw) const { Size rdelta(draw.X(rounding), draw.Y(rounding)); int newside = minmax(max(rdelta.cx, rdelta.cy) >> 1, 0, 50); Vector out; Rect rc = rect * draw; int w1 = lw >> 1, w2 = (lw + 1) >> 1; rc.Deflate(w1, w1, w2, w2); if(newside <= 0) { out.SetCount(4); out[0] = rc.TopRight(); out[1] = rc.TopLeft(); out[2] = rc.BottomLeft(); out[3] = rc.BottomRight(); } else { out.SetCount(4 * (newside + 1)); Point *d0 = out.Begin(), *d1 = d0 + 2 * (newside + 1), *d2 = d1, *d3 = out.End(); Rect inner = rc.Deflated(rdelta); *d0++ = Point(rc.right, inner.top); *--d1 = Point(rc.left, inner.top); *d2++ = Point(rc.left, inner.bottom); *--d3 = Point(rc.right, inner.bottom); for(int i = 1; i < newside; i++) { double a = i * (M_PI / 2) / newside; int c = fround(rdelta.cx * cos(a)), s = fround(rdelta.cy * sin(a)); *d0++ = Point(inner.right + c, inner.top - s); *--d1 = Point(inner.left - c, inner.top - s); *d2++ = Point(inner.left - c, inner.bottom + s); *--d3 = Point(inner.right + c, inner.bottom + s); } *d0++ = Point(inner.right, rc.top); *--d1 = Point(inner.left, rc.top); *d2++ = Point(inner.left, rc.bottom); *--d3 = Point(inner.right, rc.bottom); ASSERT(d0 == d1 && d2 == d3); } return out; } void VectorRectData::Paint(ScalingDraw& draw) const { Rect drc = rect * draw, irc = drc; switch(style) { case STYLE_RECT: { if(!IsNull(outline_color)) { Size frmsize(max(draw.X(outline_width), 1), max(draw.Y(outline_width), 1)); irc.Deflate(frmsize); DrawRectMinusRect(*draw.draw, drc, irc, outline_color); } draw.draw->DrawRect(irc, fill_color); break; } case STYLE_ROUNDED_RECT: { draw.draw->DrawPolygon(GetPoly(draw, outline_width), fill_color, outline_width, outline_color); break; } case STYLE_ELLIPSE: { int w2 = outline_width >> 1, w1 = (outline_width - 1) >> 1; draw.draw->DrawEllipse(drc.Deflated(w1), fill_color, outline_width, outline_color); break; } } int ow = IsNull(outline_color) ? 0 : outline_width; drc.Deflate(draw.X(ow + hmargin), draw.Y(ow + vmargin)); Font font = GetScaledFont(draw); Size sz = GetTextSize(text, font); int x, y; switch(halign) { case ALIGN_LEFT: x = drc.left; break; default: x = (drc.left + drc.right - sz.cx) >> 1; break; case ALIGN_RIGHT: x = drc.right - sz.cx; break; } switch(valign) { case ALIGN_TOP: y = drc.top; break; default: y = (drc.top + drc.bottom - sz.cy) >> 1; break; case ALIGN_BOTTOM: y = drc.bottom - sz.cy; break; } draw.draw->DrawText(x, y, text, font, text_color); } Font VectorRectData::GetScaledFont(const ScalingDraw& draw) const { int newht = draw.Y(font.GetHeight()); int newwd = draw.X(font.Info().GetAveWidth()); return font().Height(newht ? newht : 1).Width(newwd); } void VectorRect::DragPaint(ScalingDraw& draw) const { switch(style) { case STYLE_RECT: { DrawLineRect(*draw.draw, rect * draw); break; } case STYLE_ROUNDED_RECT: { draw.draw->DrawPolygon(GetPoly(draw, 0), Null, 0, Black()); break; } case STYLE_ELLIPSE: { Rect rc = rect * draw; draw.draw->DrawEllipse(rc, Null, 0, Black()); DrawLineRect(*draw.draw, rc, PEN_DOT); break; } } } /* int VectorRect::Nearest(Point pt, int& maxdistsquared) const { if(style != STYLE_ELLIPSE) return VectorObject::Nearest(pt, maxdistsquared); int s2 = GetEllipseDistanceSquared(rect, pt); if(tabs(s2) <= tabs(maxdistsquared)) { maxdistsquared = s2; return (0 << INDEX_SHIFT) | TRACK_RECT | RECT_MASK | ARG_MASK; } return -1; } */ const VectorInfo& VectorRect::ClassInfo() { static VectorInfo i = VectorInfoFor( "VectorRect", "r", VectorGeomArgDef(&VectorRect::rect), NULL, Array() << VectorArgDef(&VectorRect::text_color, "TextColor", "x", Black()) << VectorArgDef(&VectorRect::fill_color, "FillColor", "c", White()) << VectorArgDef(&VectorRect::outline_color, "OutlineColor", "o", Black()) << VectorArgDef(&VectorRect::outline_width, "OutlineWidth", "u", 0) << VectorArgDef(&VectorRect::rounding, "Rounding", "r", 10) << VectorArgDef(&VectorRect::style, "Style", "s", STYLE_RECT, VectorArg::EDIT_RECT_STYLE) << VectorArgDef(&VectorRect::font, "SetFont", "f", Arial(20)) << VectorArgDef(&VectorRect::text, "Text", "t", Null) << VectorArgDef(&VectorRect::halign, "AlignHorz", "h", ALIGN_CENTER, VectorArg::EDIT_ALIGN_HORZ) << VectorArgDef(&VectorRect::valign, "AlignVert", "v", ALIGN_CENTER, VectorArg::EDIT_ALIGN_VERT) << VectorArgDef(&VectorRect::hmargin, "MarginHorz", "m", 0) << VectorArgDef(&VectorRect::vmargin, "MarginVert", "n", 0) << VectorArgDef(&VectorRect::link, "Link", "z", Null) ).DefaultName("Rectangular") .Icon(VecImg::rect_object); return i; } void VectorArcData::Paint(ScalingDraw& draw) const { Rect rlim = rect * draw; rlim.Deflate(line_width >> 1); draw.draw->DrawArc(rlim, from * draw, to * draw, line_width, line_color); } void VectorArc::DesignPaint(ScalingDraw& draw) const { VectorArcData::Paint(draw); Point center = (rect * draw).CenterPoint(); draw.draw->DrawLine(center, from * draw, PEN_DOT); draw.draw->DrawLine(center, to * draw, PEN_DOT); } void VectorArc::DragPaint(ScalingDraw& draw) const { Rect rlim = rect * draw; draw.draw->DrawArc(rlim, from * draw, to * draw, 0, Black()); Point center = rlim.CenterPoint(); draw.draw->DrawLine(center, from * draw, PEN_DOT); draw.draw->DrawLine(center, to * draw, PEN_DOT); DrawLineRect(*draw.draw, rlim, PEN_DOT); } int VectorArc::Nearest(const Scaling& scaling, Point pt, int& maxdistsquared) const { int s2, track = -1; if((s2 = GetEllipseDistanceSquared(rect * scaling, pt)) < tabs(maxdistsquared)) { maxdistsquared = s2; track = (0 << INDEX_SHIFT) | TRACK_RECT | ARG_MASK | RECT_MASK; } if((s2 = Squared(pt - from * scaling)) < tabs(maxdistsquared)) { maxdistsquared = s2; track = (0 << INDEX_SHIFT) | TRACK_POINT | ARG_MASK; } if((s2 = Squared(pt - to * scaling)) < tabs(maxdistsquared)) { maxdistsquared = s2; track = (1 << INDEX_SHIFT) | TRACK_POINT | ARG_MASK; } if(track >= 0) return track; Point c = rect.CenterPoint(); if((s2 = GetLineDistanceSquared(c, from * scaling, pt)) < tabs(maxdistsquared)) { maxdistsquared = s2; track = (0 << INDEX_SHIFT) | TRACK_POINT | ARG_MASK; } if((s2 = GetLineDistanceSquared(c, to * scaling, pt)) < tabs(maxdistsquared)) { maxdistsquared = s2; track = (1 << INDEX_SHIFT) | TRACK_POINT | ARG_MASK; } return track; } const VectorInfo& VectorArc::ClassInfo() { static VectorInfo i = VectorInfoFor( "VectorArc", "a", VectorGeomArgDef(&VectorArc::rect), VectorGeomArgDef(&VectorArc::from, &VectorArc::to), Array() << VectorArgDef(&VectorArc::line_color, "LineColor", "l", Black()) << VectorArgDef(&VectorArc::line_width, "LineWidth", "w", 0) << VectorArgDef(&VectorArc::link, "Link", "z", Null) ).DefaultName("Arc") .Icon(VecImg::arc_object); return i; }