/// <summary> /// Calculates line buffer like sense areas based on current positioning of blocks. /// </summary> private void doCalculateSenseAreas() { // No blocks: no areas. Just a defensive check. if (positionedBlocks == null || positionedBlocks.Length == 0) { return; } List <SenseArea> areas = new List <SenseArea>(); ushort currX = ushort.MinValue; ushort currY = (ushort)positionedBlocks[0].LocY; ushort lineIx = 0; short senseIx = -1; Block? bLast = null; PositionedBlock? pbLast = null; for (int i = 0; i != positionedBlocks.Length; ++i) { Block b = measuredBlocks[i]; PositionedBlock pb = positionedBlocks[i]; // Starting a new sense? // Moving to new line? // -> If we've had a range before, that's a new sense area. if (b.FirstInCedictSense || pb.LocY != currY) { // We've been building a block already if (bLast.HasValue) { SenseArea area = new SenseArea(lineIx, currX, (ushort)(pbLast.Value.LocX + bLast.Value.Width), (ushort)senseIx); areas.Add(area); } // Increment either sense index or line index or both if (b.FirstInCedictSense) { ++senseIx; } if (pb.LocY != currY) { ++lineIx; } currY = (ushort)pb.LocY; currX = (ushort)pb.LocX; } // Current block is next last block bLast = b; pbLast = pb; } // Finish off very last block SenseArea last = new SenseArea(lineIx, currX, (ushort)(pbLast.Value.LocX + bLast.Value.Width), (ushort)senseIx); areas.Add(last); // We're done here. senseAreas = areas.ToArray(); }
public override void Use() { Debug.WriteLine(player.currentSelection); if (player.currentSelection.HasValue) { Debug.WriteLine(player.targetPoint); PositionedBlock b = player.currentSelection.Value; Vector3i pos = b.position; Chunk c = this.player.world.ChunkAt(pos.asVector3()); debug(c, "current"); /*foreach (Cardinal card in Enum.GetValues(typeof(Cardinal))) * { * debug(c.GetNeighbour(card), card.ToString()); * }*/ } }
/// <summary> /// Calculates layout of content in entry body, taking current width into account for line breaks. /// </summary> /// <param name="lemmaL">Left position of body content area.</param> /// <param name="lemmaW">Width of body content area.</param> /// <returns>Bottom of content area.</returns> private float doArrangeBlocks(float lemmaL, float lemmaW) { float lemmaTop = (float)padTop + pinyinInfo.PinyinHeight * 1.3F; // Will not work reduntantly if (positionedBlocks != null) { if (positionedBlocks.Length == 0) return lemmaTop; else return positionedBlocks[positionedBlocks.Length - 1].LocY + lemmaLineHeight; } // This is always re-done when function is called // We only get here when width has changed, so we do need to rearrange positionedBlocks = new PositionedBlock[measuredBlocks.Length]; senseAreas = null; List<int> currHiliteIndexes = new List<int>(); // Cycle memory for block positioning float blockX = lemmaL; float blockY = lemmaTop; bool firstBlock = true; PositionedBlock lastPB = new PositionedBlock(); Block lastBlock = new Block(); for (int i = 0; i != measuredBlocks.Length; ++i) { Block block = measuredBlocks[i]; // If current block is a sense ID, and we've had block before: // Add extra space on left if (block.SenseId && !firstBlock) blockX += spaceWidth; // Use current position PositionedBlock pb = new PositionedBlock { BlockIdx = (ushort)i, LocX = (short)Math.Round(blockX), LocY = (short)Math.Round(blockY), }; // New block extends beyond available width: break to next line // Also break if block explicitly requests it // But, if last block is "stick right", break together if (pb.LocX + ((float)block.Width) - lemmaL > lemmaW || block.NewLine) { blockY += lemmaLineHeight; blockX = lemmaL; // No stick if (firstBlock || !lastBlock.StickRight) { pb.LocX = (short)Math.Round(blockX); pb.LocY = (short)Math.Round(blockY); } // We break together else { // Last block breaks onto this line lastPB.LocX = (short)Math.Round(blockX); lastPB.LocY = (short)Math.Round(blockY); positionedBlocks[i - 1] = lastPB; // We move on by last block's width plus (optional) space blockX += ((float)lastBlock.Width); if (!lastBlock.SenseId && lastBlock.SpaceAfter) blockX += spaceWidth; // So. pb.LocX = (short)Math.Round(blockX); pb.LocY = (short)Math.Round(blockY); } } // Add to list of positioned blocks positionedBlocks[i] = pb; // This is a text block with a highlight? Collect it too! if (!block.SenseId && block.Hilite) { if (currHiliteIndexes.Count != 0 && currHiliteIndexes[currHiliteIndexes.Count - 1] != i - 1) doCollectHighlightRange(ref currHiliteIndexes); currHiliteIndexes.Add(i); } // Move right by block's width; space optional blockX += ((float)block.Width); if (!block.SenseId && block.SpaceAfter) blockX += spaceWidth; // Remeber "last block" for next round lastPB = pb; lastBlock = measuredBlocks[lastPB.BlockIdx]; firstBlock = false; } // Collect any last highlights doCollectHighlightRange(ref currHiliteIndexes); // In link areas, fill in positioned blocks and calculate actual link areas. doCalculateLinkAreas(); // Update sense areas doCalculateSenseAreas(); // Return bottom of content area. return measuredBlocks.Length == 0 ? blockY : blockY + lemmaLineHeight; }
/// <summary> /// Paints highlights behind target text blocks. /// </summary> private void doPaintTargetHilites(Graphics g, Color bgcol) { // No highlights - done here if (targetHiliteIndexes == null) { return; } // Needed to make gradient work g.SmoothingMode = SmoothingMode.None; // We offset highlight vertically for more pleasing aesthetics (lots of empty space at top in text) float topOfs = lemmaCharHeight / 15.0F; // TO-DO: This will need more work; depends on font size & Scale // All the measured and positioned blocks in entry body using (Brush b = new SolidBrush(Magic.HiliteColor)) { // Every adjacent range foreach (List <int> idxList in targetHiliteIndexes) { int lastY = int.MinValue; int lastRight = int.MinValue; for (int i = 0; i != idxList.Count; ++i) { PositionedBlock pb = positionedBlocks[idxList[i]]; Block tb = measuredBlocks[pb.BlockIdx]; // !! Paint gradients first. // Linear gradient brush has ugly bug where white line (1px or less) is drawn right at the darkest edge // If we draw a bit bigger gradient, then overdraw with solid, this will be covered up. // Thank you, Microsoft. Beer's on me. // EXtending gradient on left if (i == 0) { RectangleF rleft = new RectangleF( pb.LocX - 2.0F * spaceWidth + 1.0F, pb.LocY, 2.0F * spaceWidth, lemmaCharHeight); rleft.Y += topOfs; using (LinearGradientBrush lbr = new LinearGradientBrush(rleft, bgcol, Magic.HiliteColor, LinearGradientMode.Horizontal)) { rleft.X += 1.0F; rleft.Width -= 1.0F; g.FillRectangle(lbr, rleft); } } // Extending gradient on right if (i == idxList.Count - 1) { RectangleF rright = new RectangleF( pb.LocX + ((float)tb.Width) - 1.0F, pb.LocY, 2.0F * spaceWidth, lemmaCharHeight); rright.Y += topOfs; using (LinearGradientBrush lbr = new LinearGradientBrush(rright, Magic.HiliteColor, bgcol, LinearGradientMode.Horizontal)) { g.FillRectangle(lbr, rright); } } // Rectangle behind this specific block RectangleF rect = new RectangleF( new PointF(pb.LocX, pb.LocY), new SizeF((float)tb.Width, lemmaCharHeight)); rect.X -= 1.0F; // Extend solid area to cover up buggy gradient edge rect.Y += topOfs; rect.Width += 2.0F; // Extend solid area to cover up buggy gradient edge g.FillRectangle(b, rect); // If this block is on the same line as before, fill space between blocks if (pb.LocY == lastY) { rect.X = lastRight; rect.Width = pb.LocX - lastRight; g.FillRectangle(b, rect); } // Remember Y of block so we can fill empty areas between blocks on the same line lastY = pb.LocY; lastRight = pb.LocX + tb.Width; } } } }
/// <summary> /// Fills in positioned blocks for links, and calculates links' active areas. /// </summary> private void doCalculateLinkAreas() { // No links: nothing to do. if (targetLinks == null) { return; } // Clear old positioned blocks and active areas in links foreach (LinkArea link in targetLinks) { link.ActiveAreas.Clear(); link.PositionedBlocks.Clear(); } // Look at each positioned block, add to correct link that has its block. // Positioned blocks will be in their correct order in each link's list. foreach (PositionedBlock pb in positionedBlocks) { foreach (LinkArea link in targetLinks) { if (link.BlockIds.Contains(pb.BlockIdx)) { link.PositionedBlocks.Add(pb); } } } // Calculate links' active areas. That means encapsulating rectangles // of positioned blocks that are on the same line. foreach (LinkArea link in targetLinks) { // There is a single block if (link.PositionedBlocks.Count == 1) { PositionedBlock pb = link.PositionedBlocks[0]; ushort width = measuredBlocks[pb.BlockIdx].Width; Rectangle rect = new Rectangle( (int)pb.LocX, (int)pb.LocY, (int)width, (int)lemmaCharHeight); link.ActiveAreas.Add(rect); } // There are multiple blocks else { short lastY = short.MinValue; short currLeft = short.MinValue; for (int i = 0; i != link.PositionedBlocks.Count; ++i) { PositionedBlock pb = link.PositionedBlocks[i]; // Block on a new line if (pb.LocY != lastY) { // We had a previous block, which completed an area if (lastY != short.MinValue) { PositionedBlock previousPB = link.PositionedBlocks[i - 1]; ushort previousWidth = measuredBlocks[previousPB.BlockIdx].Width; Rectangle rect = new Rectangle( (int)currLeft, (int)lastY, (int)(previousPB.LocX + ((float)previousWidth) - currLeft), (int)(lemmaCharHeight)); link.ActiveAreas.Add(rect); } // This block's left is merged area's left. currLeft = pb.LocX; } lastY = pb.LocY; } // Last block completes last area PositionedBlock lastPB = link.PositionedBlocks[link.PositionedBlocks.Count - 1]; ushort lastWidth = measuredBlocks[lastPB.BlockIdx].Width; Rectangle lastRect = new Rectangle( (int)currLeft, (int)lastY, (int)(lastPB.LocX + ((float)lastWidth) - currLeft), (int)(lemmaCharHeight)); link.ActiveAreas.Add(lastRect); } } }
/// <summary> /// Calculates layout of content in entry body, taking current width into account for line breaks. /// </summary> /// <param name="lemmaL">Left position of body content area.</param> /// <param name="lemmaW">Width of body content area.</param> /// <returns>Bottom of content area.</returns> private float doArrangeBlocks(float lemmaL, float lemmaW) { float lemmaTop = (float)padTop + pinyinInfo.PinyinHeight * 1.3F; // Will not work reduntantly if (positionedBlocks != null) { if (positionedBlocks.Length == 0) { return(lemmaTop); } else { return(positionedBlocks[positionedBlocks.Length - 1].LocY + lemmaLineHeight); } } // This is always re-done when function is called // We only get here when width has changed, so we do need to rearrange positionedBlocks = new PositionedBlock[measuredBlocks.Length]; senseAreas = null; List <int> currHiliteIndexes = new List <int>(); // Cycle memory for block positioning float blockX = lemmaL; float blockY = lemmaTop; bool firstBlock = true; PositionedBlock lastPB = new PositionedBlock(); Block lastBlock = new Block(); for (int i = 0; i != measuredBlocks.Length; ++i) { Block block = measuredBlocks[i]; // If current block is a sense ID, and we've had block before: // Add extra space on left if (block.SenseId && !firstBlock) { blockX += spaceWidth; } // Use current position PositionedBlock pb = new PositionedBlock { BlockIdx = (ushort)i, LocX = (short)Math.Round(blockX), LocY = (short)Math.Round(blockY), }; // New block extends beyond available width: break to next line // Also break if block explicitly requests it // But, if last block is "stick right", break together if (pb.LocX + ((float)block.Width) - lemmaL > lemmaW || block.NewLine) { blockY += lemmaLineHeight; blockX = lemmaL; // No stick if (firstBlock || !lastBlock.StickRight) { pb.LocX = (short)Math.Round(blockX); pb.LocY = (short)Math.Round(blockY); } // We break together else { // Last block breaks onto this line lastPB.LocX = (short)Math.Round(blockX); lastPB.LocY = (short)Math.Round(blockY); positionedBlocks[i - 1] = lastPB; // We move on by last block's width plus (optional) space blockX += ((float)lastBlock.Width); if (!lastBlock.SenseId && lastBlock.SpaceAfter) { blockX += spaceWidth; } // So. pb.LocX = (short)Math.Round(blockX); pb.LocY = (short)Math.Round(blockY); } } // Add to list of positioned blocks positionedBlocks[i] = pb; // This is a text block with a highlight? Collect it too! if (!block.SenseId && block.Hilite) { if (currHiliteIndexes.Count != 0 && currHiliteIndexes[currHiliteIndexes.Count - 1] != i - 1) { doCollectHighlightRange(ref currHiliteIndexes); } currHiliteIndexes.Add(i); } // Move right by block's width; space optional blockX += ((float)block.Width); if (!block.SenseId && block.SpaceAfter) { blockX += spaceWidth; } // Remeber "last block" for next round lastPB = pb; lastBlock = measuredBlocks[lastPB.BlockIdx]; firstBlock = false; } // Collect any last highlights doCollectHighlightRange(ref currHiliteIndexes); // In link areas, fill in positioned blocks and calculate actual link areas. doCalculateLinkAreas(); // Update sense areas doCalculateSenseAreas(); // Return bottom of content area. return(measuredBlocks.Length == 0 ? blockY : blockY + lemmaLineHeight); }