UltimateZoneBuilder/Source/Plugins/BuilderModes/ClassicModes/BridgeMode.cs

856 lines
35 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Plugins;
using CodeImp.DoomBuilder.BuilderModes.Interface;
namespace CodeImp.DoomBuilder.BuilderModes.ClassicModes {
[EditMode(DisplayName = "Bridge Mode",
SwitchAction = "bridgemode",
ButtonImage = "BridgeMode.png",
ButtonOrder = 51,
ButtonGroup = "002_tools",
AllowCopyPaste = false,
Volatile = true,
Optional = false)]
public class BridgeMode : BaseClassicMode {
private const float GRIP_SIZE = 9.0f;
private const float LINE_THICKNESS = 0.8f;
internal static int MAX_SUBDIVISIONS = 32;
internal static int MIN_SUBDIVISIONS = 0;
protected string undoName = "Bridge draw";
protected string shapeName = "mesh";
private Vector2D[] pointGroup1;
private Vector2D[] pointGroup2;
private SectorProperties[] sectorProps1;
private SectorProperties[] sectorProps2;
private float[] relLenGroup1;
private float[] relLenGroup2;
private ControlHandle[] controlHandles;
private int curControlHandle = -1;
private PixelColor handleColor;
private List<Vector2D[]> curves;
private int segmentsCount;
// Options
protected bool snaptogrid; // SHIFT to toggle
//tools form
private BridgeModeForm form;
public BridgeMode() {
// We have no destructor
GC.SuppressFinalize(this);
}
// Engaging
public override void OnEngage() {
base.OnEngage();
renderer.SetPresentation(Presentation.Standard);
//check selection
ICollection<Linedef> selection = General.Map.Map.GetSelectedLinedefs(true);
List<Line> lines = new List<Line>();
foreach (Linedef ld in selection) {
Line l = new Line(ld);
lines.Add(l);
}
//do we have valid selection?
if (!setup(lines)) {
FinishDraw();
return;
}
//show form
form = new BridgeModeForm();
form.OnCancelClick += new EventHandler(form_OnCancelClick);
form.OnOkClick += new EventHandler(form_OnOkClick);
form.OnFlipClick += new EventHandler(form_OnFlipClick);
form.OnSubdivisionChanged += new EventHandler(form_OnSubdivisionChanged);
form.Show(Form.ActiveForm);
General.Interface.FocusDisplay();
handleColor = General.Colors.BrightColors[new Random().Next(General.Colors.BrightColors.Length - 1)];
update();
}
// When select button is pressed
protected override void OnSelectBegin() {
base.OnSelectBegin();
//check if control handle is selected
for (int i = 0; i < 4; i++) {
if (mousemappos.x <= controlHandles[i].Position.x + GRIP_SIZE && mousemappos.x >= controlHandles[i].Position.x - GRIP_SIZE && mousemappos.y <= controlHandles[i].Position.y + GRIP_SIZE && mousemappos.y >= controlHandles[i].Position.y - GRIP_SIZE) {
curControlHandle = i;
General.Interface.SetCursor(Cursors.Cross);
return;
}
}
curControlHandle = -1;
}
// When select button is released
protected override void OnSelectEnd() {
base.OnSelectEnd();
General.Interface.SetCursor(Cursors.Default);
curControlHandle = -1;
}
// Mouse moves
public override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
if (curControlHandle != -1) {
ControlHandle handle = controlHandles[curControlHandle];
if (snaptogrid) {
handle.Position = DrawGeometryMode.GetCurrentPosition(mousemappos, false, true, renderer, new List<DrawnVertex>()).pos;
} else {
handle.Position = mousemappos;
}
if (form.MirrorMode) {
Vector2D pos = handle.RelativePosition;
//handle angle
float angle = (float)Math.Atan2(-pos.y, -pos.x) + (float)Math.PI / 2f;
//angle of line, connecting handles ControlledPoints
float dirAngle = -(float)Math.Atan2(handle.Pair.ControlledPoint.y - handle.ControlledPoint.y, handle.Pair.ControlledPoint.x - handle.ControlledPoint.x);
float length = (float)Math.Sqrt(Math.Pow(Math.Abs(pos.x), 2.0) + Math.Pow(Math.Abs(pos.y), 2.0));
float mirroredAngle = angle + dirAngle * 2.0f;
handle.Pair.RelativePosition = new Vector2D((float)Math.Sin(mirroredAngle) * length, (float)Math.Cos(mirroredAngle) * length);
} else if (form.CopyMode) {
handle.Pair.RelativePosition = handle.RelativePosition;
}
update();
}
}
// Accepted
public override void OnAccept() {
Cursor.Current = Cursors.AppStarting;
General.Settings.FindDefaultDrawSettings();
//get vertices
List<List<Vector2D[]>> shapes = getShapes();
List<List<List<DrawnVertex>>> drawShapes = new List<List<List<DrawnVertex>>>();
List<List<DrawnVertex>> shapesRow;
List<DrawnVertex> points;
//set stitch range
float stitchrange = BuilderPlug.Me.StitchRange;
BuilderPlug.Me.StitchRange = 0.1f;
for (int i = 0; i < shapes.Count; i++) {
shapesRow = new List<List<DrawnVertex>>();
for (int c = 0; c < shapes[i].Count; c++) {
points = new List<DrawnVertex>();
for (int p = 0; p < shapes[i][c].Length; p++)
points.Add(DrawGeometryMode.GetCurrentPosition(shapes[i][c][p], true, false, renderer, points));
shapesRow.Add(points);
}
drawShapes.Add(shapesRow);
}
//restore stitch range
BuilderPlug.Me.StitchRange = stitchrange;
//draw lines
if (drawShapes.Count > 0) {
// Make undo for the draw
General.Map.UndoRedo.CreateUndo("Bridge ("+form.Subdivisions+" subdivisions)");
List<List<SectorProperties>> sectorProps = new List<List<SectorProperties>>();
List<List<List<Sector>>> newSectors = new List<List<List<Sector>>>();
//create sector properties collection
//sector row
for (int i = 0; i < drawShapes.Count; i++) {
sectorProps.Add(new List<SectorProperties>());
//sector in row
for (int c = 0; c < drawShapes[i].Count; c++)
sectorProps[i].Add(getSectorProperties(i, c));
}
// Make the drawing
//sector row
for (int i = 0; i < drawShapes.Count; i++) {
newSectors.Add(new List<List<Sector>>());
//sector in row
for (int c = 0; c < drawShapes[i].Count; c++) {
if (!Tools.DrawLines(drawShapes[i][c])) {
// Drawing failed
// NOTE: I have to call this twice, because the first time only cancels this volatile mode
General.Interface.DisplayStatus(StatusType.Warning, "Failed to create a Bezier Path...");
General.Map.UndoRedo.WithdrawUndo();
General.Map.UndoRedo.WithdrawUndo();
return;
}
List<Sector> newsectors = General.Map.Map.GetMarkedSectors(true);
newSectors[i].Add(newsectors);
//set floor/ceiling heights and brightness
foreach (Sector s in newsectors) {
SectorProperties sp = sectorProps[i][c];
s.Brightness = sp.Brightness;
s.FloorHeight = sp.FloorHeight;
s.CeilHeight = (sp.CeilingHeight < sp.FloorHeight ? sp.FloorHeight + 8 : sp.CeilingHeight);
}
}
}
//apply textures
//sector row
for (int i = 0; i < newSectors.Count; i++) {
//sector in row
for (int c = 0; c < newSectors[i].Count; c++) {
foreach (Sector s in newSectors[i][c]) {
foreach(Sidedef sd in s.Sidedefs){
if (sd.LowRequired())
sd.SetTextureLow(sectorProps[i][c].LowTexture);
if (sd.HighRequired())
sd.SetTextureHigh(sectorProps[i][c].HighTexture);
}
}
}
}
//apply textures to front/back sides of shape
//sector row
for (int i = 0; i < newSectors.Count; i++) {
//first/last sector in row
for (int c = 0; c < newSectors[i].Count; c += newSectors[i].Count-1) {
foreach (Sector s in newSectors[i][c]) {
foreach (Sidedef sd in s.Sidedefs) {
if (sd.Other != null) {
if (sd.Other.LowRequired() && sd.Other.LowTexture == "-")
sd.Other.SetTextureLow(sectorProps[i][c].LowTexture);
if (sd.Other.HighRequired() && sd.Other.HighTexture == "-")
sd.Other.SetTextureHigh(sectorProps[i][c].HighTexture);
}
}
}
}
}
General.Interface.DisplayStatus(StatusType.Action, "Created a Bridge with " + form.Subdivisions + " subdivisions.");
// Snap to map format accuracy
General.Map.Map.SnapAllToAccuracy();
// Clear selection
General.Map.Map.ClearAllSelected();
// Update cached values
General.Map.Map.Update();
// Update the used textures
General.Map.Data.UpdateUsedTextures();
// Map is changed
General.Map.IsChanged = true;
}
//close form
if (form != null)
form.Close();
// Done
Cursor.Current = Cursors.Default;
// Return to original mode
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
}
// Cancelled
public override void OnCancel() {
// Cancel base class
base.OnCancel();
//close form
if (form != null)
form.Dispose();
// Return to original mode
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
}
// When a key is released
public override void OnKeyUp(KeyEventArgs e) {
base.OnKeyUp(e);
if (snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) update();
}
// When a key is pressed
public override void OnKeyDown(KeyEventArgs e) {
base.OnKeyDown(e);
if (snaptogrid != (General.Interface.ShiftState ^ General.Interface.SnapToGrid)) update();
}
// This redraws the display
public override void OnRedrawDisplay() {
renderer.RedrawSurface();
// Render lines
if (renderer.StartPlotter(true)) {
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
renderer.PlotVerticesSet(General.Map.Map.Vertices);
renderer.Finish();
}
// Render things
if (renderer.StartThings(true)) {
renderer.RenderThingSet(General.Map.Map.Things, 1.0f);
renderer.Finish();
}
// Normal update
update();
}
//this checks if initial data is valid
private bool setup(List<Line> lines) {
if (!setupPointGroups(lines))
return false;
//setup control handles
Vector2D center1 = getPointOnLine(pointGroup1[0], pointGroup1[segmentsCount - 1], 0.5f);
Vector2D center2 = getPointOnLine(pointGroup2[0], pointGroup2[segmentsCount - 1], 0.5f);
Vector2D loc1 = getHandleLocation(pointGroup1[0], pointGroup1[segmentsCount - 1], center2);
Vector2D loc2 = getHandleLocation(pointGroup2[0], pointGroup2[segmentsCount - 1], center1);
ControlHandle ch1 = new ControlHandle();
ch1.ControlledPoint = pointGroup1[0];
ch1.RelativePosition = loc1;
ControlHandle ch2 = new ControlHandle();
ch2.ControlledPoint = pointGroup2[0];
ch2.RelativePosition = loc2;
ControlHandle ch3 = new ControlHandle();
ch3.ControlledPoint = pointGroup1[segmentsCount - 1];
ch3.RelativePosition = loc1;
ControlHandle ch4 = new ControlHandle();
ch4.ControlledPoint = pointGroup2[segmentsCount - 1];
ch4.RelativePosition = loc2;
ch1.Pair = ch3;
ch2.Pair = ch4;
ch3.Pair = ch1;
ch4.Pair = ch2;
controlHandles = new ControlHandle[] {ch1, ch2, ch3, ch4};
//setup relative segments lengths
relLenGroup1 = getRelativeLengths(pointGroup1);
relLenGroup2 = getRelativeLengths(pointGroup2);
return true;
}
private void update() {
if (renderer.StartOverlay(true)) {
snaptogrid = General.Interface.ShiftState ^ General.Interface.SnapToGrid;
PixelColor linesColor = snaptogrid ? General.Colors.Selection : General.Colors.Highlight;
//draw curves
Vector2D cp1, cp2;
curves = new List<Vector2D[]>();
for (int i = 0; i < segmentsCount; i++) {
cp1 = getPointOnLine(controlHandles[0].Position, controlHandles[2].Position, relLenGroup1[i]);
cp2 = getPointOnLine(controlHandles[1].Position, controlHandles[3].Position, relLenGroup2[i]);
curves.Add(getBezierCurve(pointGroup1[i], pointGroup2[i], cp1, cp2, form.Subdivisions));
for (int c = 1; c < curves[i].Length; c++ )
renderer.RenderLine(curves[i][c - 1], curves[i][c], LINE_THICKNESS, linesColor, true);
}
//draw connecting lines
if (form.Subdivisions > 1) {
for (int i = 1; i < segmentsCount; i++) {
for (int c = 1; c < form.Subdivisions; c++) {
renderer.RenderLine(curves[i-1][c], curves[i][c], LINE_THICKNESS, linesColor, true);
}
}
}
//draw vertices
float vsize = ((float)renderer.VertexSize + 1.0f) / renderer.Scale;
foreach(Vector2D[] points in curves){
for (int i = 1; i < points.Length - 1; i++ ) {
renderer.RenderRectangleFilled(new RectangleF(points[i].x - vsize, points[i].y - vsize, vsize * 2.0f, vsize * 2.0f), linesColor, true);
}
}
//draw handle lines
renderer.RenderLine(pointGroup1[0], controlHandles[0].Position, LINE_THICKNESS, handleColor, true);
renderer.RenderLine(pointGroup2[0], controlHandles[1].Position, LINE_THICKNESS, handleColor, true);
renderer.RenderLine(pointGroup1[segmentsCount - 1], controlHandles[2].Position, LINE_THICKNESS, handleColor, true);
renderer.RenderLine(pointGroup2[segmentsCount - 1], controlHandles[3].Position, LINE_THICKNESS, handleColor, true);
//draw handles
float gripsize = GRIP_SIZE / renderer.Scale;
for (int i = 0; i < 4; i++) {
RectangleF handleRect = new RectangleF(controlHandles[i].Position.x - gripsize * 0.5f, controlHandles[i].Position.y - gripsize * 0.5f, gripsize, gripsize);
renderer.RenderRectangleFilled(handleRect, General.Colors.Background, true);
renderer.RenderRectangle(handleRect, 2, General.Colors.Highlight, true);
}
renderer.Finish();
}
renderer.Present();
}
private SectorProperties getSectorProperties(int lineIndex, int sectorIndex) {
float delta = (float)sectorIndex / (float)form.Subdivisions;
delta += (1.0f - delta) / (float)form.Subdivisions;
SectorProperties sp = new SectorProperties();
sp.Brightness = intepolateValue(sectorProps1[lineIndex].Brightness, sectorProps2[lineIndex].Brightness, delta, form.BrightnessMode);
sp.FloorHeight = intepolateValue(sectorProps1[lineIndex].FloorHeight, sectorProps2[lineIndex].FloorHeight, delta, form.FloorAlignMode);
sp.CeilingHeight = intepolateValue(sectorProps1[lineIndex].CeilingHeight, sectorProps2[lineIndex].CeilingHeight, delta, form.CeilingAlignMode);
//textures
sp.LowTexture = sectorProps1[lineIndex].LowTexture != "-" ? sectorProps1[lineIndex].LowTexture : sectorProps2[lineIndex].LowTexture;
sp.HighTexture = sectorProps1[lineIndex].HighTexture != "-" ? sectorProps1[lineIndex].HighTexture : sectorProps2[lineIndex].HighTexture;
return sp;
}
//this returns a list of shapes to draw
private List<List<Vector2D[]>> getShapes() {
List<List<Vector2D[]>> shapes = new List<List<Vector2D[]>>();
for (int i = 1; i < segmentsCount; i++) {
List<Vector2D[]> segShapes = new List<Vector2D[]>();
for (int c = 1; c <= form.Subdivisions; c++) {
Vector2D p0 = curves[i - 1][c - 1];
Vector2D p1 = curves[i - 1][c];
Vector2D p2 = curves[i][c];
Vector2D p3 = curves[i][c - 1];
segShapes.Add(new Vector2D[] { p0, p1, p2, p3, p0 });
}
shapes.Add(segShapes);
}
return shapes;
}
//POINT OPS
//this returns an array of linedef lengths relative to total segment length
private float[] getRelativeLengths(Vector2D[] pointGroup) {
float[] relLenGroup = new float[pointGroup.Length];
relLenGroup[0] = 0.0f;
//get length and angle of line, which defines the shape
float length = getLineLength(pointGroup[0], pointGroup[segmentsCount - 1]);
float angle = (float)Math.Atan2(pointGroup[0].y - pointGroup[segmentsCount - 1].y, pointGroup[0].x - pointGroup[segmentsCount - 1].x);
float curAngle, diff, segLen;
Vector2D p0, p1;
//get relative length of every line
for (int i = 1; i < pointGroup.Length - 1; i++) {
p0 = pointGroup[i - 1];
p1 = pointGroup[i];
curAngle = (float)Math.Atan2(p0.y - p1.y, p0.x - p1.x);
diff = (angle + (float)Math.PI) - (curAngle + (float)Math.PI);
segLen = (int)(getLineLength(p0, p1) * Math.Cos(diff));
relLenGroup[i] = relLenGroup[i - 1] + segLen / length;
}
relLenGroup[pointGroup.Length - 1] = 1.0f;
return relLenGroup;
}
private float getLineLength(Vector2D p0, Vector2D p1) {
float vx = Math.Abs(p0.x - p1.x);
float vy = Math.Abs(p0.y - p1.y);
return (float)Math.Sqrt(vx * vx + vy * vy);
}
//this returns relative handle location
private Vector2D getHandleLocation(Vector2D start, Vector2D end, Vector2D direction) {
float angle = -(float)Math.Atan2(start.y - end.y, start.x - end.x);
float dirAngle = -(float)Math.Atan2(direction.y - start.y, direction.x - start.x);
float length = (float)Math.Sqrt(Math.Pow(Math.Abs(start.x - end.x), 2.0) + Math.Pow(Math.Abs(start.y - end.y), 2.0)) * 0.3f;
float diff = (angle + (float)Math.PI) - (dirAngle + (float)Math.PI);
if (diff > Math.PI || (diff < 0 && diff > -Math.PI))
angle += (float)Math.PI;
return new Vector2D((float)(Math.Sin(angle) * length), (float)(Math.Cos(angle) * length));
}
//LINE DRAWING
//this returns array of Vector2D to draw 4-point bezier curve
private Vector2D[] getBezierCurve(Vector2D p1, Vector2D p2, Vector2D cp1, Vector2D cp2, int steps) {
if (steps < 0) return null;
int totalSteps = steps + 1;
Vector2D[] points = new Vector2D[totalSteps];
float step = 1f / (float)steps;
float curStep = 0f;
float curStepInv, m1, m2, m3, m4;
for (int i = 0; i < totalSteps; i++) {
curStepInv = 1f - curStep;
m1 = curStepInv * curStepInv * curStepInv;
m2 = 3 * curStep * curStepInv * curStepInv;
m3 = 3 * curStep * curStep * curStepInv;
m4 = curStep * curStep * curStep;
int px = (int)(m1 * p1.x + m2 * cp1.x + m3 * cp2.x + m4 * p2.x);
int py = (int)(m1 * p1.y + m2 * cp1.y + m3 * cp2.y + m4 * p2.y);
points[i] = new Vector2D(px, py);
curStep += step;
}
return points;
}
//returns true if 2 lines intersect
private bool linesIntersect(Line line1, Line line2) {
float zn = (line2.End.y - line2.Start.y) * (line1.End.x - line1.Start.x) - (line2.End.x - line2.Start.x) * (line1.End.y - line1.Start.y);
float ch1 = (line2.End.x - line2.Start.x) * (line1.Start.y - line2.Start.y) - (line2.End.y - line2.Start.y) * (line1.Start.x - line2.Start.x);
float ch2 = (line1.End.x - line1.Start.x) * (line1.Start.y - line2.Start.y) - (line1.End.y - line1.Start.y) * (line1.Start.x - line2.Start.x);
if (zn == 0) return false;
if ((ch1 / zn <= 1 && ch1 / zn >= 0) && (ch2 / zn <= 1 && ch2 / zn >= 0))
return true;
return false;
}
//it's basically 2-point bezier curve
private Vector2D getPointOnLine(Vector2D p1, Vector2D p2, float delta) {
return new Vector2D((int)((1f - delta) * p1.x + delta * p2.x), (int)((1f - delta) * p1.y + delta * p2.y));
}
//LINE SORTING
//this gets two arrays of connected points from given lines. Returns true if all went well.
private bool setupPointGroups(List<Line> linesList) {
//find prev/next lines for each line
for (int i = 0; i < linesList.Count; i++) {
Line curLine = linesList[i];
for (int c = 0; c < linesList.Count; c++) {
if (c != i) {//don't wanna play with ourselves :)
Line line = linesList[c];
//check start and end points
if (curLine.Start == line.Start) {
line.Invert();
curLine.Previous = line;
} else if (curLine.Start == line.End) {
curLine.Previous = line;
} else if (curLine.End == line.End) {
line.Invert();
curLine.Next = line;
} else if (curLine.End == line.Start) {
curLine.Next = line;
}
}
}
}
List<List<Vector2D>> pointGroups = new List<List<Vector2D>>();
List<List<Line>> sortedLines = new List<List<Line>>();
//now find start lines
for (int i = 0; i < linesList.Count; i++) {
Line curLine = linesList[i];
if (curLine.Previous == null) { //found start
//collect points
Line l = curLine;
List<Vector2D> points = new List<Vector2D>();
List<Line> lines = new List<Line>();
points.Add(l.Start);
do {
points.Add(l.End);
lines.Add(l);
} while ((l = l.Next) != null);
pointGroups.Add(points);
sortedLines.Add(lines);
}
}
if (pointGroups.Count != 2) {
General.Interface.DisplayStatus(StatusType.Warning, "Incorrect number of linedef groups! Expected 2, but got " + pointGroups.Count);
return false;
}
if (pointGroups[0].Count != pointGroups[1].Count) {
General.Interface.DisplayStatus(StatusType.Warning, "Linedefs groups must have equal length! Got " + pointGroups[0].Count + " in first group and " + pointGroups[1].Count + " in second.");
return false;
}
//check if lines from first group intersect with lines from second group
foreach (Line l1 in sortedLines[0]) {
foreach (Line l2 in sortedLines[1]) {
if (linesIntersect(l1, l2)) {
General.Interface.DisplayStatus(StatusType.Warning, "One or more lines from first group intersect with one or more lines from second group!");
return false;
}
}
}
//both groups count should match at this point
segmentsCount = pointGroups[0].Count;
//collect sector properties
sectorProps1 = new SectorProperties[sortedLines[0].Count];
for (int i = 0; i < sortedLines[0].Count; i++ ) {
sectorProps1[i] = sortedLines[0][i].SectorProperties;
}
sectorProps2 = new SectorProperties[sortedLines[1].Count];
for (int i = 0; i < sortedLines[1].Count; i++) {
sectorProps2[i] = sortedLines[1][i].SectorProperties;
}
//check if we need to reverse one of point groups
Line line1 = new Line(pointGroups[0][0], pointGroups[1][0]);
Line line2 = new Line(pointGroups[0][segmentsCount - 1], pointGroups[1][segmentsCount - 1]);
if (linesIntersect(line1, line2)) {
pointGroups[0].Reverse();
Array.Reverse(sectorProps1);
}
//fill point groups
pointGroup1 = new Vector2D[segmentsCount];
pointGroup2 = new Vector2D[segmentsCount];
pointGroups[0].CopyTo(pointGroup1);
pointGroups[1].CopyTo(pointGroup2);
return true;
}
//Easing functions. Based on Robert Penner's original easing equations (http://www.robertpenner.com/easing/)
/**
* Easing equation function for a sinusoidal (sin(t)) easing in: accelerating from zero velocity.
*/
private int easeInSine(int val1, int val2, float delta) {
float f_val1 = (float)val1;
float f_val2 = (float)val2 - f_val1;
return (int)(-f_val2 * Math.Cos(delta * (Math.PI / 2.0f)) + f_val2 + f_val1);
}
/**
* Easing equation function for a sinusoidal (sin(t)) easing out: decelerating from zero velocity.
*/
private int easeOutSine(int val1, int val2, float delta) {
float f_val1 = (float)val1;
float f_val2 = (float)val2;
return (int)((f_val2 - f_val1) * Math.Sin(delta * ((float)Math.PI / 2.0f)) + f_val1);
}
/**
* Easing equation function for a sinusoidal (sin(t)) easing in/out: acceleration until halfway, then deceleration.
*/
private int easeInOutSine(int val1, int val2, float delta) {
float f_val1 = (float)val1;
float f_val2 = (float)val2;
return (int)(-f_val2 / 2.0f * (Math.Cos(Math.PI * delta) - 1.0f) + f_val1);
}
private int intepolateValue(int val1, int val2, float delta, string mode) {
switch (mode) {
case BridgeInterpolationMode.HIGHEST:
case BridgeInterpolationMode.BRIGHTNESS_HIGHEST:
return Math.Max(val1, val2);
break;
case BridgeInterpolationMode.LOWEST:
case BridgeInterpolationMode.BRIGHTNESS_LOWEST:
return Math.Min(val1, val2);
break;
case BridgeInterpolationMode.LINEAR:
return (int)(delta * (float)val2 + (1.0f - delta) * (float)val1);
break;
case BridgeInterpolationMode.IN_SINE:
return easeInSine(val1, val2, delta);
break;
case BridgeInterpolationMode.OUT_SINE:
return easeOutSine(val1, val2, delta);
break;
case BridgeInterpolationMode.IN_OUT_SINE:
return easeInOutSine(val1, val2, delta);
break;
default:
throw new Exception("DrawBezierPathMode.intepolateValue: got unknown mode: '" + mode + "'");
break;
}
return -1;
}
//EVENTS
private void form_OnSubdivisionChanged(object sender, EventArgs e) {
update();
}
private void form_OnOkClick(object sender, EventArgs e) {
FinishDraw();
}
private void form_OnCancelClick(object sender, EventArgs e) {
OnCancel();
}
private void form_OnFlipClick(object sender, EventArgs e) {
Array.Reverse(pointGroup1);
Array.Reverse(sectorProps1);
//swap handles position
Vector2D p = controlHandles[0].Position;
controlHandles[0].Position = controlHandles[2].Position;
controlHandles[2].Position = p;
update();
}
//ACTIONS
// Finish drawing
[BeginAction("finishdraw")]
public void FinishDraw() {
// Accept the changes
General.Editing.AcceptMode();
}
[BeginAction("increasesubdivlevel")]
private void increaseSubdivLevel() {
if (form != null && form.Subdivisions < MAX_SUBDIVISIONS)
form.Subdivisions++;
}
[BeginAction("decreasesubdivlevel")]
private void decreaseSubdivLevel() {
if (form != null && form.Subdivisions > MIN_SUBDIVISIONS)
form.Subdivisions--;
}
}
internal struct SectorProperties {
public int FloorHeight;
public int CeilingHeight;
public int Brightness;
public float Angle;
public string HighTexture;
public string LowTexture;
}
internal class ControlHandle
{
public Vector2D Position;
public Vector2D ControlledPoint; //point, to which this handle is assigned
public Vector2D RelativePosition {
get {
return new Vector2D(Position.x - ControlledPoint.x, Position.y - ControlledPoint.y);
}
set {
Position = new Vector2D(ControlledPoint.x + value.x, ControlledPoint.y + value.y);
}
}
public ControlHandle Pair; //second handle, to which this handle is paired
}
internal class Line {
public Vector2D Start { get { return start; } }
private Vector2D start;
public Vector2D End { get { return end; } }
private Vector2D end;
public SectorProperties SectorProperties;
public Line Previous;
public Line Next;
public Line(Linedef ld) {
start = new Vector2D((int)ld.Start.Position.x, (int)ld.Start.Position.y);
end = new Vector2D((int)ld.End.Position.x, (int)ld.End.Position.y);
SectorProperties = new SectorProperties();
SectorProperties.Angle = ld.Angle;
if (ld.Back != null) {
SectorProperties.CeilingHeight = ld.Back.Sector.CeilHeight;
SectorProperties.FloorHeight = ld.Back.Sector.FloorHeight;
SectorProperties.Brightness = ld.Back.Sector.Brightness;
SectorProperties.HighTexture = ld.Back.HighTexture;
SectorProperties.LowTexture = ld.Back.LowTexture;
}else if(ld.Front != null){
SectorProperties.CeilingHeight = ld.Front.Sector.CeilHeight;
SectorProperties.FloorHeight = ld.Front.Sector.FloorHeight;
SectorProperties.Brightness = ld.Front.Sector.Brightness;
SectorProperties.HighTexture = ld.Front.HighTexture;
SectorProperties.LowTexture = ld.Front.LowTexture;
}else{
SectorProperties.CeilingHeight = 128;
SectorProperties.FloorHeight = 0;
SectorProperties.Brightness = 192;
SectorProperties.HighTexture = "-";
SectorProperties.LowTexture = "-";
}
}
public Line(Vector2D start, Vector2D end) {
this.start = start;
this.end = end;
}
public void Invert() {
Vector2D s = start;
start = end;
end = s;
SectorProperties.Angle *= -1;
}
}
}