Added new tech tree layout implementation using Microsoft Automatic Graph Layouts Added some functionality to the database editor tools
215 lines
No EOL
8 KiB
C#
215 lines
No EOL
8 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
|
|
namespace Microsoft.Msagl.Layout.OverlapRemovalFixedSegments {
|
|
internal class FakeBitmap {
|
|
readonly int _width;
|
|
readonly int _height;
|
|
readonly byte[] _m;
|
|
|
|
public FakeBitmap(int width, int height) {
|
|
_width = width;
|
|
_height = height;
|
|
int size = _width*_height;
|
|
_m = new byte[size];
|
|
}
|
|
|
|
|
|
public byte GetPixelValue(int x, int y) {
|
|
if (!InBounds(x, y))
|
|
return 0;
|
|
|
|
return _m[x + y*_width];
|
|
}
|
|
|
|
int Offset(int x, int y) {
|
|
Debug.Assert(InBounds(x, y));
|
|
return x + _width*y;
|
|
}
|
|
|
|
bool InBounds(int x, int y) {
|
|
return 0 <= x && x < _width &&
|
|
0 <= y && y < _height;
|
|
}
|
|
|
|
public void FillRectangle(int left, int bottom, int w, int h) {
|
|
AdjustLeftTop(ref left, ref bottom);
|
|
int top = Math.Min(bottom + h, _height - 1);
|
|
int right = Math.Min(left + w, _width - 1);
|
|
for (int y = bottom; y < top; y++) {
|
|
int offset = Offset(left, y);
|
|
for (int x = left; x < right; x++) {
|
|
_m[offset++] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdjustLeftTop(ref int left, ref int bottom) {
|
|
if (left < 0)
|
|
left = 0;
|
|
if (bottom < 0)
|
|
bottom = 0;
|
|
if (left >= _width)
|
|
left = _width - 1;
|
|
if (bottom >= _height)
|
|
bottom = _height - 1;
|
|
|
|
}
|
|
|
|
void Plot(int x, int y) {
|
|
_m[Offset(x, y)] = 1;
|
|
}
|
|
|
|
internal void DrawFatSegByX(PixelPoint a, PixelPoint b, int insertRectHalfSize) {
|
|
var perp = new PixelPoint(-(b.Y - a.Y), b.X - a.X);
|
|
if (perp.Y == 0) {
|
|
FillRectangle(a.X, a.Y - insertRectHalfSize, b.X - a.X, 2*insertRectHalfSize);
|
|
return;
|
|
}
|
|
if (perp.Y < 0) perp = -perp;
|
|
var pixelInside = a;
|
|
double perpLen = Math.Sqrt(perp*perp);
|
|
// pixel p is inside if and only if (p-a)*perp belongs to the interval [-halfInterval, halfInterval]
|
|
int halfInterval = (int) (perpLen*insertRectHalfSize);
|
|
for (int x = a.X; x <= b.X; x++)
|
|
ScanVertLine(x, ref pixelInside, a, b, perp, halfInterval);
|
|
}
|
|
|
|
void ScanVertLine(int x, ref PixelPoint pixelInside, PixelPoint a, PixelPoint b, PixelPoint perp,
|
|
int halfInterval) {
|
|
ScanVertLineUp(x, pixelInside, a, perp, halfInterval);
|
|
ScanVertLineDown(x, pixelInside, a, perp, halfInterval);
|
|
UpdatePixelInsideForXCase(ref pixelInside, a, b, perp, halfInterval, Math.Sign(b.Y - a.Y));
|
|
}
|
|
|
|
void ScanVertLineDown(int x, PixelPoint pixelInside, PixelPoint a, PixelPoint perp, int halfInterval) {
|
|
var y = pixelInside.Y;
|
|
var proj = perp*(pixelInside - a);
|
|
Debug.Assert(Math.Abs(proj) <= halfInterval);
|
|
do {
|
|
Plot(x, y);
|
|
proj -= perp.Y;
|
|
if (proj < - halfInterval) break;
|
|
y--;
|
|
} while (y >= 0);
|
|
}
|
|
|
|
void ScanVertLineUp(int x, PixelPoint pixelInside, PixelPoint a, PixelPoint perp, int halfInterval) {
|
|
Debug.Assert(perp.Y > 0);
|
|
var y = pixelInside.Y;
|
|
var proj = perp*(pixelInside - a);
|
|
Debug.Assert(Math.Abs(proj) <= halfInterval);
|
|
do {
|
|
Plot(x, y);
|
|
proj += perp.Y;
|
|
if (proj > halfInterval) break;
|
|
y++;
|
|
} while (y < _height);
|
|
|
|
}
|
|
|
|
static void UpdatePixelInsideForXCase(ref PixelPoint pixelInside, PixelPoint a, PixelPoint b, PixelPoint perp,
|
|
int halfInterval, int sign) {
|
|
if (pixelInside.X == b.X) return;
|
|
pixelInside.X++;
|
|
int proj = perp*(pixelInside - a);
|
|
if (Math.Abs(proj) <= halfInterval) return;
|
|
pixelInside.Y += sign;
|
|
proj += sign*perp.Y;
|
|
if (Math.Abs(proj) <= halfInterval) return;
|
|
|
|
pixelInside.Y -= 2*sign;
|
|
proj -= 2*sign*perp.Y;
|
|
Debug.Assert(Math.Abs(proj - 2*sign*perp.Y) <= halfInterval);
|
|
}
|
|
|
|
internal void DrawFatSegByY(PixelPoint a, PixelPoint b, int halfDistInPixels) {
|
|
Debug.Assert(a.Y <= b.Y);
|
|
var perp = new PixelPoint(-b.Y + a.Y, b.X - a.X);
|
|
if (perp.X == 0) {
|
|
FillRectangle(a.X - halfDistInPixels, a.Y, 2*halfDistInPixels, b.Y - a.Y);
|
|
return;
|
|
}
|
|
if (perp.X < 0) perp = -perp;
|
|
var pixelInside = a;
|
|
double perpLen = Math.Sqrt(perp*perp);
|
|
// pixel p is inside if and only if (p-a)*perp belongs to the interval [-halfInterval, halfInterval]
|
|
int halfInterval = (int) (perpLen*halfDistInPixels);
|
|
for (int y = a.Y; y <= b.Y; y++)
|
|
ScanHorizontalLine(y, ref pixelInside, a, b, perp, halfInterval);
|
|
}
|
|
|
|
void ScanHorizontalLine(int x, ref PixelPoint pixelInside, PixelPoint a, PixelPoint b, PixelPoint perp,
|
|
int halfInterval) {
|
|
ScanHorizontalLineRight(x, pixelInside, a, perp, halfInterval);
|
|
ScanHorizontalLineLeft(x, pixelInside, a, perp, halfInterval);
|
|
UpdatePixelInsideForYCase(ref pixelInside, a, b, perp, halfInterval, Math.Sign(b.Y - a.Y));
|
|
|
|
}
|
|
|
|
void UpdatePixelInsideForYCase(ref PixelPoint pixelInside, PixelPoint a, PixelPoint b, PixelPoint perp,
|
|
int halfInterval, int sign) {
|
|
if (pixelInside.Y == b.Y) return;
|
|
pixelInside.Y++;
|
|
int proj = perp*(pixelInside - a);
|
|
if (Math.Abs(proj) <= halfInterval) return;
|
|
pixelInside.X += sign;
|
|
proj += sign*perp.X;
|
|
if (Math.Abs(proj) <= halfInterval) return;
|
|
|
|
pixelInside.X -= 2*sign;
|
|
proj -= 2*sign*perp.X;
|
|
Debug.Assert(Math.Abs(proj) <= halfInterval);
|
|
}
|
|
|
|
void ScanHorizontalLineRight(int y, PixelPoint pixelInside, PixelPoint a, PixelPoint perp, int halfInterval) {
|
|
Debug.Assert(perp.X > 0);
|
|
var x = pixelInside.X;
|
|
var proj = perp*(pixelInside - a);
|
|
Debug.Assert(Math.Abs(proj) <= halfInterval);
|
|
do {
|
|
Plot(x, y);
|
|
proj += perp.X;
|
|
if (proj > halfInterval) break;
|
|
x++;
|
|
} while (x < _width);
|
|
}
|
|
|
|
void ScanHorizontalLineLeft(int y, PixelPoint pixelInside, PixelPoint a, PixelPoint perp, int halfInterval) {
|
|
Debug.Assert(perp.X > 0);
|
|
var x = pixelInside.X;
|
|
var proj = perp*(pixelInside - a);
|
|
Debug.Assert(Math.Abs(proj) <= halfInterval);
|
|
do {
|
|
Plot(x, y);
|
|
proj -= perp.X;
|
|
if (proj < - halfInterval) break;
|
|
x--;
|
|
} while (x >= 0);
|
|
|
|
}
|
|
|
|
internal void DrawFatSeg(PixelPoint a, PixelPoint b, int insertRectHalfSizeInPixels) {
|
|
double dx = b.X - a.X;
|
|
double dy = b.Y - a.Y;
|
|
FillRectangle(a.X - insertRectHalfSizeInPixels, a.Y - insertRectHalfSizeInPixels,
|
|
2*insertRectHalfSizeInPixels,
|
|
2*insertRectHalfSizeInPixels);
|
|
FillRectangle(b.X - insertRectHalfSizeInPixels, b.Y - insertRectHalfSizeInPixels,
|
|
2*insertRectHalfSizeInPixels,
|
|
2*insertRectHalfSizeInPixels);
|
|
if (Math.Abs(dx) > Math.Abs(dy)) {
|
|
if (dx > 0)
|
|
DrawFatSegByX(a, b, insertRectHalfSizeInPixels);
|
|
else
|
|
DrawFatSegByX(b, a, insertRectHalfSizeInPixels);
|
|
}
|
|
else {
|
|
if (dy > 0)
|
|
DrawFatSegByY(a, b, insertRectHalfSizeInPixels);
|
|
else
|
|
DrawFatSegByY(b, a, insertRectHalfSizeInPixels);
|
|
}
|
|
}
|
|
}
|
|
} |