using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Drawing.Drawing2D; namespace GUI { public partial class ctrlChipDisplay : UserControl { const int _chipSizeX = 13000; const int _chipSizeY = 21500; const double _xyRatio = (double)_chipSizeY/_chipSizeX; const int _canvasSizeX = 3000; const int _canvasSizeY = (int)(_canvasSizeX*_xyRatio); int _dimensionX = 0; int _dimensionY = 0; bool[] _visibleLayers = new bool[] { true, true, true, true, true, true }; Color[] _layerColors = new Color[] { Color.FromArgb(100, 128, 128, 192), Color.FromArgb(255, 255, 0), Color.FromArgb(255, 0, 255), Color.FromArgb(0x4D, 255, 0x4D), Color.FromArgb(255, 0x4D, 0x4D), Color.FromArgb(0x80, 0x1A, 0xC0), Color.FromArgb(191, 128, 0, 255) }; Bitmap _imgBackground; Bitmap _imgHitBuffer; Bitmap _imgHighlight; Bitmap _imgHighNodes; int _chipScale = 1; int _translateX = 0; int _translateY = 0; bool _viewPortChanged = true; bool _needRefresh = true; bool _isDragging = false; Point _previousLocation; bool _showState = false; ChipDefinitions _chipDef; public ctrlChipDisplay() { InitializeComponent(); } public void SetChipDefinitions(ChipDefinitions chipDef) { bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime); if(!designMode) { _chipDef = chipDef; RenderChip(); tmrDrawChip.Enabled = true; } } bool _refreshingState = false; public bool ShowState { get { return _showState; } set { _showState = value; _needRefresh = true; } } public void RefreshState(bool async = false) { if(!_refreshingState && _showState) { Action updateState = () => { _refreshingState = true; lock(_imgHighNodes) { using(Graphics g = Graphics.FromImage(_imgHighNodes)) { g.Clear(Color.Transparent); using(Brush brush = new SolidBrush(Color.FromArgb(100, 255, 0, 64))) { foreach(node node in _chipDef.Nodes) { if(node != null) { if(node.num != 1 && CoreWrapper.isNodeHigh(node.num)) { foreach(List segments in node.segs) { DrawSegment(g, brush, segments, false); } } } } } } } _needRefresh = true; _refreshingState = false; }; if(async) { Task.Run(updateState); } else { updateState(); } } } public void SetVisibleLayers(bool showDiffusion, bool showGroundedDiffusion, bool showPoweredDiffusion, bool showPolysilicon, bool showMetal, bool showProtection) { _visibleLayers = new bool[] { showMetal, showDiffusion, showProtection, showGroundedDiffusion, showPoweredDiffusion, showPolysilicon }; _viewPortChanged = true; RenderChip(); } public void RenderChip() { _imgBackground = new Bitmap(_canvasSizeX, _canvasSizeY); _imgHitBuffer = new Bitmap(_canvasSizeX, _canvasSizeY); _imgHighlight = new Bitmap(_canvasSizeX, _canvasSizeY); _imgHighNodes = new Bitmap(_canvasSizeX, _canvasSizeY); using(Graphics g = Graphics.FromImage(_imgBackground)) { DrawBackground(g); } using(Graphics g = Graphics.FromImage(_imgHitBuffer)) { DrawHitBuffer(g); } using(Graphics g = Graphics.FromImage(_imgHighlight)) { g.Clear(Color.Transparent); } using(Graphics g = Graphics.FromImage(_imgHighNodes)) { g.Clear(Color.Transparent); } RefreshPicture(); } private void DrawBackground(Graphics g) { g.FillRectangle(Brushes.Black, 0, 0, _canvasSizeX, _canvasSizeY); foreach(List segmentDef in _chipDef.SegmentDefs) { int layer = segmentDef[2]; if(_visibleLayers[layer]) { using(Brush brush = new SolidBrush(_layerColors[layer])) { DrawSegment(g, brush, segmentDef.GetRange(3, segmentDef.Count - 3), layer==0 || layer==6); } } } } private void DrawHitBuffer(Graphics g) { g.FillRectangle(Brushes.Black, 0, 0, _canvasSizeX, _canvasSizeY); foreach(List segmentDef in _chipDef.SegmentDefs) { int segmentID = segmentDef[0]; int layer = segmentDef[2]; using(Brush brush = new SolidBrush(Color.FromArgb((int)(0xFF000000|segmentID)))) { DrawSegment(g, brush, segmentDef.GetRange(3, segmentDef.Count - 3), false); } } } private void DrawSegment(Graphics g, Brush brush, List segment, bool drawBorder) { Func scaleDistance = (x) => { return (int)Math.Round((double)(x*_canvasSizeX/_chipSizeX)); }; int dx = 0;//200; int dy = 0;//-300; GraphicsPath path = new GraphicsPath(); Point[] points = new Point[segment.Count / 2 + 1]; points[0] = new Point(scaleDistance(segment[0]+dx), scaleDistance(_chipSizeY-segment[1]+dy)); for(var i = 2; i picChip.Height) { _dimensionX = (int)(picChip.Height / _xyRatio); _dimensionY = picChip.Height; } else { _dimensionX = picChip.Width; _dimensionY = (int)(picChip.Width * _xyRatio); } } _translateX = Math.Min(Math.Max(_translateX, -_dimensionX / 2), _dimensionX * _chipScale - _dimensionX / 2); _translateY = Math.Min(Math.Max(_translateY, -_dimensionY / 2), _dimensionY * _chipScale - _dimensionY / 2); using(Graphics g = Graphics.FromImage(_imgViewport)) { g.InterpolationMode = InterpolationMode.NearestNeighbor; g.PixelOffsetMode = PixelOffsetMode.Half; g.SmoothingMode = SmoothingMode.None; g.Clear(Color.Black); int xPos = -_translateX + (picChip.Width - _dimensionX) / 2; int yPos = -_translateY + (picChip.Height - _dimensionY) / 2; g.DrawImage(_imgBackground, xPos, yPos, _dimensionX*_chipScale, (int)(_dimensionY*_chipScale)); g.DrawImage(_imgHighlight, xPos, yPos, _dimensionX*_chipScale, (int)(_dimensionY*_chipScale)); g.DrawLine(Pens.White, 0, picChip.Height - 1, picChip.Width - 1, picChip.Height - 1); } _viewPortChanged = false; _needRefresh = true; } } private void RefreshPicture() { UpdateViewportImage(); if(_needRefresh && _imgBackground != null) { using(Graphics g = Graphics.FromImage(picChip.Image)) { g.DrawImage(_imgViewport, 0, 0); if(ShowState) { g.InterpolationMode = InterpolationMode.NearestNeighbor; g.PixelOffsetMode = PixelOffsetMode.Half; g.SmoothingMode = SmoothingMode.None; int xPos = -_translateX + (picChip.Width - _dimensionX) / 2; int yPos = -_translateY + (picChip.Height - _dimensionY) / 2; lock(_imgHighNodes) { g.DrawImage(_imgHighNodes, xPos, yPos, _dimensionX*_chipScale, (int)(_dimensionY*_chipScale)); } } } picChip.Invalidate(); _needRefresh = false; } } private void ZoomOut() { if(_chipScale > 1) { _translateX = _translateX / 2 - _dimensionX / 4; _translateY = _translateY / 2 - _dimensionY / 4; _chipScale /= 2; _viewPortChanged = true; } } private void ZoomIn(Point location) { if(_chipScale < 32) { int x = location.X - (picChip.Width - _dimensionX) / 2; int y = location.Y - (picChip.Height - _dimensionY) / 2; _translateX = (_translateX + (x - _dimensionX / 4)) * 2; _translateY = (_translateY + (y - _dimensionY / 4)) * 2; _chipScale *= 2; _viewPortChanged = true; } } public void HighlightNode(List nodeNumbers, bool focusOnSelection = false) { List> segmentList = new List>(); using(Graphics g = Graphics.FromImage(_imgHighlight)) { g.Clear(Color.Transparent); foreach(int nn in nodeNumbers) { if(nn < 0) { segmentList.Add(_chipDef.Transistors[-nn + 1].bb); //Draw transistor using(Brush brush = new SolidBrush(Color.FromArgb(180, 255, 255, 255))) { List bb = _chipDef.Transistors[-nn + 1].bb; DrawSegment(g, brush, new List { bb[0], bb[2], bb[1], bb[2], bb[1], bb[3], bb[0], bb[3] }, false); } } else if(_chipDef.Nodes[nn].segs.Count > 0) { segmentList.AddRange(_chipDef.Nodes[nn].segs); //Draw node Color color = CoreWrapper.isNodeHigh(nn) ? Color.FromArgb(180, 255, 255, 255) : Color.FromArgb(180, 255, 255, 255); using(Brush brush = new SolidBrush(color)) { foreach(List segments in _chipDef.Nodes[nn].segs) { DrawSegment(g, brush, segments, false); } } } } } if(focusOnSelection && segmentList.Count > 0) { var xmin = segmentList[0][0]; var xmax = segmentList[0][0]; var ymin = segmentList[0][1]; var ymax = segmentList[0][1]; foreach(List segments in segmentList) { for(var i = 0; i < segments.Count; i += 2) { if(segments[i] < xmin) xmin = segments[i]; if(segments[i] > xmax) xmax = segments[i]; if(segments[i + 1] < ymin) ymin = segments[i + 1]; if(segments[i + 1] > ymax) ymax = segments[i + 1]; } } ZoomToBox(xmin, xmax, ymin, ymax); } _viewPortChanged = true; } private void ZoomToBox(int xmin, int xmax, int ymin, int ymax) { var xmid = (xmin + xmax) / 2; var ymid = (ymin + ymax) / 2; var x = xmid * _canvasSizeX / _chipSizeX; var y = (_chipSizeY - ymid) * _canvasSizeY / _chipSizeY; int widthOnCanvas = ((xmax - xmin) * _canvasSizeX / _chipSizeX); int heightOnCanvas = ((ymax - ymin) * _canvasSizeY / _chipSizeY); int maxHorizontalScale = _canvasSizeX / widthOnCanvas; int maxVerticalScale = _canvasSizeY / heightOnCanvas; _chipScale = Math.Max(1, Math.Min(32, Math.Min(maxHorizontalScale, maxVerticalScale))); _translateX = x * (_dimensionX * _chipScale) / _canvasSizeX - (_dimensionX / 2); _translateY = y * (_dimensionY * _chipScale) / _canvasSizeY - (_dimensionY / 2); } private void FindNodeAtLocation(Point location, bool highlightGroup) { int x = (location.X + _translateX - (picChip.Width - _dimensionX) / 2); int y = (location.Y + _translateY - (picChip.Height - _dimensionY) / 2); int xChip = x * _canvasSizeX / (_dimensionX * _chipScale); int yChip = y * _canvasSizeY / (_dimensionY * _chipScale); if(xChip >= 0 && xChip < _canvasSizeX && yChip >= 0 && yChip < _canvasSizeY) { List nodes = _chipDef.Nodes; int w = _imgHitBuffer.GetPixel(xChip, yChip).ToArgb() & 0xFFFFFF; if(w <= 0 || w >= nodes.Count) return; // we have a node, but maybe we clicked over a transistor List nodelist = new List() { w }; // match the coordinate against transistor gate bounding boxes x = xChip*_chipSizeX/_canvasSizeX; y = _chipSizeY - yChip*_chipSizeY/_canvasSizeY; string s1 = "x: " + x + " y: " + y; string s2 = "node: " + w + " " + _chipDef.getNodeName(w); for(var i = 0; i= xmin) && (x <= xmax) && (y >= ymin) && (y <= ymax)) { // only one match at most, so we replace rather than push nodelist = new List { -_chipDef.getTransistorIndex(nodes[w].gates[i].name) - 1 }; s2="transistor: " + nodes[w].gates[i].name + " on " + s2; } } // if this is a shift-click, just find and highlight the pass-connected group // and list the nodes (or nodenames, preferably) if(highlightGroup) { nodelist = new List(_chipDef.getNodeGroup(w)); s2 = "nodegroup from " + s2 + " (nodes: " + string.Join(",", nodelist.Select((nn) => { return _chipDef.getNodeName(nn).Length > 0 ? _chipDef.getNodeName(nn) : nn.ToString(); })) + ")"; } HighlightNode(nodelist); lblNodeInfo.Text = s1 + Environment.NewLine + s2; } } #region Events private void picChip_SizeChanged(object sender, EventArgs e) { _viewPortChanged = true; RefreshPicture(); } private void picChip_MouseClick(object sender, MouseEventArgs e) { if(e.Button == MouseButtons.Right) { ZoomOut(); } } private void picChip_MouseDoubleClick(object sender, MouseEventArgs e) { if(e.Button == MouseButtons.Left) { ZoomIn(e.Location); } } private void picChip_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e) { if(e.Delta < 0) { ZoomOut(); } else if(e.Delta > 0) { ZoomIn(e.Location); } } bool _dragged = false; private void picChip_MouseDown(object sender, MouseEventArgs e) { _isDragging = true; _dragged = false; _previousLocation = e.Location; } private void picChip_MouseUp(object sender, MouseEventArgs e) { _isDragging = false; if(!_dragged) { FindNodeAtLocation(e.Location, ModifierKeys.HasFlag(Keys.Shift)); } } private void picChip_MouseMove(object sender, MouseEventArgs e) { if(Application.OpenForms[0].ContainsFocus) { this.Focus(); } if(ModifierKeys.HasFlag(Keys.Alt)) { FindNodeAtLocation(e.Location, ModifierKeys.HasFlag(Keys.Shift)); } if(_isDragging) { int dx = e.Location.X - _previousLocation.X; int dy = e.Location.Y - _previousLocation.Y; if(Math.Abs(dx) > 2 || Math.Abs(dy) > 2) { _dragged = true; } _translateX -= dx; _translateY -= dy; _previousLocation = e.Location; _viewPortChanged = true; } } private void tmrDrawChip_Tick(object sender, EventArgs e) { RefreshPicture(); } private void picChip_MouseEnter(object sender, EventArgs e) { this.MouseWheel += picChip_MouseWheel; } private void picChip_MouseLeave(object sender, EventArgs e) { this.MouseWheel -= picChip_MouseWheel; } #endregion } }