示例#1
0
 public static float Length2Sq(this PointF p)
 {
     return(Math_.Sqr(p.X) + Math_.Sqr(p.Y));
 }
示例#2
0
        protected override void OnMouseWheel(MouseWheelEventArgs args)
        {
            base.OnMouseWheel(args);

            // If there is a mouse op in progress, forward the event
            var op = MouseOperations.Active;

            if (op != null && !op.Cancelled)
            {
                op.MouseWheel(args);
                if (args.Handled)
                {
                    return;
                }
            }

            var location  = args.GetPosition(this);
            var along_ray = Options.MouseCentredZoom || Keyboard.Modifiers.HasFlag(ModifierKeys.Alt);
            var chart_pt  = ClientToChart(location);
            var hit       = HitTestZone(location, Keyboard.Modifiers, args.ToMouseBtns());

            // Batch mouse wheel events into 100ms groups
            var defer_nav_checkpoint = DeferNavCheckpoints();

            Dispatcher_.BeginInvokeDelayed(() =>
            {
                Util.Dispose(ref defer_nav_checkpoint !);
                SaveNavCheckpoint();
            }, TimeSpan.FromMilliseconds(100));

            var scale = 0.001f;

            if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
            {
                scale *= 0.1f;
            }
            if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
            {
                scale *= 0.01f;
            }
            var delta = Math_.Clamp(args.Delta * scale, -0.999f, 0.999f);
            var chg   = (string?)null;

            // If zooming is allowed on both axes, translate the camera
            if (hit.Zone == EZone.Chart && XAxis.AllowZoom && YAxis.AllowZoom)
            {
                // Translate the camera along a ray through 'point'
                var loc = Gui_.MapPoint(this, Scene, location);
                Scene.Window.MouseNavigateZ(loc.ToPointF(), args.ToMouseBtns(Keyboard.Modifiers), args.Delta, along_ray);
                chg = nameof(SetRangeFromCamera);
            }

            // Otherwise, zoom on the allowed axis only
            else if (hit.Zone == EZone.XAxis || (hit.Zone == EZone.Chart && !YAxis.AllowZoom))
            {
                if (hit.ModifierKeys.HasFlag(ModifierKeys.Control) && XAxis.AllowScroll)
                {
                    // Scroll the XAxis
                    XAxis.Shift(XAxis.Span * delta);
                    chg = nameof(SetCameraFromRange);
                }
                else if (!hit.ModifierKeys.HasFlag(ModifierKeys.Control) && XAxis.AllowZoom)
                {
                    // Change the aspect ratio by zooming on the XAxis
                    var x    = along_ray ? chart_pt.X : XAxis.Centre;
                    var left = (XAxis.Min - x) * (1f - delta);
                    var rite = (XAxis.Max - x) * (1f - delta);
                    XAxis.Set(x + left, x + rite);
                    if (Options.LockAspect != null)
                    {
                        YAxis.Span *= (1f - delta);
                    }

                    chg = nameof(SetCameraFromRange);
                }
            }
            else if (hit.Zone == EZone.YAxis || (hit.Zone == EZone.Chart && !XAxis.AllowZoom))
            {
                if (hit.ModifierKeys.HasFlag(ModifierKeys.Control) && YAxis.AllowScroll)
                {
                    // Scroll the YAxis
                    YAxis.Shift(YAxis.Span * delta);
                    chg = nameof(SetCameraFromRange);
                }
                else if (!hit.ModifierKeys.HasFlag(ModifierKeys.Control) && YAxis.AllowZoom)
                {
                    // Change the aspect ratio by zooming on the YAxis
                    var y    = along_ray ? chart_pt.Y : YAxis.Centre;
                    var left = (YAxis.Min - y) * (1f - delta);
                    var rite = (YAxis.Max - y) * (1f - delta);
                    YAxis.Set(y + left, y + rite);
                    if (Options.LockAspect != null)
                    {
                        XAxis.Span *= (1f - delta);
                    }

                    chg = nameof(SetCameraFromRange);
                }
            }

            switch (chg)
            {
            // Update the axes from the camera position
            case nameof(SetRangeFromCamera):
                SetRangeFromCamera();
                NotifyPropertyChanged(nameof(ValueAtPointer));
                Invalidate();
                break;

            // Set the camera position from the new axis ranges
            case nameof(SetCameraFromRange):
                SetCameraFromRange();
                NotifyPropertyChanged(nameof(ValueAtPointer));
                Invalidate();
                break;
            }
        }
示例#3
0
        public Bond(int perm, Element elem1, Element elem2, GameConstants consts)
        {
            Perm = perm;

            // The electro-static force between two charged objects is F = k*Q*q/r²
            // Assume elem1 and elem2 are separated such that their outermost electron shells just touch
            // The total bond strength is the sum of the electro static forces:
            //  P1 - P2 (repulsive), E1 - E2 (repulsive), P1 - E2 (attractive), P2 - E1 (attractive)

            // Assuming ionic/covalent bonding only, P1 and P2 can share electrons in their outer orbital.
            // The proton charges are the effective (Zeff) positive charge, the electron charge is
            // the charge of the maximum number of electrons that can be borrowed when trying to fill the
            // the outer orbital.

            // Find the number of electrons available to be shared between elem1 and elem2 in a covalent bond
            var shareable = Shareable(elem1, elem2);

            // Constant to make the strength reasonable numbers
            var scaler = 1e11 * consts.CoulombConstant * Math_.Sqr(consts.ElectronCharge) / Math_.Sqr(10e-12);

            // Assume these electrons sit between the two atoms, calculate the electro-static
            // force between the electrons and each element
            var strength = 0.0;

            Order = 0;            // Order is the number of electrons actually shared in the bond
            for (int i = 0; i != shareable; ++i)
            {
                // This is the charge experienced by each element in relation to shared electron 'i'
                var charge1 = elem1.EffectiveCharge(consts, elem1.AtomicNumber + i + 1);
                var charge2 = elem2.EffectiveCharge(consts, elem2.AtomicNumber + i + 1);

                // If either element experiences a negative charge, then this shared election does
                // not contribute to the bond and no further shared electrons will either
                if (charge1 < 0 || charge2 < 0)
                {
                    break;
                }

                // Accumulate the attractive forces
                strength += scaler * charge1 * 1.0 / Math_.Sqr(elem1.ValenceOrbitalRadius);
                strength += scaler * charge2 * 1.0 / Math_.Sqr(elem2.ValenceOrbitalRadius);
                ++Order;
            }
            {            // Remove the effective charge repulsive force
                var charge1 = elem1.EffectiveCharge(consts, elem1.AtomicNumber + Order);
                var charge2 = elem2.EffectiveCharge(consts, elem2.AtomicNumber + Order);
                strength -= scaler * charge1 * charge2 / Math_.Sqr(elem1.ValenceOrbitalRadius + elem2.ValenceOrbitalRadius);
            }

            // Scale the strength by 'Order'
            // Relationship (got from Carbon) is Strength = BaseStrength * Order ^ 0.8
            Count        = new int[Math.Max(Order, 1)];
            BaseStrength = strength * Math.Pow(Order, -0.8);

            // Ionicity > ~1.8 (Paulie scale) results in an ionic bond (as opposed to covalent bond).
            // The atoms of covalent materials are bound tightly to each other in stable molecules, but those
            // molecules are generally not very strongly attracted to other molecules in the Compound. On the
            // other hand, the atoms (ions) in ionic materials show strong attractions to other ions in their
            // vicinity. This generally leads to low melting points for covalent solids, and high melting points
            // for ionic solids.
            Ionicity = Math.Abs(elem2.Electronegativity - elem1.Electronegativity);
        }
