ultimatepp/uppsrc/VectorDes/VectorObj.cpp
mdelfede d2b54f7989 changed svn layout
git-svn-id: svn://ultimatepp.org/upp/trunk@281 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2008-06-07 22:31:27 +00:00

1913 lines
52 KiB
C++

#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<byte> 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<Ctrl> 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> 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> 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> 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> 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<VectorObject> (*newfunc)(),
One< VectorGeomArg<Rect> > rect_args_, One< VectorGeomArg<Point> > point_args_, Array<VectorArg> 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<Rect> *ra = info.RectArgs()) {
for(int i = 0, n = ra->GetCount(this); i < n; i++)
out << (out.IsEmpty() ? "" : "; ") << ra->Get(this, i);
}
if(const VectorGeomArg<Point> *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<int>& sel, bool is_hot) const
{
const VectorInfo& vi = Info();
Color c = (is_hot ? LtMagenta() : LtBlue());
Vector<bool> 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<Rect> *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<Point> *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<Rect> *ra = vi.RectArgs())
for(int i = 0; i < ra->GetCount(this); i++)
box |= ra->Get(this, i);
if(const VectorGeomArg<Point> *pa = vi.PointArgs())
for(int i = 0; i < pa->GetCount(this); i++)
box |= pa->Get(this, i);
return box;
}
One<VectorObject> VectorObject::Copy() const
{
const VectorInfo& vi = Info();
One<VectorObject> newobj = vi.New();
if(const VectorGeomArg<Rect> *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<Point> *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<int> VectorObject::Contains(const Scaling& scaling, Rect rc) const
{
const VectorInfo& vi = Info();
Index<int> out;
if(const VectorGeomArg<Rect> *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<Point> *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<Rect> *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<Point> *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<Rect> *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<Point> *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<Rect> *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<Point> *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<Rect> *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<Point> *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<Rect> * 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<Rect> *ra = vi.RectArgs()) {
int n = ra->GetCount(this);
if(n) {
out << ".SetRect(";
bool sc = ra->CanSetCount();
if(sc)
out << "Vector<Rect>()";
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<Point> *pa = vi.PointArgs()) {
int n = pa->GetCount(this);
if(n) {
out << ".SetPoint(";
bool sc = pa->CanSetCount();
if(sc)
out << "Vector<Point>()";
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> VectorObject::Load(CParser& parser)
{
String type = parser.ReadId();
parser.PassChar(',');
const VectorInfo *vi = RegisteredObjects().Get(type, NULL);
if(!vi)
return NULL;
One<VectorObject> 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<Point> *pa = vi->PointArgs()) {
Vector<Point> 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<Rect> *ra = vi->RectArgs()) {
Vector<Rect> 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<String, const VectorInfo *> 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<VectorImage>(value))
*this = ValueTo<VectorImage>(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<int> > VectorImage::Contains(const Scaling& scaling, Rect rc) const
{
VectorMap< int, Index<int> > 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<int> 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<int>& 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<int, int>& 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<VectorObject> 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<Rect> *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<Point> *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<VectorObject> object = info->New();
if(const VectorGeomArg<Rect> *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<Point> *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<int> > 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<Point> 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<Point>() << 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>(
"VectorLine", "l",
NULL,
VectorGeomArgDef(&VectorLine::points),
Array<VectorArg>()
<< 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<Point> 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<int>& 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<Point> VectorRectData::GetPoly(ScalingDraw& draw, int lw) const
{
Size rdelta(draw.X(rounding), draw.Y(rounding));
int newside = minmax<int>(max(rdelta.cx, rdelta.cy) >> 1, 0, 50);
Vector<Point> 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>(
"VectorRect", "r",
VectorGeomArgDef(&VectorRect::rect),
NULL,
Array<VectorArg>()
<< 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>(
"VectorArc", "a",
VectorGeomArgDef(&VectorArc::rect),
VectorGeomArgDef(&VectorArc::from, &VectorArc::to),
Array<VectorArg>()
<< 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;
}