コード例 #1
0
        public bool ApplyMirroring()
        {
            if (m_settings.mirrorMode == MirrorMode.None)
            {
                return(false);
            }

            bool needsSetup = false;

            if (m_mirrorRelation == null || m_mirrorRelation.Length != m_points.Length)
            {
                m_mirrorRelation = new int[m_points.Length];
                needsSetup       = true;
            }
            else if (m_prevMirrorMode != m_settings.mirrorMode)
            {
                m_prevMirrorMode = m_settings.mirrorMode;
                needsSetup       = true;
            }

            Vector3 planeNormal = GetMirrorPlane(m_settings.mirrorMode);

            if (needsSetup)
            {
                if (npBuildMirroringRelation(m_points, m_normalsBase, m_points.Length, planeNormal, 0.001f, m_mirrorRelation) == 0)
                {
                    Debug.LogWarning("NormalEditor: this mesh seems not symmetric");
                    m_mirrorRelation      = null;
                    m_settings.mirrorMode = MirrorMode.None;
                    return(false);
                }
            }
            npApplyMirroring(m_mirrorRelation, m_normals.Length, planeNormal, m_normals);
            return(true);
        }
コード例 #2
0
        void PrepareMirror()
        {
            if (m_settings.mirrorMode == MirrorMode.None)
            {
                return;
            }

            bool needsSetup = false;

            if (m_mirrorRelation == null || m_mirrorRelation.Count != m_points.Count)
            {
                m_mirrorRelation = new PinnedList <int>(m_points.Count);
                needsSetup       = true;
            }
            if (m_prevMirrorMode != m_settings.mirrorMode)
            {
                m_prevMirrorMode = m_settings.mirrorMode;
                needsSetup       = true;
            }

            Vector3 planeNormal = GetMirrorPlane(m_settings.mirrorMode);

            if (needsSetup)
            {
                npMeshData tmp = m_npModelData;
                tmp.vertices = m_pointsBasePredeformed;
                tmp.normals  = m_normalsBasePredeformed;
                if (npBuildMirroringRelation(ref tmp, planeNormal, m_settings.mirrorEpsilon, m_mirrorRelation) == 0)
                {
                    Debug.LogWarning("This mesh seems not symmetric");
                    m_mirrorRelation      = null;
                    m_settings.mirrorMode = MirrorMode.None;
                }
            }
        }
コード例 #3
0
ファイル: Cartridge.cs プロジェクト: wensioness/EmuNes
        public Cartridge(byte[] programRom, byte[] characterRom, byte mapperId, MirrorMode mirrorMode)
        {
            ProgramRom   = programRom.ToArray();
            CharacterRom = characterRom.ToArray();
            MapperId     = mapperId;
            MirrorMode   = mirrorMode;

            SaveRam = new SaveRam();

            // compute CRC
            byte[] romBody = new byte[programRom.Length + characterRom.Length];
            Array.Copy(programRom, romBody, programRom.Length);
            Array.Copy(characterRom, 0, romBody, programRom.Length, characterRom.Length);
            Crc32 crc32 = new Crc32();

            Crc = crc32.ComputeChecksum(romBody.ToArray());

            if (DetermineMapperId != null)
            {
                MapperId = DetermineMapperId(Crc, MapperId);
            }

            DetermineCartridgeMap();
            Debug.WriteLine(ToString());
        }
コード例 #4
0
        protected ushort MirrorAddress(MirrorMode mirrorMode, ushort address)
        {
            address %= 0x1000;
            int table  = address / 0x0400;
            int offset = address % 0x0400;

            int nameTableIndex = ((int)mirrorMode >> (table * 2)) & 0x03;

            return((ushort)(nameTableIndex * 0x0400 + offset));
        }
コード例 #5
0
ファイル: UIFormManager.cs プロジェクト: icemile/CodeProject
 /// <summary>
 /// Mirror forms.
 /// </summary>
 /// <param name="mode">Mode of mirror.</param>
 public void MirrorForms(MirrorMode mode)
 {
     foreach (var forms in layerForms.Values)
     {
         foreach (var form in forms)
         {
             form.Mirror(mode);
         }
     }
 }