示例#4
0
            /// <summary>
            /// Write the user input into the console and update the caret location.
            /// Set 'redraw' true if everything up to '_caret' is unchanged.</summary>
            private void Update(bool redraw)
            {
                // Notes:
                //  - It's much easier to treat the console buffer as a 1D array.
                //  - '_caret' is the position in the console buffer that corresponds to '_anchor'.
                //    Callers can move the visible portion of the user input by adjusting '_caret' and '_anchor'.
                //  - The console buffer may get resized.
                //  - Writing to the console can cause it to roll over.
                //  - There seems to be a weird Linux-only bug where pasting text that extends past
                //    the end of the console buffer does not cause it to automatically roll over.
                //    I haven't found a work-around for this, but it only seems to be a problem for paste,
                //    and the full string pasted is added to the user input buffer.
                //
                // Think of it like this:
                //                                    _caret
                //   Console Buffer:    [---------------V------------] (1D array)
                //   User Input:  (=====================^===I==)       (1D array)
                //                                   _anchor

                using (_console.SyncWrites())
                {
                    var buf_length = _console.BufferLength;
                    var buf_size   = _console.BufferSize;
                    if (buf_size.IsEmpty)
                    {
                        return;
                    }

                    // Determine if the whole user input needs redrawing
                    var moved   = MoveToNextLine;
                    var resized = buf_size != _last_size;
                    redraw |= moved | resized;

                    // Erase the previous buffer text.
                    // If 'redraw', clear from the start of the user input.
                    // Otherwise, just erase from the insert position onwards.
                    _console.Clear(
                        _console.NormaliseLocation(Pt(_caret - (redraw ? _anchor : 0))),
                        _console.NormaliseLocation(Pt(_caret + (_last_length - _anchor))),
                        false);

                    // If the caret was moved by something else, set '_caret' to the next new line
                    if (resized)
                    {
                        var pt = Pt(_caret, _last_size.Width);
                        _caret = Idx(pt);
                    }
                    if (moved)
                    {
                        var pt0 = Pt(_caret);
                        var pt1 = _console.Location;
                        pt0.Y  = Math_.Clamp(pt1.Y + (pt1.X != 0 ? 1 : 0), 0, buf_size.Height - 1);
                        _caret = Idx(pt0);
                    }

                    // Write the user input to the console buffer.
                    // Always write up to 'Insert', even if it causes a roll of the buffer.
                    // For text after the insert position, only write up to the end of the buffer.
                    int origin = 0, beg = 0, len = 0;
                    if (redraw)
                    {
                        // Where in the console buffer to start writing to
                        origin = Math_.Clamp(_caret - _anchor, 0, buf_length - 1);

                        // Where in the user input to read from
                        beg = Math_.Clamp(_anchor - _caret, 0, _line.Length);

                        // The length of user input to write
                        // Clamp to within the console buffer, unless the 'Insert' position extends beyond it.
                        len = Math_.Clamp(_line.Length - beg, 0, buf_length - origin - 1);
                        len = Math.Max(len, Insert - beg);
                    }
                    // Otherwise, just write forwards from '_anchor'
                    else
                    {
                        // Where in the console buffer to start writing to
                        origin = Math_.Clamp(_caret, 0, buf_length - 1);

                        // Where in the user input to read from
                        beg = Math_.Clamp(_anchor, 0, _line.Length);

                        // The length of user input to write
                        // Clamp to within the console buffer, unless the 'Insert' position extends beyond it.
                        len = Math_.Clamp(_line.Length - beg, 0, buf_length - origin - 1);
                        len = Math.Max(len, Insert - beg);
                    }

                    // Write to the console (this could invalidate 'origin')
                    _console.Location = _console.NormaliseLocation(Pt(origin));
                    _console.Write(_line.ToString(beg, len));

                    // Move the caret position to match the 'Insert' position.
                    _caret            = Math_.Clamp(_caret + (Insert - _anchor), 0, buf_length - 1);
                    _console.Location = _console.NormaliseLocation(Pt(_caret));
                    MoveToNextLine    = false;

                    // Save the values used in this update
                    _last_length = _line.Length;
                    _last_size   = buf_size;
                    _anchor      = Insert;
                }
            }
示例#5
0
        /// <summary>Handle mouse messages over 'Target' and perform resizing</summary>
        public bool PreFilterMessage(ref Message m)
        {
            if (m.HWnd == Target.Handle || Win32.IsChild(Target.Handle, m.HWnd))
            {
                switch (m.Msg)
                {
                case Win32.WM_LBUTTONDOWN:
                    #region
                {
                    var pt = Control.MousePosition;
                    m_mask = Mask(pt);
                    if (m_mask != EBoxZone.None)
                    {
                        Target.Cursor  = m_mask.ToCursor();
                        m_grab         = pt;
                        m_size         = Target.Size;
                        m_loc          = Target.Location;
                        Target.Capture = true;
                        return(true);
                    }
                    break;
                }

                    #endregion
                case Win32.WM_MOUSEMOVE:
                    #region
                {
                    var pt = Control.MousePosition;
                    if (m_grab != null)
                    {
                        var mn    = Target.MinimumSize;
                        var mx    = Target.MaximumSize != Size.Empty ? Target.MaximumSize : new Size(int.MaxValue, int.MaxValue);
                        var delta = Point_.Subtract(pt, m_grab.Value);
                        if ((m_mask & EBoxZone.Right) != 0)
                        {
                            Target.Width = Math_.Clamp(m_size.Width + delta.Width, mn.Width, mx.Width);
                        }
                        if ((m_mask & EBoxZone.Bottom) != 0)
                        {
                            Target.Height = Math_.Clamp(m_size.Height + delta.Height, mn.Height, mx.Height);
                        }
                        if ((m_mask & EBoxZone.Left) != 0)
                        {
                            Target.Width = Math_.Clamp(m_size.Width - delta.Width, mn.Width, mx.Width); Target.Left = m_loc.X + m_size.Width - Target.Width;
                        }
                        if ((m_mask & EBoxZone.Top) != 0)
                        {
                            Target.Height = Math_.Clamp(m_size.Height - delta.Height, mn.Height, mx.Height); Target.Top = m_loc.Y + m_size.Height - Target.Height;
                        }
                        return(true);
                    }
                    else if (!Win32.IsChild(Target.Handle, m.HWnd))
                    {
                        var mask = Mask(pt);
                        if (mask != m_mask)
                        {
                            Target.Cursor = mask.ToCursor();
                            m_mask        = mask;
                        }
                    }
                    else
                    {
                        Target.Cursor = Cursors.Default;
                    }
                    break;
                }

                    #endregion
                case Win32.WM_LBUTTONUP:
                    #region
                {
                    if (m_grab != null)
                    {
                        m_grab         = null;
                        Target.Capture = false;
                        return(true);
                    }
                    break;
                }

                    #endregion
                case Win32.WM_MOUSELEAVE:
                    #region
                {
                    m_mask = EBoxZone.None;
                    break;
                }
                    #endregion
                }
            }
            return(false);
        }
