/// <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();
        }
Beispiel #2
0
        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;
        }
Beispiel #4
0
        /// <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);
        }