コード例 #6
0
ファイル: UIFormManager.cs プロジェクト: icemile/CodeProject
        /// <summary>
        /// Mirror forms.
        /// </summary>
        /// <param name="layer">Target layer.</param>
        /// <param name="mode">Mode of mirror.</param>
        public void MirrorForms(string layer, MirrorMode mode)
        {
            if (!layerForms.ContainsKey(layer))
            {
                LogUtility.LogWarning(0, "Mirror forms error: Can not find any form in the \"{0}\" layer.", layer);
                return;
            }

            foreach (var form in layerForms[layer])
            {
                form.Mirror(mode);
            }
        }
コード例 #7
0
        public override void Reset()
        {
            loadRegister      = 0;
            loadRegisterCount = 0;
            SetControlRegister(0x1C);

            mirrorMode = MirrorMode.Horizontal;

            prgBankSelectedLo = 0;
            prgBankSelectedHi = (byte)(prgBankCount - 1);

            chrBankSelectedLo = 0;
            chrBankSelectedHi = 0;
        }
コード例 #8
0
        /// <summary>
        /// Mirror RectTransform.
        /// </summary>
        /// <param name="rect">Target RectTransform.</param>
        /// <param name="mode">Mode of mirror.</param>
        public static void Mirror(RectTransform rect, MirrorMode mode)
        {
            var anchorPos = rect.anchoredPosition;
            var minX      = rect.anchorMin.x;
            var minY      = rect.anchorMin.y;
            var maxX      = rect.anchorMax.x;
            var maxY      = rect.anchorMax.y;

            var pivotX = rect.pivot.x;
            var pivotY = rect.pivot.y;

            var h = 1;
            var v = 1;

            switch (mode)
            {
            case MirrorMode.Horizontal:
                minX   = 1 - minX;
                maxX   = 1 - maxX;
                pivotX = 1 - pivotX;
                h      = -1;
                break;

            case MirrorMode.Vertical:
                minY   = 1 - minY;
                maxY   = 1 - maxY;
                pivotY = 1 - pivotY;
                v      = -1;
                break;

            case MirrorMode.Both:
                minX = 1 - minX;
                minY = 1 - minY;
                maxX = 1 - maxX;
                maxY = 1 - maxY;

                pivotX = 1 - pivotX;
                pivotY = 1 - pivotY;

                h = v = -1;
                break;
            }

            rect.anchorMin        = new Vector2(minX, minY);
            rect.anchorMax        = new Vector2(maxX, maxY);
            rect.pivot            = new Vector2(pivotX, pivotY);
            anchorPos             = new Vector2(anchorPos.x * h, anchorPos.y * v);
            rect.anchoredPosition = anchorPos;
        }
コード例 #9
0
        public override byte this[ushort address]
        {
            set
            {
                if (address == 0x8000)
                {
                    // ignore mirror mode set by register $8000 for TC0190
                    MirrorMode oldMirrorMode = MirrorMode;
                    base[address] = value;
                    MirrorMode = oldMirrorMode;
                }
                else if (address == 0xC000)
                {
                    // irq latch - reload value
                    irqReload = value;
                    irqReload ^= 0xFF;
                }
                else if (address == 0xC001)
                {
                    // irq reload - set irq counter to reload value
                    irqReloadPrimed = true;
                }
                else if (address == 0xC002)
                {
                    // enable IRQ
                    irqEnabled = true;
                }
                else if (address == 0xC003)
                {
                    // disable IRQ
                    irqEnabled = false;
                    CancelInterruptRequest?.Invoke();
                }
                else if (address == 0xE000)
                {
                    // $E000: [.M.. ....]   Mirroring: 0 = Vert, 1 = Horz
                    MirrorMode = (value & Bin.Bit6) != 0 ? MirrorMode.Horizontal : MirrorMode.Vertical;
                }
                else
                {
                    // default Taito TC0190 behaviour (mapper 33)
                    base[address] = value;
                }

            }
        }