示例#6
0
        /// <summary>Show the view3D context menu</summary>
        public void ShowContextMenu()
        {
            if (Window == null)
            {
                return;
            }
            var context_menu = new ContextMenuStrip();

            context_menu.Closed += (s, a) => Refresh();

            {            // View
                var view_menu = context_menu.Items.Add2(new ToolStripMenuItem("View")
                {
                    Name = CMenuItems.View
                });
                {                // Show focus
                    var opt = view_menu.DropDownItems.Add2(new ToolStripMenuItem("Show Focus")
                    {
                        Name = CMenuItems.ViewMenu.ShowFocus
                    });
                    opt.Checked = Window.FocusPointVisible;
                    opt.Click  += (s, a) =>
                    {
                        Window.FocusPointVisible = !Window.FocusPointVisible;
                        Refresh();
                    };
                }
                {                // Show Origin
                    var opt = view_menu.DropDownItems.Add2(new ToolStripMenuItem("Show Origin")
                    {
                        Name = CMenuItems.ViewMenu.ShowOrigin
                    });
                    opt.Checked = Window.OriginPointVisible;
                    opt.Click  += (s, a) =>
                    {
                        Window.OriginPointVisible = !Window.OriginPointVisible;
                        Refresh();
                    };
                }
                {                // Show coords
                }
                {                // Axis Views
                    var opt = view_menu.DropDownItems.Add2(new ToolStripComboBox("Views")
                    {
                        Name = CMenuItems.ViewMenu.Views, DropDownStyle = ComboBoxStyle.DropDownList
                    });
                    opt.Items.Add("Views");
                    opt.Items.Add("Axis +X");
                    opt.Items.Add("Axis -X");
                    opt.Items.Add("Axis +Y");
                    opt.Items.Add("Axis -Y");
                    opt.Items.Add("Axis +Z");
                    opt.Items.Add("Axis -Z");
                    opt.Items.Add("Axis -X,-Y,-Z");
                    opt.SelectedIndex         = 0;
                    opt.SelectedIndexChanged += (s, a) =>
                    {
                        var pos = Camera.FocusPoint;
                        switch (opt.SelectedIndex)
                        {
                        case 1: Camera.ResetView(v4.XAxis); break;

                        case 2: Camera.ResetView(-v4.XAxis); break;

                        case 3: Camera.ResetView(v4.YAxis); break;

                        case 4: Camera.ResetView(-v4.YAxis); break;

                        case 5: Camera.ResetView(v4.ZAxis); break;

                        case 6: Camera.ResetView(-v4.ZAxis); break;

                        case 7: Camera.ResetView(-v4.XAxis - v4.YAxis - v4.ZAxis); break;
                        }
                        Camera.FocusPoint = pos;
                        Refresh();
                    };
                }
                {                // Object Manager UI
                    //var obj_mgr_ui = new ToolStripMenuItem("Object Manager");
                    //view_menu.DropDownItems.Add(obj_mgr_ui);
                    //obj_mgr_ui.Click += (s,a) => Window.ShowObjectManager(true);
                }
            }
            {            // Navigation
                var rdr_menu = context_menu.Items.Add2(new ToolStripMenuItem("Navigation")
                {
                    Name = CMenuItems.Navigation
                });
                {                // Reset View
                    var opt = rdr_menu.DropDownItems.Add2(new ToolStripMenuItem("Reset View")
                    {
                        Name = CMenuItems.NavMenu.ResetView
                    });
                    opt.Click += (s, a) =>
                    {
                        Camera.ResetView();
                        Refresh();
                    };
                }
                {                // Align to
                    var align_menu = rdr_menu.DropDownItems.Add2(new ToolStripMenuItem("Align")
                    {
                        Name = CMenuItems.NavMenu.Align
                    });
                    {
                        var opt = align_menu.DropDownItems.Add2(new ToolStripComboBox("Aligns")
                        {
                            Name = CMenuItems.NavMenu.AlignMenu.Aligns, DropDownStyle = ComboBoxStyle.DropDownList
                        });
                        opt.Items.Add("None");
                        opt.Items.Add("X");
                        opt.Items.Add("Y");
                        opt.Items.Add("Z");

                        var axis = Camera.AlignAxis;
                        if (Math_.FEql(axis, v4.XAxis))
                        {
                            opt.SelectedIndex = 1;
                        }
                        else if (Math_.FEql(axis, v4.YAxis))
                        {
                            opt.SelectedIndex = 2;
                        }
                        else if (Math_.FEql(axis, v4.ZAxis))
                        {
                            opt.SelectedIndex = 3;
                        }
                        else
                        {
                            opt.SelectedIndex = 0;
                        }
                        opt.SelectedIndexChanged += (s, a) =>
                        {
                            switch (opt.SelectedIndex)
                            {
                            default: Camera.AlignAxis = v4.Zero;  break;

                            case 1:  Camera.AlignAxis = v4.XAxis; break;

                            case 2:  Camera.AlignAxis = v4.YAxis; break;

                            case 3:  Camera.AlignAxis = v4.ZAxis; break;
                            }
                            Refresh();
                        };
                    }
                }
                {                // Motion lock
                }
                {                // Orbit
                    //var orbit_menu = new ToolStripMenuItem("Orbit");
                    //rdr_menu.DropDownItems.Add(orbit_menu);
                    //orbit_menu.Click += delegate {};
                }
            }
            {            // Tools
                var tools_menu = context_menu.Items.Add2(new ToolStripMenuItem("Tools")
                {
                    Name = CMenuItems.Tools
                });
                {                // Measure
                    var opt = tools_menu.DropDownItems.Add2(new ToolStripMenuItem("Measure...")
                    {
                        Name = CMenuItems.ToolsMenu.Measure
                    });
                    opt.Click += (s, a) =>
                    {
                        ShowMeasurementUI = true;
                    };
                }
                {                // Angle
                    var opt = tools_menu.DropDownItems.Add2(new ToolStripMenuItem("Angle...")
                    {
                        Name = CMenuItems.ToolsMenu.Angle
                    });
                    opt.Click += (s, a) =>
                    {
                        Window.ShowAngleTool = true;
                    };
                }
            }
            {            // Rendering
                var rdr_menu = context_menu.Items.Add2(new ToolStripMenuItem("Rendering")
                {
                    Name = CMenuItems.Rendering
                });
                {                // Solid/Wireframe/Solid+Wire
                    var opt = rdr_menu.DropDownItems.Add2(new ToolStripComboBox {
                        Name = CMenuItems.RenderingMenu.FillMode, DropDownStyle = ComboBoxStyle.DropDownList
                    });
                    opt.Items.AddRange(Enum <View3d.EFillMode> .Names.Cast <object>().ToArray());
                    opt.SelectedIndex         = (int)Window.FillMode;
                    opt.SelectedIndexChanged += (s, a) =>
                    {
                        Window.FillMode = (View3d.EFillMode)opt.SelectedIndex;
                        Refresh();
                    };
                }
                {                // Render2D
                    var opt = rdr_menu.DropDownItems.Add2(new ToolStripMenuItem(Window.Camera.Orthographic ? "Perspective" : "Orthographic")
                    {
                        Name = CMenuItems.RenderingMenu.Orthographic
                    });
                    opt.Click += (s, a) =>
                    {
                        var _2d = Window.Camera.Orthographic;
                        Window.Camera.Orthographic = !_2d;
                        opt.Text = _2d ? "Perspective" : "Orthographic";
                        Refresh();
                    };
                }
                {                // Lighting...
                    var opt = rdr_menu.DropDownItems.Add2(new ToolStripMenuItem("Lighting...")
                    {
                        Name = CMenuItems.RenderingMenu.Lighting
                    });
                    opt.Click += (s, a) =>
                    {
                        Window.ShowLightingDlg();
                    };
                }
                {                // Background colour
                    var bk_colour_menu = rdr_menu.DropDownItems.Add2(new ToolStripMenuItem("Background Colour")
                    {
                        Name = CMenuItems.RenderingMenu.Background
                    });
                    var opt = bk_colour_menu.DropDownItems.Add2(new ToolStripButton(" "));
                    opt.AutoToolTip = false;
                    opt.BackColor   = Window.BackgroundColour;
                    opt.Click      += (s, a) =>
                    {
                        var cd = new ColourUI();
                        if (cd.ShowDialog() == DialogResult.OK)
                        {
                            opt.BackColor = cd.Colour;
                        }
                    };
                    opt.BackColorChanged += (s, a) =>
                    {
                        Window.BackgroundColour = opt.BackColor;
                        Refresh();
                    };
                }
            }

            // Allow users to add custom menu options to the context menu
            // Do this last so that users have the option of removing options they don't want displayed
            OnCustomiseContextMenu(new CustomContextMenuEventArgs(context_menu));

            context_menu.Show(MousePosition);
        }
