mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-23 20:32:34 +00:00
0369c969d1
Draw Curve Mode: added settings panel. Sectors mode: added "Make Door" button to the toolbar. Swapped Side panel and Info panel z-order. Interface: split toolbar into 3 separate toolbars. All toolbar buttons are now viewable at 1024x768. Interface: grouped stuff in "Modes" menu a bit better. Interface: added "Draw [stuff]" buttons to modes toolbar. Interface: reorganized main menu. Hope it makes more sense now. API: added General.Interface.AddModesButton() and General.Interface.AddModesMenu(), which can be used to add buttons to specific group in "Modes" toolbar and menu items to specific group in "Modes" menu, so actions, which behave like an editing mode, but are not part of one can be added there.
414 lines
12 KiB
C#
414 lines
12 KiB
C#
#region ================== Namespaces
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Windows.Forms;
|
|
using CodeImp.DoomBuilder.Actions;
|
|
using CodeImp.DoomBuilder.Controls;
|
|
using CodeImp.DoomBuilder.Editing;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using CodeImp.DoomBuilder.Map;
|
|
using CodeImp.DoomBuilder.Rendering;
|
|
using CodeImp.DoomBuilder.Windows;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.BuilderModes
|
|
{
|
|
[EditMode(DisplayName = "Draw Grid Mode",
|
|
SwitchAction = "drawgridmode",
|
|
ButtonImage = "DrawGridMode.png", //mxd
|
|
ButtonOrder = int.MinValue + 5, //mxd
|
|
ButtonGroup = "000_drawing", //mxd
|
|
AllowCopyPaste = false,
|
|
Volatile = true,
|
|
Optional = false)]
|
|
|
|
public class DrawGridMode : DrawGeometryMode
|
|
{
|
|
#region ================== Variables
|
|
|
|
private static int horizontalSlices = 3;
|
|
private static int verticalSlices = 3;
|
|
private static bool triangulate;
|
|
private static bool gridlock;
|
|
|
|
private List<DrawnVertex[]> gridpoints;
|
|
private HintLabel hintLabel;
|
|
|
|
private int width;
|
|
private int height;
|
|
private int slicesH;
|
|
private int slicesV;
|
|
private int initialGridSize;
|
|
private Vector2D start;
|
|
private Vector2D end;
|
|
|
|
//interface
|
|
private Docker settingsdocker;
|
|
private DrawGridOptionsPanel panel;
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor
|
|
|
|
public DrawGridMode() {
|
|
snaptogrid = true;
|
|
|
|
//Options docker
|
|
panel = new DrawGridOptionsPanel();
|
|
panel.OnValueChanged += OptionsPanelOnValueChanged;
|
|
panel.OnGridLockChanged += OptionsPanelOnOnGridLockChanged;
|
|
settingsdocker = new Docker("drawgrid", "Draw Grid Settings", panel);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Events
|
|
|
|
public override void OnEngage() {
|
|
base.OnEngage();
|
|
General.Interface.AddDocker(settingsdocker);
|
|
General.Interface.SelectDocker(settingsdocker);
|
|
initialGridSize = General.Map.Grid.GridSize;
|
|
|
|
//setup settings panel
|
|
panel.Triangulate = triangulate;
|
|
panel.LockToGrid = gridlock;
|
|
panel.HorizontalSlices = horizontalSlices - 1;
|
|
panel.VerticalSlices = verticalSlices - 1;
|
|
}
|
|
|
|
|
|
public override void OnDisengage() {
|
|
General.Interface.RemoveDocker(settingsdocker);
|
|
base.OnDisengage();
|
|
}
|
|
|
|
override public void OnAccept() {
|
|
Cursor.Current = Cursors.AppStarting;
|
|
General.Settings.FindDefaultDrawSettings();
|
|
|
|
// When we have a shape...
|
|
if(gridpoints.Count > 0) {
|
|
// Make undo for the draw
|
|
General.Map.UndoRedo.CreateUndo("Grid draw");
|
|
|
|
// Make an analysis and show info
|
|
string[] adjectives = new[] { "gloomy", "sad", "unhappy", "lonely", "troubled", "depressed", "heartsick", "glum", "pessimistic", "bitter", "downcast" }; // aaand my english vocabulary ends here :)
|
|
string word = adjectives[new Random().Next(adjectives.Length - 1)];
|
|
string a = (word[0] == 'u' ? "an " : "a ");
|
|
|
|
General.Interface.DisplayStatus(StatusType.Action, "Created " + a + word + " grid.");
|
|
|
|
List<Sector> newsectors = new List<Sector>();
|
|
foreach (DrawnVertex[] shape in gridpoints) {
|
|
if(!Tools.DrawLines(shape, true, BuilderPlug.Me.AutoAlignTextureOffsetsOnCreate)) {
|
|
// Drawing failed
|
|
// NOTE: I have to call this twice, because the first time only cancels this volatile mode
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
General.Map.UndoRedo.WithdrawUndo();
|
|
return;
|
|
}
|
|
|
|
// Update cached values after each step...
|
|
General.Map.Map.Update();
|
|
|
|
newsectors.AddRange(General.Map.Map.GetMarkedSectors(true));
|
|
}
|
|
|
|
// Snap to map format accuracy
|
|
General.Map.Map.SnapAllToAccuracy();
|
|
|
|
// Clear selection
|
|
General.Map.Map.ClearAllSelected();
|
|
|
|
// Edit new sectors?
|
|
if(BuilderPlug.Me.EditNewSector && (newsectors.Count > 0))
|
|
General.Interface.ShowEditSectors(newsectors);
|
|
|
|
// Update the used textures
|
|
General.Map.Data.UpdateUsedTextures();
|
|
|
|
// Map is changed
|
|
General.Map.IsChanged = true;
|
|
}
|
|
|
|
// Done
|
|
Cursor.Current = Cursors.Default;
|
|
|
|
// Return to original mode
|
|
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
|
|
}
|
|
|
|
public override void OnHelp() {
|
|
General.ShowHelp("/gzdb/features/classic_modes/mode_drawgrid.html");
|
|
}
|
|
|
|
private void OptionsPanelOnValueChanged(object sender, EventArgs eventArgs) {
|
|
triangulate = panel.Triangulate;
|
|
horizontalSlices = panel.HorizontalSlices + 1;
|
|
verticalSlices = panel.VerticalSlices + 1;
|
|
Update();
|
|
}
|
|
|
|
private void OptionsPanelOnOnGridLockChanged(object sender, EventArgs eventArgs) {
|
|
gridlock = panel.LockToGrid;
|
|
Update();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Methods
|
|
|
|
override protected void Update() {
|
|
PixelColor stitchcolor = General.Colors.Highlight;
|
|
PixelColor losecolor = General.Colors.Selection;
|
|
|
|
snaptonearest = General.Interface.CtrlState ^ General.Interface.AutoMerge;
|
|
|
|
DrawnVertex curp = GetCurrentPosition();
|
|
float vsize = (renderer.VertexSize + 1.0f) / renderer.Scale;
|
|
|
|
// Render drawing lines
|
|
if(renderer.StartOverlay(true)) {
|
|
PixelColor color = snaptonearest ? stitchcolor : losecolor;
|
|
|
|
if(points.Count == 1) {
|
|
updateReferencePoints(points[0], curp);
|
|
List<Vector2D[]> shapes = getShapes(start, end);
|
|
|
|
//render shape
|
|
foreach (Vector2D[] shape in shapes) {
|
|
for(int i = 1; i < shape.Length; i++)
|
|
renderer.RenderLine(shape[i - 1], shape[i], LINE_THICKNESS, color, true);
|
|
}
|
|
|
|
//vertices
|
|
foreach (Vector2D[] shape in shapes) {
|
|
for (int i = 0; i < shape.Length; i++)
|
|
renderer.RenderRectangleFilled(new RectangleF(shape[i].x - vsize, shape[i].y - vsize, vsize * 2.0f, vsize * 2.0f), color, true);
|
|
}
|
|
|
|
//and labels
|
|
Vector2D[] labelCoords = new[] { start, new Vector2D(end.x, start.y), end, new Vector2D(start.x, end.y), start };
|
|
for(int i = 1; i < 5; i++) {
|
|
labels[i - 1].Start = labelCoords[i - 1];
|
|
labels[i - 1].End = labelCoords[i];
|
|
renderer.RenderText(labels[i - 1].TextLabel);
|
|
}
|
|
|
|
//render hint
|
|
if (horizontalSlices > 1 || verticalSlices > 1) {
|
|
hintLabel.Text = "H: " + (slicesH - 1) + "; V: " + (slicesV - 1);
|
|
if(width > hintLabel.Text.Length * vsize && height > 16 * vsize) {
|
|
float vPos = start.y + height / 2.0f;
|
|
hintLabel.Start = new Vector2D(start.x, vPos);
|
|
hintLabel.End = new Vector2D(end.x, vPos);
|
|
renderer.RenderText(hintLabel.TextLabel);
|
|
}
|
|
}
|
|
} else {
|
|
// Render vertex at cursor
|
|
renderer.RenderRectangleFilled(new RectangleF(curp.pos.x - vsize, curp.pos.y - vsize, vsize * 2.0f, vsize * 2.0f), color, true);
|
|
}
|
|
|
|
// Done
|
|
renderer.Finish();
|
|
}
|
|
|
|
// Done
|
|
renderer.Present();
|
|
}
|
|
|
|
// This draws a point at a specific location
|
|
override public bool DrawPointAt(Vector2D pos, bool stitch, bool stitchline) {
|
|
if(pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary ||
|
|
pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary)
|
|
return false;
|
|
|
|
DrawnVertex newpoint = new DrawnVertex();
|
|
newpoint.pos = pos;
|
|
newpoint.stitch = true;
|
|
newpoint.stitchline = stitchline;
|
|
points.Add(newpoint);
|
|
|
|
if(points.Count == 1) { //add point and labels
|
|
labels.AddRange(new[] { new LineLengthLabel(false), new LineLengthLabel(false), new LineLengthLabel(false), new LineLengthLabel(false) });
|
|
hintLabel = new HintLabel();
|
|
Update();
|
|
} else if(points[0].pos == points[1].pos) { //nothing is drawn
|
|
points = new List<DrawnVertex>();
|
|
FinishDraw();
|
|
} else {
|
|
//create vertices for final shape.
|
|
updateReferencePoints(points[0], newpoint);
|
|
gridpoints = new List<DrawnVertex[]>();
|
|
List<Vector2D[]> shapes = getShapes(start, end);
|
|
|
|
foreach (Vector2D[] shape in shapes) {
|
|
DrawnVertex[] verts = new DrawnVertex[shape.Length];
|
|
for(int i = 0; i < shape.Length; i++) {
|
|
newpoint = new DrawnVertex();
|
|
newpoint.pos = shape[i];
|
|
newpoint.stitch = true;
|
|
newpoint.stitchline = stitchline;
|
|
verts[i] = newpoint;
|
|
}
|
|
|
|
gridpoints.Add(verts);
|
|
}
|
|
|
|
FinishDraw();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected List<Vector2D[]> getShapes(Vector2D s, Vector2D e) {
|
|
//no shape
|
|
if(s == e) return new List<Vector2D[]>();
|
|
|
|
//line
|
|
if (e.x == s.x || e.y == s.y) return new List<Vector2D[]>{ new[] {s, e} };
|
|
|
|
//rectangles
|
|
if(gridlock) {
|
|
//rendering will be screwed if start point is not aligned to current grid size
|
|
if (General.Map.Grid.GridSize > initialGridSize && General.Map.Grid.SnappedToGrid(start) == start) {
|
|
initialGridSize = General.Map.Grid.GridSize;
|
|
}
|
|
|
|
int gs = (General.Map.Grid.GridSize > initialGridSize && General.Map.Grid.SnappedToGrid(start) != start ? initialGridSize : General.Map.Grid.GridSize);
|
|
slicesH = width / gs;
|
|
slicesV = height / gs;
|
|
} else {
|
|
slicesH = horizontalSlices;
|
|
slicesV = verticalSlices;
|
|
}
|
|
|
|
float rectWidth = Math.Max(1, (slicesH > 0 ? (float)width / slicesH : width));
|
|
float rectHeight = Math.Max(1, (slicesV > 0 ? (float)height / slicesV : height));
|
|
|
|
//create shape
|
|
List<Vector2D> rect = new List<Vector2D> { s, new Vector2D((int)e.x, (int)s.y), e, new Vector2D((int)s.x, (int)e.y), s };
|
|
if(!gridlock && slicesH == 1 && slicesV == 1) {
|
|
if(triangulate) rect.AddRange(new[] { s, e });
|
|
return new List<Vector2D[]>{ rect.ToArray() };
|
|
}
|
|
|
|
//create blocks
|
|
List<Vector2D[]> shapes = new List<Vector2D[]> { rect.ToArray() };
|
|
RectangleF[,] blocks = new RectangleF[slicesH, slicesV];
|
|
for(int w = 0; w < slicesH; w++) {
|
|
for(int h = 0; h < slicesV; h++) {
|
|
blocks[w, h] = RectangleF.FromLTRB((int)Math.Round(s.x + rectWidth * w), (int)Math.Round(s.y + rectHeight * h), (int)Math.Round(s.x + rectWidth * (w + 1)), (int)Math.Round(s.y + rectHeight * (h + 1)));
|
|
}
|
|
}
|
|
|
|
//add subdivisions
|
|
if(slicesH > 1) {
|
|
for(int w = 1; w < slicesH; w++) {
|
|
int px = (int) Math.Round(blocks[w, 0].X);
|
|
shapes.Add(new[] {new Vector2D(px, s.y), new Vector2D(px, e.y)});
|
|
}
|
|
}
|
|
if(slicesV > 1) {
|
|
for(int h = 1; h < slicesV; h++) {
|
|
int py = (int) Math.Round(blocks[0, h].Y);
|
|
shapes.Add(new[] { new Vector2D(s.x, py), new Vector2D(e.x, py) });
|
|
}
|
|
}
|
|
|
|
//triangulate?
|
|
if (triangulate) {
|
|
bool startflip = false;
|
|
bool flip = false;
|
|
|
|
for(int w = 0; w < slicesH; w++) {
|
|
for(int h = 0; h < slicesV; h++) {
|
|
if (flip) {
|
|
shapes.Add(new[] { new Vector2D(blocks[w, h].X, blocks[w, h].Y), new Vector2D(blocks[w, h].Right, blocks[w, h].Bottom) });
|
|
} else {
|
|
shapes.Add(new[] { new Vector2D(blocks[w, h].Right, blocks[w, h].Y), new Vector2D(blocks[w, h].X, blocks[w, h].Bottom) });
|
|
}
|
|
|
|
flip = !flip;
|
|
}
|
|
|
|
startflip = !startflip;
|
|
flip = startflip;
|
|
}
|
|
}
|
|
|
|
return shapes;
|
|
}
|
|
|
|
//update top-left and bottom-right points, which define drawing shape
|
|
private void updateReferencePoints(DrawnVertex p1, DrawnVertex p2) {
|
|
if(!p1.pos.IsFinite() || !p2.pos.IsFinite()) return;
|
|
|
|
if(p1.pos.x < p2.pos.x) {
|
|
start.x = p1.pos.x;
|
|
end.x = p2.pos.x;
|
|
} else {
|
|
start.x = p2.pos.x;
|
|
end.x = p1.pos.x;
|
|
}
|
|
|
|
if(p1.pos.y < p2.pos.y) {
|
|
start.y = p1.pos.y;
|
|
end.y = p2.pos.y;
|
|
} else {
|
|
start.y = p2.pos.y;
|
|
end.y = p1.pos.y;
|
|
}
|
|
|
|
width = (int)(end.x - start.x);
|
|
height = (int)(end.y - start.y);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Actions
|
|
|
|
[BeginAction("increasebevel")]
|
|
protected void increaseBevel() {
|
|
if(!gridlock && (points.Count < 2 || horizontalSlices < width - 2)) {
|
|
horizontalSlices++;
|
|
panel.HorizontalSlices = horizontalSlices - 1;
|
|
Update();
|
|
}
|
|
}
|
|
|
|
[BeginAction("decreasebevel")]
|
|
protected void decreaseBevel() {
|
|
if(!gridlock && horizontalSlices > 1) {
|
|
horizontalSlices--;
|
|
panel.HorizontalSlices = horizontalSlices - 1;
|
|
Update();
|
|
}
|
|
}
|
|
|
|
[BeginAction("increasesubdivlevel")]
|
|
protected void increaseSubdivLevel() {
|
|
if(!gridlock && (points.Count < 2 || verticalSlices < height - 2)) {
|
|
verticalSlices++;
|
|
panel.VerticalSlices = verticalSlices - 1;
|
|
Update();
|
|
}
|
|
}
|
|
|
|
[BeginAction("decreasesubdivlevel")]
|
|
protected void decreaseSubdivLevel() {
|
|
if(!gridlock && verticalSlices > 1) {
|
|
verticalSlices--;
|
|
panel.VerticalSlices = verticalSlices - 1;
|
|
Update();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|