コード例 #10
0
ファイル: Memory.cs プロジェクト: falk/corenes
        private ushort MirrorAddress(MirrorMode mode, ushort address)
        {
            int[,] mirrorLookup =
            {
                { 0, 0, 1, 1 },
                { 0, 1, 0, 1 },
                { 0, 0, 0, 0 },
                { 1, 1, 1, 1 },
                { 0, 1, 2, 3 },
            };

            address = (ushort)((address - 0x2000) % 0x1000);
            var table  = address / 0x0400;
            var offset = address % 0x0400;

            return((ushort)(0x2000 + mirrorLookup[(int)mode, table] * 0x0400 + offset));
        }
コード例 #11
0
        public static Vector3 GetMirrorPlane(MirrorMode mirrorMode)
        {
            switch (mirrorMode)
            {
            case MirrorMode.RightToLeft:    return(Vector3.left);

            case MirrorMode.LeftToRight:    return(Vector3.right);

            case MirrorMode.ForwardToBack:  return(Vector3.back);

            case MirrorMode.BackToForward:  return(Vector3.forward);

            case MirrorMode.UpToDown:       return(Vector3.down);

            case MirrorMode.DownToUp:       return(Vector3.up);
            }
            return(Vector3.up);
        }
コード例 #12
0
        bool ApplyMirroringInternal()
        {
            if (m_settings.mirrorMode == MirrorMode.None)
            {
                return(false);
            }

            bool needsSetup = false;

            if (m_mirrorRelation == null || m_mirrorRelation.Length != m_points.Length)
            {
                m_mirrorRelation = new PinnedList <int>(m_points.Length);
                needsSetup       = true;
            }
            else if (m_prevMirrorMode != m_settings.mirrorMode)
            {
                m_prevMirrorMode = m_settings.mirrorMode;
                needsSetup       = true;
            }

            Vector3 planeNormal = GetMirrorPlane(m_settings.mirrorMode);

            if (needsSetup)
            {
                npModelData tmp = m_npModelData;
                tmp.vertices = m_pointsPredeformed;
                tmp.normals  = m_normalsBasePredeformed;
                if (npBuildMirroringRelation(ref tmp, planeNormal, 0.0001f, m_mirrorRelation) == 0)
                {
                    Debug.LogWarning("NormalEditor: this mesh seems not symmetric");
                    m_mirrorRelation      = null;
                    m_settings.mirrorMode = MirrorMode.None;
                    return(false);
                }
            }

            npApplyMirroring(m_normals.Length, m_mirrorRelation, planeNormal, m_normalsPredeformed);
            return(true);
        }
コード例 #13
0
        public Cartridge()
        {
            var path = "C:\\mario.NES";

            byte[] rom    = File.ReadAllBytes(path);
            byte[] header = new ArraySegment <byte>(rom, 0, 16).ToArray();

            // http://wiki.nesdev.com/w/index.php/INES#iNES_file_format
            // First 4 bytes are magic INES header

            _prgrom16kb = header[4];
            _chrrom8Kb  = header[5];

            Mirroring = (header[6] & 1) == 1 ? MirrorMode.Vertical : MirrorMode.Horizontal;

            int prgBytes = 0x4000 * _prgrom16kb;

            PRG = new ArraySegment <byte>(rom, 16, prgBytes).ToArray();

            int chrBytes = 0x2000 * _chrrom8Kb;

            CHR = new ArraySegment <byte>(rom, 16 + prgBytes, chrBytes).ToArray();
        }
コード例 #14
0
        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Create

        public Mirror()
        {
            _Mode = MirrorMode.EdgeOrFace;
        }
コード例 #15
0
ファイル: Mapper000.cs プロジェクト: FoolRunning/Emulator
 public Mapper000(ushort prgBankCount, byte chrBankCount, MirrorMode cartMirrorMode) : base(prgBankCount, chrBankCount, cartMirrorMode)
 {
     cpuAddressMask = prgBankCount == 1 ? (uint)0x3FFF : (uint)0x7FFF;
 }