示例#7
0
        protected override void OnPaint(PaintEventArgs e)
        {
            var gfx = e.Graphics;
            var dim = LayoutDimensions;

            if (dim.Empty)
            {
                base.OnPaint(e);
                return;
            }

            // Draw the wheel
            if ((Parts & EParts.Wheel) != 0)
            {
                var bm = WheelBitmap;
                gfx.DrawImageUnscaled(bm, 0, 0);

                // Draw the colour selection
                if ((Parts & EParts.ColourSelection) != 0)
                {
                    var pt = WheelPoint(HSVColour);
                    gfx.DrawEllipse(Pens.Black, pt.X - 2f, pt.Y - 2f, 4f, 4f);
                }
            }

            gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

            // Draw the VSlider
            if ((Parts & EParts.VSlider) != 0)
            {
                gfx.DrawString(ValueLabel, SystemFonts.DefaultFont, Brushes.Black, dim.VLabel);
                if (!dim.VSlider.IsEmpty)
                {
                    using (var b = new LinearGradientBrush(
                               VerticalLayout ? dim.VSlider.LeftCentre()  : dim.VSlider.BottomCentre(),
                               VerticalLayout ? dim.VSlider.RightCentre() : dim.VSlider.TopCentre(),
                               Color.Black, Color.White))
                        gfx.FillRectangle(b, dim.VSlider);

                    gfx.DrawRectangle(Pens.Black, dim.VSlider);
                }

                // Draw the brightness selection
                if ((Parts & EParts.VSelection) != 0)
                {
                    var v = VerticalLayout
                                                ? Math_.Lerp(dim.VSlider.Left, dim.VSlider.Right, (double)HSVColour.V)
                                                : Math_.Lerp(dim.VSlider.Bottom, dim.VSlider.Top, (double)HSVColour.V);
                    var pts = SliderSelector(v, dim.VSlider, VerticalLayout);
                    gfx.DrawLines(Pens.Black, pts);
                }
            }

            // Draw the ASlider
            if ((Parts & EParts.ASlider) != 0)
            {
                gfx.DrawString(AlphaLabel, SystemFonts.DefaultFont, Brushes.Black, dim.ALabel);
                if (!dim.ASlider.IsEmpty)
                {
                    using (var b = new LinearGradientBrush(
                               VerticalLayout ? dim.ASlider.LeftCentre()  : dim.ASlider.BottomCentre(),
                               VerticalLayout ? dim.ASlider.RightCentre() : dim.ASlider.TopCentre(),
                               Color.Black, Color.White))
                        gfx.FillRectangle(b, dim.ASlider);

                    gfx.DrawRectangle(Pens.Black, dim.ASlider);
                }

                // Draw the alpha selection
                if ((Parts & EParts.ASelection) != 0)
                {
                    var v = VerticalLayout
                                                ? Math_.Lerp(dim.ASlider.Left, dim.ASlider.Right, (double)HSVColour.A)
                                                : Math_.Lerp(dim.ASlider.Bottom, dim.ASlider.Top, (double)HSVColour.A);
                    var pts = SliderSelector(v, dim.ASlider, VerticalLayout);
                    gfx.DrawLines(Pens.Black, pts);
                }
            }
        }
示例#8
0
 public static string Vec4(v4 vec)
 {
     Debug.Assert(Math_.IsFinite(vec));
     return($"{vec.x} {vec.y} {vec.z} {vec.w}");
 }
示例#9
0
 public static string Mat4x4(m4x4 mat)
 {
     Debug.Assert(Math_.IsFinite(mat));
     return($"{Vec4(mat.x)} {Vec4(mat.y)} {Vec4(mat.z)} {Vec4(mat.w)}");
 }
示例#10
0
 public static float Length2Sq(this SizeF s)
 {
     return(Math_.Sqr(s.Width) + Math_.Sqr(s.Height));
 }
示例#11
0
 public static float Length2(this SizeF s)
 {
     return(Math_.Sqrt(s.Length2Sq()));
 }
示例#12
0
 public static float Area(this SizeF s)
 {
     return(Math_.SignF(s.Width >= 0 && s.Height >= 0) * Math.Abs(s.Width * s.Height));
 }
示例#13
0
 /// <summary>Returns the signed area. Returns a negative value if Width and/or Height are negative</summary>
 public static int Area(this Size s)
 {
     return(Math_.SignI(s.Width >= 0 && s.Height >= 0) * Math.Abs(s.Width * s.Height));
 }
示例#14
0
 public static float Length2(this PointF p)
 {
     return(Math_.Sqrt(p.Length2Sq()));
 }
示例#15
0
        /// <summary>
        /// Determine if executing trades in 'loop' should result in a profit.
        /// Returns true if profitable and a copy of this loop in 'loop'</summary>
        public bool IsProfitable(bool forward, Fund fund, out Loop loop)
        {
            // How to think about this:
            // - We want to see what happens if we convert some currency to each of the coins
            //   in the loop, ending up back at the initial currency. If the result is more than
            //   we started with, then it's a profitable loop.
            // - We can go in either direction around the loop.
            // - We want to execute each trade around a profitable loop at the same time, so we're
            //   limited to the smallest balance for the coins in the loop.
            // - The rate by volume does not depend on our account balance. We calculate the effective
            //   rate at each of the offered volumes then determine if any of those volumes are profitable
            //   and whether we have enough balance for the given volumes.
            // - The 'Bid' table contains the amounts of base currency people want to buy, ordered by price.
            // - The 'Ask' table contains the amounts of base currency people want to sell, ordered by price.
            loop = null;

            // Construct an "order book" of volumes and complete-loop prices (e.g. BTC to BTC price for each volume)
            var dir  = forward ? +1 : -1;
            var coin = forward ? Beg : End;
            var tt   = forward ? ETradeType.B2Q : ETradeType.Q2B;
            var obk  = new OrderBook(coin, coin, tt)
            {
                new Offer(1m, decimal.MaxValue._(coin.Symbol))
            };

            foreach (var pair in EnumPairs(dir))
            {
                // Limit the volume calculated, there's no point in calculating large volumes if we can't trade them
                Unit <decimal> bal = 0m;
                OrderBook      b2q = null, q2b = null;
                using (Task_.NoSyncContext())
                {
                    Misc.RunOnMainThread(() =>
                    {
                        bal = coin.Balances[fund].Available;
                        b2q = new OrderBook(pair.MarketDepth.B2Q);
                        q2b = new OrderBook(pair.MarketDepth.Q2B);
                    }).Wait();
                }

                // Note: the trade prices are in quote currency
                if (pair.Base == coin)
                {
                    obk = MergeRates(obk, b2q, bal, invert: false);
                }
                else if (pair.Quote == coin)
                {
                    obk = MergeRates(obk, q2b, bal, invert: true);
                }
                else
                {
                    throw new Exception($"Pair {pair} does not include Coin {coin}. Loop is invalid.");
                }

                // Get the next coin in the loop
                coin = pair.OtherCoin(coin);
            }
            if (obk.Count == 0)
            {
                return(false);
            }

            // Save the best profit ratio for this loop (as an indication)
            if (forward)
            {
                ProfitRatioFwd = obk[0].PriceQ2B;
            }
            else
            {
                ProfitRatioBck = obk[0].PriceQ2B;
            }

            // Look for any volumes that have a nett gain
            var amount_gain = obk.Where(x => x.PriceQ2B > 1).Sum(x => x.PriceQ2B * x.AmountBase);

            if (amount_gain == 0)
            {
                return(false);
            }

            // Create a copy of the loop for editing (with the direction set)
            loop = new Loop(this, obk, dir);

            // Find the maximum profitable volume to trade
            var amount = 0m._(loop.Beg);

            foreach (var ordr in loop.Rate.Where(x => x.PriceQ2B > 1))
            {
                amount += ordr.AmountBase;
            }

            // Calculate the effective fee in initial coin currency.
            // Do all trades assuming no fee, but accumulate the fee separately
            var fee            = 0m._(loop.Beg);
            var initial_volume = amount;

            // Trade each pair in the loop (in the given direction) to check
            // that the trade is still profitable after fees. Record each trade
            // so that we can determine the trade scale
            coin = loop.Beg;
            var trades = new List <Trade>();

            foreach (var pair in loop.EnumPairs(loop.Direction))
            {
                // If we trade 'volume' using 'pair' that will result in a new volume
                // in the new currency. There will also be a fee charged (in quote currency).
                // If we're trading to quote currency, the new volume is reduced by the fee.
                // If we're trading to base currency, the cost is increased by the fee.

                // Calculate the result of the trade
                var new_coin = pair.OtherCoin(coin);
                var trade    = pair.Base == coin
                                        ? pair.BaseToQuote(fund, amount)
                                        : pair.QuoteToBase(fund, amount);

                // Record the trade amount.
                trades.Add(trade);

                // Convert the fee so far to the new coin using the effective rate,
                // and add on the fee for this trade.
                var rate = trade.AmountOut / trade.AmountIn;
                fee = fee * rate + trade.AmountOut * pair.Fee;

                // Advance to the next pair
                coin   = new_coin;
                amount = trade.AmountOut;
            }

            // Record the volume to trade, the scale, and the expected profit.
            // If the new volume is greater than the initial volume, WIN!
            // Update the profitability of the loop now we've accounted for fees.
            loop.TradeScale   = 1m;
            loop.Tradeability = string.Empty;
            loop.TradeVolume  = initial_volume;
            loop.Profit       = (amount - fee) - initial_volume;
            if (forward)
            {
                loop.ProfitRatioFwd = ProfitRatioFwd = (amount - fee) / initial_volume;
            }
            else
            {
                loop.ProfitRatioBck = ProfitRatioBck = (amount - fee) / initial_volume;
            }
            if (loop.ProfitRatio <= 1m)
            {
                return(false);
            }

            // Determine the trade scale based on the available balances
            foreach (var trade in trades)
            {
                var pair = trade.Pair;

                // Get the balance available for this trade and determine a trade scaling factor.
                // Increase the required volume to allow for the fee
                // Reduce the available balance slightly to deal with rounding errors
                var bal   = trade.CoinIn.Balances[fund].Available * 0.999m;
                var req   = trade.AmountIn * (1 + pair.Fee);
                var scale = Math_.Clamp((decimal)(bal / req), 0m, 1m);
                if (scale < loop.TradeScale)
                {
                    loop.TradeScale   = Math_.Clamp(scale, 0, loop.TradeScale);
                    loop.LimitingCoin = trade.CoinIn;
                }
            }

            // Check that all traded volumes are within the limits
            var all_trades_valid = EValidation.Valid;

            foreach (var trade in trades)
            {
                // Check the unscaled amount, if that's too small we'll ignore this loop
                var validation0 = trade.Validate();
                all_trades_valid |= validation0;

                // Record why the base trade isn't valid
                if (validation0 != EValidation.Valid)
                {
                    loop.Tradeability += $"{trade.Description} - {validation0}\n";
                }

                // If the volume to trade, multiplied by the trade scale, is outside the
                // allowed range of trading volume, set the scale to zero. This is to prevent
                // loops being traded where part of the loop would be rejected.
                var validation1 = new Trade(trade, loop.TradeScale).Validate();
                if (validation1.HasFlag(EValidation.AmountInOutOfRange))
                {
                    loop.Tradeability += $"Not enough {trade.CoinIn} to trade\n";
                }
                if (validation1.HasFlag(EValidation.AmountOutOutOfRange))
                {
                    loop.Tradeability += $"Trade result volume of {trade.CoinOut} is too small\n";
                }
                if (validation1 != EValidation.Valid)
                {
                    loop.TradeScale = 0m;
                }
            }

            // Return the profitable loop (even if scaled to 0)
            return(all_trades_valid == EValidation.Valid);
        }
示例#16
0
 public static bool FEqlRelative(Size lhs, Size rhs, double tol)
 {
     return
         (Math_.FEqlRelative(lhs.Width, rhs.Width, tol) &&
          Math_.FEqlRelative(lhs.Height, rhs.Height, tol));
 }
示例#17
0
        /// <summary>Set up the text box fields</summary>
        private void SetupFields()
        {
            // Validation
            CancelEventHandler Validating = (s, a) =>
            {
                a.Cancel = !byte.TryParse(((TextBox)s).Text, out var dummy);
            };

            m_edit_red.Validating   += Validating;
            m_edit_green.Validating += Validating;
            m_edit_blue.Validating  += Validating;
            m_edit_alpha.Validating += Validating;
            m_edit_hue.Validating   += Validating;
            m_edit_sat.Validating   += Validating;
            m_edit_lum.Validating   += Validating;

            // Accept value
            Action <byte?, byte?, byte?, byte?> SetARGB = (a, r, g, b) =>
            {
                var c = m_wheel.Colour;
                m_wheel.Colour = Color.FromArgb(a ?? c.A, r ?? c.R, g ?? c.G, b ?? c.B);
            };
            Action <float?, float?, float?, float?> SetAHSV = (a, h, s, v) =>
            {
                var c = m_wheel.HSVColour;
                if (a.HasValue)
                {
                    a = Math_.Clamp(a.Value / 255f, 0f, 1f);
                }
                if (h.HasValue)
                {
                    h = Math_.Clamp(h.Value / 255f, 0f, 1f);
                }
                if (s.HasValue)
                {
                    s = Math_.Clamp(s.Value / 255f, 0f, 1f);
                }
                if (v.HasValue)
                {
                    v = Math_.Clamp(v.Value / 255f, 0f, 1f);
                }
                m_wheel.HSVColour = HSV.FromAHSV(a ?? c.A, h ?? c.H, s ?? c.S, v ?? c.V);
            };

            m_edit_alpha.Validated += (s, a) => SetARGB(byte.Parse(((TextBox)s).Text), null, null, null);
            m_edit_red.Validated   += (s, a) => SetARGB(null, byte.Parse(((TextBox)s).Text), null, null);
            m_edit_green.Validated += (s, a) => SetARGB(null, null, byte.Parse(((TextBox)s).Text), null);
            m_edit_blue.Validated  += (s, a) => SetARGB(null, null, null, byte.Parse(((TextBox)s).Text));
            m_edit_hue.Validated   += (s, a) => SetAHSV(null, byte.Parse(((TextBox)s).Text), null, null);
            m_edit_sat.Validated   += (s, a) => SetAHSV(null, null, byte.Parse(((TextBox)s).Text), null);
            m_edit_lum.Validated   += (s, a) => SetAHSV(null, null, null, byte.Parse(((TextBox)s).Text));

            m_edit_hex.Validating += (s, a) =>
            {
                a.Cancel = !uint.TryParse(((TextBox)s).Text, NumberStyles.HexNumber, null, out var dummy);
            };
            m_edit_hex.Validated += (s, a) =>
            {
                var argb = uint.Parse(((TextBox)s).Text, NumberStyles.HexNumber);
                unchecked { m_wheel.Colour = Color.FromArgb((int)argb); }
            };
        }