コード例 #16
0
ファイル: Mapper004.cs プロジェクト: FoolRunning/Emulator
 public Mapper004(ushort prgBankCount, byte chrBankCount, MirrorMode cartMirrorMode, SystemBus bus) :
     base(prgBankCount, chrBankCount, cartMirrorMode)
 {
     this.bus = bus;
 }
コード例 #17
0
 private void SetControlRegister(byte data)
 {
     mirrorMode  = (MirrorMode)(data & 0x03);
     prgBankMode = (PRGBankMode)((data >> 2) & 0x03);
     chrBankMode = (CHRBankMode)((data >> 4) & 0x01);
 }
コード例 #18
0
 public Mapper001(ushort prgBankCount, byte chrBankCount, MirrorMode cartMirrorMode, InfoFlags info) :
     base(prgBankCount, chrBankCount, cartMirrorMode)
 {
     //if (info.HasFlag(InfoFlags.Battery))
     cartridgeRAM = new byte[Utils.Kilo8]; // TODO: Persist
 }
コード例 #19
0
ファイル: Cartridge.cs プロジェクト: FoolRunning/Emulator
        public Cartridge(string filePath, SystemBus bus)
        {
            using (BinaryReader reader = new BinaryReader(new FileStream(filePath, FileMode.Open)))
            {
                if (reader.BaseStream.Length < 16)
                {
                    return;
                }

                byte[] header = reader.ReadBytes(16);
                if (header[0] != 'N' || header[1] != 'E' || header[2] != 'S' || header[3] != 0x1A)
                {
                    return;
                }

                byte   mapperId = (byte)((header[7] & 0xF0) | (header[6] >> 4));
                byte   chrBankCount;
                ushort prgBankCount;
                if ((header[7] & 0x0C) != 0x08)
                {
                    // v1 format
                    prgBankCount = header[4];
                    chrBankCount = header[5];
                    info         = (InfoFlags)(header[6] & 0x0F);
                    timing       = (Timing)(header[9] & 0x01);
                }
                else
                {
                    // v2 format
                    prgBankCount = (ushort)(((header[9] & 0x0F) << 8) | header[4]);
                    chrBankCount = header[5];
                    info         = (InfoFlags)(header[6] & 0x0F);
                    timing       = (Timing)(header[12] & 0x03);
                }

                if (timing != Timing.NTSC)
                {
                    Console.WriteLine("WARNING: Loaded non-NTSC ROM");
                }

                //if (info.HasFlag(InfoFlags.Trainer))
                //{
                //    trainerData = new byte[512];
                //    reader.Read(trainerData, 0, trainerData.Length);
                //}

                int prgSize = prgBankCount * Utils.Kilo16;
                prgData = new byte[prgSize];
                int countRead = reader.Read(prgData, 0, prgSize);
                Debug.Assert(countRead == prgSize);

                int chrSize = chrBankCount * Utils.Kilo8;
                chrData   = new byte[chrSize];
                countRead = reader.Read(chrData, 0, chrSize);
                Debug.Assert(countRead == chrSize);

                if (chrSize == 0)
                {
                    chrData = new byte[Utils.Kilo8]; // Cartridge has RAM?
                }
                MirrorMode cartMirrorMode = info.HasFlag(InfoFlags.Mirroring) ? MirrorMode.Vertical : MirrorMode.Horizontal;
                if (mapperId == 0)
                {
                    // Fix some simple incorrect mapping IDs
                    if (chrBankCount > 1)
                    {
                        mapperId = 3;
                    }
                    else if (prgBankCount > 2)
                    {
                        mapperId = 2;
                    }
                }

                switch (mapperId)
                {
                case 0: mapper = new Mapper000(prgBankCount, chrBankCount, cartMirrorMode); break;

                case 1: mapper = new Mapper001(prgBankCount, chrBankCount, cartMirrorMode, info); break;

                case 2: mapper = new Mapper002(prgBankCount, chrBankCount, cartMirrorMode); break;

                case 3: mapper = new Mapper003(prgBankCount, chrBankCount, cartMirrorMode); break;

                case 4: mapper = new Mapper004(prgBankCount, chrBankCount, cartMirrorMode, bus); break;

                default:
                    throw new NotImplementedException("Mapper " + mapperId + " is not implemented");
                }
            }
        }