示例#18
0
 /// <summary>Compare sizes for approximate equality</summary>
 public static bool FEql(Point lhs, Point rhs)
 {
     return
         (Math_.FEql(lhs.X, rhs.X) &&
          Math_.FEql(lhs.Y, rhs.Y));
 }
示例#19
0
 /// <summary>The distance from x1,y1 to x2,y2</summary>
 public static double Distance(double x1, double y1, double x2, double y2)
 {
     return(Math_.Sqrt(Math_.Sqr(x2 - x1) + Math_.Sqr(y2 - y1)));
 }
示例#20
0
 public static bool FEqlRelative(Point lhs, Point rhs, double tol)
 {
     return
         (Math_.FEqlRelative(lhs.X, rhs.X, tol) &&
          Math_.FEqlRelative(lhs.Y, rhs.Y, tol));
 }
示例#21
0
        /// <summary>Update the derived fields from the given spec</summary>
        public void UseSpec(ShipSpec spec)
        {
            Spec = spec;

            // Determine the size and mass of the ship
            FuelMass     = 0;
            FuelVolume   = 0;
            FuelTankMass = 0;
            double tank_volume = 0;

            foreach (var f in spec.Fuel)
            {
                double v;
                FuelMass     += f.Mass;
                FuelVolume   += v = f.Volume(Consts, World);
                FuelTankMass += f.TankMass(v, World);
                tank_volume  += f.TankAndFuelVolume(v, World);
            }

            PassengerMass = Spec.PassengerCount * Consts.AveragePassengerWeight;
            double passenger_volume = Spec.PassengerCount * Consts.AveragePassengerPersonalSpace;

            // Assume the ship is a spherical ball housing the passengers only
            double xs_area = Consts.CabinPressure / Spec.HullCompound.Strength;

            ;                       double ship_inner = Math_.SphereRadius(passenger_volume);
            double ship_outer  = Math_.Sqrt(2.0 * xs_area / Math_.Tau + Math_.Sqr(ship_inner));
            var    hull_volume = (2.0 / 3.0) * Math_.Tau * (Math_.Cubed(ship_outer) - Math_.Cubed(ship_inner));

            HullMass         = hull_volume * Spec.HullCompound.Density(World.AverageLocalTemperature, 0.0);
            TotalVolume      = hull_volume + tank_volume;
            TotalInitialMass = HullMass + FuelTankMass + PassengerMass;

            // Construction time is a function of how big the ship is
            ConstructionTime = TotalVolume / Consts.ShipConstructionRate;
        }
示例#22
0
 /// <summary>Compare sizes for approximate equality</summary>
 public static bool FEql(Vector lhs, Vector rhs)
 {
     return
         (Math_.FEql(lhs.X, rhs.X) &&
          Math_.FEql(lhs.Y, rhs.Y));
 }
示例#23
0
        /// <summary>Round parameters to match the server rules</summary>
        public OrderParams Canonicalise(CurrencyPair pair, BinanceApi api)
        {
            // Canonicalise doesn't throw, it just does it's best.
            // Use Validate to get error messages.

            // Find the rules for 'cp'. Valid if no rules found
            var rules = api.SymbolRules[pair];

            if (rules == null)
            {
                return(this);
            }

            var ticker = api.TickerData[pair];

            if (Type == EOrderType.MARKET)
            {
                PriceQ2B = ticker.PriceQ2B;
            }

            if (!Type.IsAlgo())
            {
                StopPriceQ2B = null;
            }
            else if (StopPriceQ2B == null)
            {
                StopPriceQ2B = PriceQ2B;
            }

            // Truncate to the expected precision. Can't round because we might round to a value greater than the balance
            AmountBase = Math_.Truncate(AmountBase, rules.BaseAssetPrecision);
            if (PriceQ2B != null)
            {
                PriceQ2B = Math_.Truncate(PriceQ2B.Value, rules.PricePrecision);
            }
            if (StopPriceQ2B != null)
            {
                StopPriceQ2B = Math_.Truncate(StopPriceQ2B.Value, rules.PricePrecision);
            }
            if (IcebergAmountBase != null)
            {
                IcebergAmountBase = Math_.Truncate(IcebergAmountBase.Value, rules.BaseAssetPrecision);
            }

            // Round to the tick size
            foreach (var filter in rules.Filters.OfType <ServerRulesData.FilterPrice>().Where(x => x.FilterType == EFilterType.PRICE_FILTER))
            {
                if (PriceQ2B != null)
                {
                    PriceQ2B = filter.Round(PriceQ2B.Value);
                }
                if (StopPriceQ2B != null)
                {
                    StopPriceQ2B = filter.Round(StopPriceQ2B.Value);
                }
            }

            // Round to the lot size
            var filter_type = Type != EOrderType.MARKET ? EFilterType.LOT_SIZE : EFilterType.MARKET_LOT_SIZE;

            foreach (var filter in rules.Filters.OfType <ServerRulesData.FilterLotSize>().Where(x => x.FilterType == filter_type))
            {
                AmountBase = filter.Round(AmountBase);
                if (IcebergAmountBase != null)
                {
                    IcebergAmountBase = filter.Round(IcebergAmountBase.Value);
                }
            }

            // Test against min notional
            foreach (var filter in rules.Filters.OfType <ServerRulesData.FilterMinNotional>().Where(x => x.FilterType == EFilterType.MIN_NOTIONAL))
            {
                if (Type == EOrderType.MARKET && !filter.ApplyToMarketOrders)
                {
                    continue;
                }

                AmountBase = filter.Round(AmountBase, PriceQ2B.Value);
            }

            return(this);
        }
示例#24
0
 public static bool FEqlRelative(Vector lhs, Vector rhs, double tol)
 {
     return
         (Math_.FEqlRelative(lhs.X, rhs.X, tol) &&
          Math_.FEqlRelative(lhs.Y, rhs.Y, tol));
 }
示例#25
0
 /// <summary>Returns the volume of the tank including the fuel</summary>
 public double TankAndFuelVolume(double fuel_volume, WorldState world)
 {
     return(Math_.SphereRadius(fuel_volume) + TankWallThickness(fuel_volume));
 }
示例#26
0
 /// <summary>Compare sizes for approximate equality</summary>
 public static bool FEql(Size lhs, Size rhs)
 {
     return
         (Math_.FEql(lhs.Width, rhs.Width) &&
          Math_.FEql(lhs.Height, rhs.Height));
 }
示例#27
0
        public GameConstants(int seed, bool real_chemistry)
        {
            GameSeed     = seed;
            ElementCount = ElementNames.Length;

            // Universal constants
            MaxGameDuration                = 30 * 60 * 60;             // 30 minutes
            InitialTimeTillNova            = 365 * 24 * 60 * 60;
            InitialTimeTillNovaErrorMargin = 20 * 24 * 60 * 60;
            m_time_scaler            = InitialTimeTillNova / MaxGameDuration;
            m_speed_of_light         = 2.99792458e8;
            m_gravitational_constant = 6.6738e-11;
            CoulombConstant          = 8.987551e9;
            ElectronCharge           = 1.60217657e-19;
            ProtonMass              = 1.67262178e-27;
            GasConstant             = 8.3144621;
            MaxMolarMass            = 2.0 * ElementCount;
            MinElectronegativity    = 0.7;
            MaxElectronegativity    = 4.0;
            MinSolidMaterialDensity = 350.0;
            MaxSolidMaterialDensity = 25000.0;

            var rnd = new Random(GameSeed);

            ValenceLevels = new int[10];
            if (real_chemistry)
            {
                StableShellCount = 8;

                // The total numbers of electrons at each orbital level
                ValenceLevels[0] = 0;
                ValenceLevels[1] = 2;
                ValenceLevels[2] = 10;
                ValenceLevels[3] = 18;
                ValenceLevels[4] = 36;
                ValenceLevels[5] = 54;
                ValenceLevels[6] = 86;
                ValenceLevels[7] = 118;
            }
            else
            {
                StableShellCount = rnd.Next(6, 11);                // [6,10]

                // The total numbers of electrons at each orbital level
                ValenceLevels[0] = 0;
                ValenceLevels[1] = rnd.Next(1, 4);
                for (int i = 2; i != ValenceLevels.Length; ++i)
                {
                    int v = 1 + ValenceLevels[i - 1];
                    ValenceLevels[i] = (int)rnd.Double(1.3 * v, 2.9 * v);
                }
            }

            OrbitalRadii    = new Range[ValenceLevels.Length];
            OrbitalRadii[0] = new Range(0.0, 0.0);
            OrbitalRadii[1] = new Range(31.0, 53.0);
            OrbitalRadii[2] = new Range(38.0, 167.0);
            OrbitalRadii[3] = new Range(71.0, 190.0);
            OrbitalRadii[4] = new Range(88.0, 243.0);
            OrbitalRadii[5] = new Range(108.0, 265.0);
            OrbitalRadii[6] = new Range(120.0, 298.0);
            OrbitalRadii[7] = new Range(132.0, 341.0);
            OrbitalRadii[8] = new Range(140.0, 390.0);
            OrbitalRadii[9] = new Range(144.0, 450.0);

            // Pick a star mass approximately the same as the sun
            const double suns_mass = 2.0e30;

            StarMass = rnd.DoubleC(suns_mass, suns_mass * 0.25);

            // Pick a distance from the star, somewhere between mercury and mars
            const double sun_to_mercury = 5.79e10;
            const double sun_to_mars    = 2.279e11;

            StarDistance = rnd.Double(sun_to_mercury, sun_to_mars);

            // The acceleration due to the star's gravity at the given distance
            m_star_gravitational_acceleration = m_gravitational_constant * StarMass / Math_.Sqr(StarDistance);

            // Calculate the required escape velocity (speed)
            // Escape Velocity = Sqrt(2 * G * M / r), G = 6.67x10^-11 m³kg^-1s^-2, M = star mass, r = distance from star
            EscapeVelocity = Math_.Sqrt(2.0 * m_gravitational_constant * StarMass / StarDistance);

            // Set up per passenger constants
            AveragePassengerWeight        = rnd.DoubleC(80.0, 10.0);
            AveragePassengerPersonalSpace = rnd.DoubleC(2.0, 0.5);
            CabinPressure = 1000;

            // The total number of people available to work
            m_total_man_power = rnd.IntC(10000, 0);

            // The ship is roughly 10% bigger than the volume of it's contents
            ShipVolumeScaler     = rnd.DoubleC(1.11, 0.0);
            ShipConstructionRate = rnd.DoubleC(0.1, 0.0);

            // The total man days needed to discover the star mass
            m_star_mass_discovery_effort = rnd.DoubleC(1000, 0.0);

            // The rate at which the star distance can be discovered proportional to the main hours assigned
            m_star_distance_discovery_effort = rnd.DoubleC(1000, 0.0);
        }
示例#28
0
        /// <summary>Generates the boundary of the hint balloon</summary>
        private GraphicsPath GeneratePath(bool region_border)
        {
            var width  = Width + (region_border ? 1 : 0);
            var height = Height + (region_border ? 1 : 0);
            var cr     = Math.Max(1, CornerRadius);

            GraphicsPath path;

            if (TipLength == 0 || TipBaseWidth == 0)
            {
                path = Gdi.RoundedRectanglePath(new RectangleF(0, 0, width, height), cr);
            }
            else
            {
                var tip_length = TipLength;
                var tip_width  = Math_.Clamp(width - 2 * (tip_length + cr), Math.Min(5, TipBaseWidth), TipBaseWidth);

                // Find the corner to start from
                path = new GraphicsPath();
                switch (TipCorner)
                {
                default:
                    Debug.Assert(false, "Unknown corner");
                    break;

                case ETipCorner.TopLeft:
                    path.AddLine(0, 0, tip_length + cr + tip_width, tip_length);
                    path.AddArc(width - tip_length - cr, tip_length, cr, cr, 270f, 90f);
                    path.AddArc(width - tip_length - cr, height - tip_length - cr, cr, cr, 0f, 90f);
                    path.AddArc(tip_length, height - tip_length - cr, cr, cr, 90f, 90f);
                    path.AddArc(tip_length, tip_length, cr, cr, 180f, 90f);
                    path.AddLine(tip_length + cr, tip_length, 0, 0);
                    break;

                case ETipCorner.TopRight:
                    path.AddLine(width, 0, width - tip_length - cr, tip_length);
                    path.AddArc(width - tip_length - cr, tip_length, cr, cr, 270f, 90f);
                    path.AddArc(width - tip_length - cr, height - tip_length - cr, cr, cr, 0f, 90f);
                    path.AddArc(tip_length, height - tip_length - cr, cr, cr, 90f, 90f);
                    path.AddArc(tip_length, tip_length, cr, cr, 180f, 90f);
                    path.AddLine(tip_length + cr, tip_length, width - tip_length - cr - tip_width, tip_length);
                    path.AddLine(width - tip_length - cr - tip_width, tip_length, width, 0);
                    break;

                case ETipCorner.BottomLeft:
                    path.AddLine(0, height, tip_length + cr, height - tip_length);
                    path.AddArc(tip_length, height - tip_length - cr, cr, cr, 90f, 90f);
                    path.AddArc(tip_length, tip_length, cr, cr, 180f, 90f);
                    path.AddArc(width - tip_length - cr, tip_length, cr, cr, 270f, 90f);
                    path.AddArc(width - tip_length - cr, height - tip_length - cr, cr, cr, 0f, 90f);
                    path.AddLine(width - tip_length - cr, height - tip_length, tip_length + cr + tip_width, height - tip_length);
                    path.AddLine(tip_length + cr + tip_width, height - tip_length, 0, height);
                    break;

                case ETipCorner.BottomRight:
                    path.AddLine(width, height, width - tip_length - cr - tip_width, height - tip_length);
                    path.AddArc(tip_length, height - tip_length - cr, cr, cr, 90f, 90f);
                    path.AddArc(tip_length, tip_length, cr, cr, 180f, 90f);
                    path.AddArc(width - tip_length - cr, tip_length, cr, cr, 270f, 90f);
                    path.AddArc(width - tip_length - cr, height - tip_length - cr, cr, cr, 0f, 90f);
                    path.AddLine(width - tip_length - cr, height - tip_length, width, height);
                    break;
                }
            }
            return(path);
        }