コード例 #20
0
ファイル: IPpu.cs プロジェクト: Dervall/LeetNES
 public Ppu(Lazy<ICpu> cpu, Lazy<IMemory> memory, Lazy<StolenPpu> ppu)
 {
     this.cpu = cpu;
     _memory = memory;
     _ppu = ppu;
     nameTables = new byte[1024];
     spriteRam = new byte[256];
     //Todo: Allow setting of mirrormode
     this.mirrorMode = MirrorMode.Vertical;
 }
コード例 #21
0
 /// <summary>
 /// Mirror UI.
 /// </summary>
 /// <param name="mode">Mode of mirror.</param>
 public virtual void Mirror(MirrorMode mode)
 {
 }
コード例 #22
0
 /// <summary>
 /// Sets the MirrorMode
 /// </summary>
 public static void SetMirrorMode(MirrorMode mode)
 {
     _mirrorMode = mode;
 }
コード例 #23
0
 protected Mapper(ushort prgBankCount, byte chrBankCount, MirrorMode cartMirrorMode)
 {
     this.prgBankCount   = prgBankCount;
     this.chrBankCount   = chrBankCount;
     this.cartMirrorMode = cartMirrorMode;
 }
コード例 #24
0
        public Mapper(Cartridge cartridge)
        {
            this.cartridge = cartridge;

            mirrorMode = (cartridge.Flag6 & 1) == 1 ? MirrorMode.Vertical : MirrorMode.Horizontal;
        }
コード例 #25
0
ファイル: pxcmcapture.cs プロジェクト: mbahar94/libpxcclr.cs
 /** 
     @brief Set the mirror mode.
     @param[in] value	The mirror mode
     @return PXCM_STATUS_NO_ERROR			successful execution.
     @return PXCM_STATUS_ITEM_UNAVAILABLE    the device property is not supported.
 */
 public pxcmStatus SetMirrorMode(MirrorMode value)
 {
     return SetProperty(Property.PROPERTY_DEVICE_MIRROR, (Single)value);
 }
コード例 #26
0
ファイル: Mapper002.cs プロジェクト: FoolRunning/Emulator
 public Mapper002(ushort prgBankCount, byte chrBankCount, MirrorMode cartMirrorMode) : base(prgBankCount, chrBankCount, cartMirrorMode)
 {
     prgSelectedBankLow  = 0;
     prgSelectedBankHigh = (uint)(prgBankCount - 1);
 }
コード例 #27
0
ファイル: Mapper003.cs プロジェクト: FoolRunning/Emulator
 public Mapper003(ushort prgBankCount, byte chrBankCount, MirrorMode cartMirrorMode) : base(prgBankCount, chrBankCount, cartMirrorMode)
 {
 }
コード例 #28
0
 public void Mirror2DObject()
 {
     independentMover = MirrorMode.Object2D;
 }
コード例 #29
0
 protected SimpleMapper(ushort prgBankCount, byte chrBankCount, MirrorMode cartMirrorMode) : base(prgBankCount, chrBankCount, cartMirrorMode)
 {
 }
コード例 #30
0
 public void DontMirror()
 {
     independentMover = MirrorMode.DontMirror;
 }