示例#29
0
 public static string Vec3(v2 vec)
 {
     Debug.Assert(Math_.IsFinite(vec));
     return($"{vec.x} {vec.y} 0");
 }
示例#30
0
        /// <summary>The grunt work of building the new line index.</summary>
        private static void BuildLineIndexAsync(BLIData d, Action <BLIData, RangeI, List <RangeI>, Exception> on_complete)
        {
            // This method runs in a background thread
            // All we're doing here is loading data around 'd.filepos' so that there are an equal number
            // of lines on either side. This can be optimised however because the existing range of
            // cached data probably overlaps the range we want loaded.
            try
            {
                Log.Write(ELogLevel.Info, "BLIAsync", $"build started. (id {d.build_issue}, reload {d.reload})");
                if (BuildCancelled(d.build_issue))
                {
                    return;
                }
                using (d.file)
                {
                    // A temporary buffer for reading sections of the file
                    var buf = new byte[d.max_line_length];

                    // Seek to the first line that starts immediately before 'filepos'
                    d.filepos = FindLineStart(d.file, d.filepos, d.fileend, d.row_delim, d.encoding, buf);
                    if (BuildCancelled(d.build_issue))
                    {
                        return;
                    }

                    // Determine the range to scan and the number of lines in each direction
                    var scan_backward = (d.fileend - d.filepos) > (d.filepos - 0);                     // scan in the most bound direction first
                    var scan_range    = CalcBufferRange(d.filepos, d.fileend, d.file_buffer_size);
                    var line_range    = CalcLineRange(d.line_cache_count);
                    var bwd_lines     = line_range.Begi;
                    var fwd_lines     = line_range.Endi;

                    // Incremental loading - only load what isn't already cached.
                    // If the 'filepos' is left of the cache centre, try to extent in left direction first.
                    // If the scan range in that direction is empty, try extending at the other end. The
                    // aim is to try to get d.line_index_count as close to d.line_cache_count as possible
                    // without loading data that is already cached.
                    #region Incremental loading
                    if (!d.reload && !d.cached_whole_line_range.Empty)
                    {
                        // Determine the direction the cached range is moving based on where 'filepos' is relative
                        // to the current cache centre and which range contains an valid area to be scanned.
                        // With incremental scans we can only update one side of the cache because the returned line index has to
                        // be a contiguous block of lines. This means one of 'bwd_lines' or 'fwd_lines' must be zero.
                        var Lrange = new RangeI(scan_range.Beg, d.cached_whole_line_range.Beg);
                        var Rrange = new RangeI(d.cached_whole_line_range.End, scan_range.End);
                        var dir    =
                            (!Lrange.Empty && !Rrange.Empty) ? Math.Sign(2 * d.filepos_line_index - d.line_cache_count) :
                            (!Lrange.Empty) ? -1 :
                            (!Rrange.Empty) ? +1 :
                            0;

                        // Determine the number of lines to scan, based on direction
                        if (dir < 0)
                        {
                            scan_backward = true;
                            scan_range    = Lrange;
                            bwd_lines    -= Math_.Clamp(d.filepos_line_index - 0, 0, bwd_lines);
                            fwd_lines     = 0;
                        }
                        else if (dir > 0)
                        {
                            scan_backward = false;
                            scan_range    = Rrange;
                            bwd_lines     = 0;
                            fwd_lines    -= Math_.Clamp(d.line_index_count - d.filepos_line_index - 1, 0, fwd_lines);
                        }
                        else if (dir == 0)
                        {
                            bwd_lines  = 0;
                            fwd_lines  = 0;
                            scan_range = RangeI.Zero;
                        }
                    }
                    #endregion

                    Debug.Assert(bwd_lines + fwd_lines <= d.line_cache_count);

                    // Build the collection of line byte ranges to add to the cache
                    var line_index = new List <RangeI>();
                    if (bwd_lines != 0 || fwd_lines != 0)
                    {
                        // Line index buffers for collecting the results
                        var fwd_line_buf = new List <RangeI>();
                        var bwd_line_buf = new List <RangeI>();

                        // Data used in the 'add_line' callback. Updated for forward and backward passes
                        var lbd = new LineBufferData
                        {
                            line_buf   = null,                           // pointer to either 'fwd_line_buf' or 'bwd_line_buf'
                            line_limit = 0,                              // Caps the number of lines read for each of the forward and backward searches
                        };

                        // Callback for adding line byte ranges to a line buffer
                        AddLineFunc add_line = (line, baddr, fend, bf, enc) =>
                        {
                            if (line.Empty && d.ignore_blanks)
                            {
                                return(true);
                            }

                            // Test 'text' against each filter to see if it's included
                            // Note: not caching this string because we want to read immediate data
                            // from the file to pick up file changes.
                            string text = d.encoding.GetString(buf, (int)line.Beg, (int)line.Size);
                            if (!PassesFilters(text, d.filters))
                            {
                                return(true);
                            }

                            // Convert the byte range to a file range
                            line = line.Shift(baddr);
                            Debug.Assert(new RangeI(0, d.fileend).Contains(line));
                            lbd.line_buf.Add(line);
                            Debug.Assert(lbd.line_buf.Count <= lbd.line_limit);
                            return((fwd_line_buf.Count + bwd_line_buf.Count) < lbd.line_limit);
                        };

                        // Callback for updating progress
                        ProgressFunc progress = (scanned, length) =>
                        {
                            int numer = fwd_line_buf.Count + bwd_line_buf.Count, denom = lbd.line_limit;
                            return(d.progress(numer, denom) && !BuildCancelled(d.build_issue));
                        };

                        // Scan twice, starting in the direction of the smallest range so that any
                        // unused cache space is used by the search in the other direction
                        var scan_from = Math_.Clamp(d.filepos, scan_range.Beg, scan_range.End);
                        for (int a = 0; a != 2; ++a, scan_backward = !scan_backward)
                        {
                            if (BuildCancelled(d.build_issue))
                            {
                                return;
                            }

                            lbd.line_buf    = scan_backward ? bwd_line_buf : fwd_line_buf;
                            lbd.line_limit += scan_backward ? bwd_lines : fwd_lines;
                            if ((bwd_line_buf.Count + fwd_line_buf.Count) < lbd.line_limit)
                            {
                                var length = scan_backward ? scan_from - scan_range.Beg : scan_range.End - scan_from;
                                FindLines(d.file, scan_from, d.fileend, scan_backward, length, add_line, d.encoding, d.row_delim, buf, progress);
                            }
                        }

                        // Scanning backward adds lines to the line index in reverse order.
                        bwd_line_buf.Reverse();

                        // 'line_index' should be a contiguous block of byte offset ranges for
                        // the lines around 'd.filepos'. If 'd.reload' is false, then the line
                        // index will only contain byte offset ranges that are not currently cached.
                        line_index.Capacity = bwd_line_buf.Count + fwd_line_buf.Count;
                        line_index.AddRange(bwd_line_buf);
                        line_index.AddRange(fwd_line_buf);
                    }

                    // Job done
                    on_complete(d, scan_range, line_index, null);
                }
            }
            catch (Exception ex)
            {
                on_complete(d, RangeI.Zero, null, ex);
            }
        }