コード例 #31
0
        void DrawCanvasArea()
        {
            EditorGUILayout.BeginHorizontal();

            // First column
            const int cw = 90;

            EditorGUILayout.BeginVertical(GUILayout.MaxWidth(cw));
            // Paint brushes
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUILayout.LabelField("Brushes", GUILayout.MaxWidth(cw));
            EditorGUI.BeginChangeCheck();
            currentBrush = (Brush)GUILayout.SelectionGrid((int)currentBrush, brushIcons, 1, GUILayout.MaxWidth(cw), GUILayout.Height(32 * brushIcons.Length));
            if (EditorGUI.EndChangeCheck())
            {
                SelectBrush();
            }
            EditorGUILayout.EndVertical();

            EditorGUILayout.Separator();

            // Mirror
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUILayout.LabelField("Mirror", GUILayout.MaxWidth(cw));
            _mirrorMode = (MirrorMode)GUILayout.SelectionGrid((int)_mirrorMode, mirrorModes, 1, GUILayout.MaxWidth(cw), GUILayout.Height(32 * mirrorModes.Length));
            EditorGUILayout.EndVertical();

            EditorGUILayout.EndVertical();


            // Column: Commands
            // Tools

            EditorGUILayout.BeginVertical(GUILayout.MaxWidth(cw));
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUILayout.LabelField("Canvas", GUILayout.MaxWidth(cw));
            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("Clear", GUILayout.Height(32)))
            {
                ClearAll();
            }
            if (GUILayout.Button("Fill", GUILayout.Height(32)))
            {
                FillAll();
            }
            EditorGUILayout.EndHorizontal();
            if (GUILayout.Button("Fit Palette", GUILayout.MaxWidth(cw), GUILayout.Height(32)))
            {
                FitPalette();
            }

            GUIStyle buttonStyle = new GUIStyle(GUI.skin.button);

            buttonStyle.padding = new RectOffset(6, 6, 6, 6);

            const int hw = cw / 2;
            Color     prevContentColor = GUI.contentColor;

            if (!EditorGUIUtility.isProSkin)
            {
                GUI.contentColor = Color.black;
            }
            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button(icons[ICON_FLIP_VERT], buttonStyle, GUILayout.MaxWidth(hw), GUILayout.Height(32)))
            {
                FlipVert();
            }
            if (GUILayout.Button(icons[ICON_FLIP_HORIZ], buttonStyle, GUILayout.MaxWidth(hw), GUILayout.Height(32)))
            {
                FlipHoriz();
            }
            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button(icons[ICON_DISP_LEFT], buttonStyle, GUILayout.MaxWidth(hw), GUILayout.Height(32)))
            {
                Displace(-1, 0);
            }
            if (GUILayout.Button(icons[ICON_DISP_RIGHT], buttonStyle, GUILayout.MaxWidth(hw), GUILayout.Height(32)))
            {
                Displace(1, 0);
            }
            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button(icons[ICON_DISP_UP], buttonStyle, GUILayout.MaxWidth(hw), GUILayout.Height(32)))
            {
                Displace(0, 1);
            }
            if (GUILayout.Button(icons[ICON_DISP_DOWN], buttonStyle, GUILayout.MaxWidth(hw), GUILayout.Height(32)))
            {
                Displace(0, -1);
            }
            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button(icons[ICON_ROTATE_LEFT], buttonStyle, GUILayout.MaxWidth(hw), GUILayout.Height(32)))
            {
                RotateLeft();
            }
            if (GUILayout.Button(icons[ICON_ROTATE_RIGHT], buttonStyle, GUILayout.MaxWidth(hw), GUILayout.Height(32)))
            {
                RotateRight();
            }
            GUILayout.FlexibleSpace();
            EditorGUILayout.EndHorizontal();

            EditorGUI.BeginChangeCheck();
            _showGrid = GUILayout.Toggle(_showGrid, "Toggle Grid", "Button", GUILayout.Height(32));
            if (EditorGUI.EndChangeCheck())
            {
                UpdateCanvasMaterialProperties();
            }

            EditorGUILayout.Separator();
            EditorGUIUtility.labelWidth = 55;
            customWidth  = EditorGUILayout.IntField("Width", customWidth, GUILayout.MaxWidth(cw));
            customHeight = EditorGUILayout.IntField("Height", customHeight, GUILayout.MaxWidth(cw));
            if (customWidth <= 0)
            {
                customWidth = width;
            }
            if (customHeight <= 0)
            {
                customHeight = height;
            }
            GUI.enabled = customWidth != width || customHeight != height;
            if (GUILayout.Button("Set Size", GUILayout.MaxWidth(cw), GUILayout.Height(32)))
            {
                SetSize(customWidth, customHeight);
            }
            GUI.enabled = true;

            EditorGUILayout.EndVertical();

            EditorGUILayout.EndVertical();

            GUI.contentColor = prevContentColor;

            // Column: Canvas
            Rect space = EditorGUILayout.BeginVertical();

            GUILayout.FlexibleSpace();
            EditorGUILayout.EndVertical();

            float min = Mathf.Min(space.width, space.height);

            float h = min;
            float w = min * width / height;

            if (w > space.width)
            {
                h = h * space.width / w;
                w = space.width;
            }
            space.height = h;
            space.width  = w;

            if (Event.current.type == EventType.Repaint)
            {
                Vector3 canvasPos = Event.current.mousePosition;
                canvasPos.x -= space.xMin;
                canvasPos.y -= space.yMin;
                if (canvasPos.x >= 0 && canvasPos.x < space.width && canvasPos.y >= 0 && canvasPos.y < space.height)
                {
                    canvasPos.x /= space.width;
                    canvasPos.y /= space.height;
                    canvasPos.y  = 1f - canvasPos.y;

                    currentTexelPos.x = (int)(canvasPos.x * width);
                    currentTexelPos.y = (int)(canvasPos.y * height);
                    canvasMaterial.SetVector("_PixelOffset", new Vector3(0.5f / space.width, 0.5f / space.height));

                    UpdateCanvasMaterialProperties();
                    isCursorOnTexture = true;
                }
                else
                {
                    isCursorOnTexture = false;
                    currentTexelPos.x = currentTexelPos.y = -1000;
                    UpdateCanvasMaterialProperties();
                }
            }

            if (Event.current.isMouse)
            {
                if (isCursorOnTexture && Event.current.type == EventType.MouseDown)
                {
                    if (Event.current.button == 0)
                    {
                        ExecuteBrush();
                    }
                    else
                    {
                        brushColor = GetCursorColor();
                    }
                }
                else if (Event.current.type == EventType.MouseUp)
                {
                    isBrushing = false;
                }
            }

            EditorGUI.DrawPreviewTexture(space, canvasTexture, canvasMaterial);

            // Column: preview
            EditorGUILayout.BeginVertical(GUILayout.MaxWidth(cw * 3));
            EditorGUILayout.BeginVertical(GUI.skin.box);
            EditorGUILayout.LabelField("Seamless Preview", GUILayout.MaxWidth(cw * 2));
            space = EditorGUILayout.BeginVertical(GUILayout.Width(cw * 3), GUILayout.Height(cw * 3));
            EditorGUILayout.Space();
            EditorGUILayout.EndVertical();
            EditorGUILayout.EndVertical();

            EditorGUILayout.Separator();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            GUILayout.Label("Support & Suggestions");
            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("Forum", GUILayout.Height(32)))
            {
                Application.OpenURL("https://kronnect.com/support");
            }
            if (GUILayout.Button("Kronnect Assets", GUILayout.Height(32)))
            {
                Application.OpenURL("https://assetstore.unity.com/publishers/15018?aid=1101lGsd");
            }
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();

            EditorGUILayout.EndVertical();

            // Paint canvas
            for (int y = 0; y < 3; y++)
            {
                for (int x = 0; x < 3; x++)
                {
                    Rect subSpace = new Rect(space.x + x * cw, space.y + y * cw, cw, cw);
                    EditorGUI.DrawPreviewTexture(subSpace, canvasTexture, previewMaterial);
                }
            }

            EditorGUILayout.EndHorizontal(); // row
        }