// Input N rectangles and points (top-left corner of rectangle) // Output all R(r, p) where point p is within rectangle r private void BatchedRangeSearch(DisplayList dl) { // 1) Sort all points and left/right rectangle sides w.r.t. x-coordinate // 2) Sweep left-to-right while storing y-intervals of rectangles intersecting // the sweepline in a segment tree T // 2.1) Left side => insert interval into T // 2.2) Right side => delete interval from T // 2.3) Point (x, y) stabbing query report all [y1,y2] where y is in [y1..y2] Coordinate[] uniqueY = RemoveDuplication(_yCoord); SegmentTree st = new SegmentTree(uniqueY, 0, uniqueY.Length); // Sweep through sorted x coordiates from left to right, skipping MinValue/MaxValue for (int i = 1; i < _xCount - 1; i++) { Coordinate c = _xCoord[i]; double y0 = c.top.value; double y1 = c.bottom.value; if (Double.Equals(c.value, dl[c.index].Left)) { st.Insert(c.index, y0, y1); st.ReportIntersection(dl, c.index, y0); } else { st.Remove(c.index, y0, y1); } } }
private void SortEndPoints(DisplayList dl, int count) { _xCoord = new Coordinate[2 * count + 2]; _yCoord = new Coordinate[2 * count + 2]; AddPoint(0, -1, Double.MinValue, Double.MinValue); int p = 1; for (int i = 0; i < count; i++) { Rect r = dl[i]; AddPoint(p, i, r.Left, r.Top); AddPoint(p + 1, i, r.Right, r.Bottom); _xCoord[p].top = _yCoord[p]; _xCoord[p].bottom = _yCoord[p + 1]; _xCoord[p + 1].top = _yCoord[p]; _xCoord[p + 1].bottom = _yCoord[p + 1]; p += 2; } AddPoint(p, count + 1, Double.MaxValue, Double.MaxValue); Array.Sort(_xCoord, new CoordinateComparer()); _xCount = _xCoord.Length; Array.Sort(_yCoord, new CoordinateComparer()); _yCount = _yCoord.Length; }
// Input N rectangles -> 2N vertical line segment, 2N horizontal line segments // Output all true intersections rectangles private void OrthogonalLineSegmentIntersection(DisplayList dl) { // Sweep through sorted x coordiates from left to right, skipping MinValue/MaxValue for (int i = 1; i < _xCount - 1; i++) { Coordinate c = _xCoord[i]; bool left = Double.Equals(c.value, dl[c.index].Left); // Left endpoint => insertion into range tree // Right endpoint => delection from range tree c.top.active = left; c.bottom.active = left; // Vertical segment [y0..y1] => report [y0..y1] AND range tree double y0 = c.top.value; double y1 = c.bottom.value; int p = Array.BinarySearch(_yCoord, y0, new CoordinateSearcher()); if (p >= 0) { do { if ((_yCoord[p].active) && (c.index != _yCoord[p].index)) { dl.ReportOverlapping(c.index, _yCoord[p].index); // Console.WriteLine("{0} {1} intersects ({2},{3})", c.index, _yCoord[p].index, c.value, _yCoord[p].value); } p++; }while (_yCoord[p].value <= y1); } } }
public void CalculateIntersections(DisplayList dl, int count) { SortEndPoints(dl, count); OrthogonalLineSegmentIntersection(dl); BatchedRangeSearch(dl); }
internal void LogInterestingPrimitives(List <PrimitiveInfo> commands, int count, List <Cluster> transparentCluster) { if (Configuration.Verbose >= 1) { // Display only interesting primitives DisplayList.PrintPrimitive(null, -1, false); Rect target = Rect.Empty; int vip = 0; for (int i = 0; i < count; i++) { PrimitiveInfo pi = commands[i]; if ((pi != null) && ((pi.overlapHasTransparency != 0) || !pi.primitive.IsOpaque)) { if (!pi.primitive.IsOpaque) { target.Union(pi.bounds); } DisplayList.PrintPrimitive(commands[i], i, false); vip++; } } Console.WriteLine(); Console.WriteLine("Interesting primitives: {0}", vip); Console.WriteLine("Area with transparency: {0}", DisplayList.LeftPad(target, 0)); Console.WriteLine(); for (int i = 0; i < transparentCluster.Count; i++) { Console.WriteLine( "Cluster {0}: {1} {2} {3}", i + 1, DisplayList.LeftPad(transparentCluster[i].DebugBounds, 0), DisplayList.LeftPad(transparentCluster[i].DebugPrimitives, 0), transparentCluster[i].DebugRasterize); } Console.WriteLine(); } }
internal static void UnitTest() { Console.WriteLine("RectangleIntersection unit test"); DisplayList dl = new DisplayList(8.5 * 96, 11 * 96); dl.Add(3, 8, 6, 36); dl.Add(25, 34, 34, 38); dl.Add(33, 37, 21, 36); dl.Add(21, 38, 23, 27); dl.Add(6, 26, 3, 8); dl.Add(31, 35, 15, 19); dl.Add(23, 38, 11, 14); dl.Add(16, 22, 3.5, 7.5); RectangleIntersection ri = new RectangleIntersection(); ri.CalculateIntersections(dl); }
public void ReportIntersection(DisplayList dl, int index, double x) { if (_sList != null) { foreach (int i in _sList) { if (index != i) { dl.ReportOverlapping(index, i); } } } if ((_left != null) && (x >= _left._min) && (x <= _left._max)) { _left.ReportIntersection(dl, index, x); } if ((_right != null) && (x >= _right._min) && (x <= _right._max)) { _right.ReportIntersection(dl, index, x); } }
/// <summary> /// Optimization phase /// </summary> /// <param name="commands"></param> /// <param name="count"></param> /// <param name="disjoint"></param> private void DisplayListOptimization(List <PrimitiveInfo> commands, int count, bool disjoint) { #if DEBUG Console.WriteLine(); Console.WriteLine("Start 3: Display list optimization"); Console.WriteLine(); #endif List <int> [] oldUnderlay = null; if (!disjoint) // If not in a subtree which needs full flattening { // The following optimization may change PrimitiveInfo.underlay, but this is needed // for cluster calcuation. So we make a copy of it for use within this routine only oldUnderlay = CopyUnderlay(count, commands); // These optimizations need to run in a seperate pass, because they may affect other primitives for (int i = 0; i < count; i++) { repeat: PrimitiveInfo pi = commands[i]; if (pi == null) { continue; } // Optimization: If a primitive is covered by an opaque primtive, delete it if (pi.overlap != null) { bool deleted = false; for (int j = 0; j < pi.overlap.Count; j++) { PrimitiveInfo qi = commands[pi.overlap[j]]; if (qi.primitive.IsOpaque && qi.FullyCovers(pi)) { DeleteCommand(i); deleted = true; break; } } if (deleted) { continue; } } // Optimization: If a primitive is covered by overlap[0], blend brush and switch order // This results in smaller area being rendered as blending of two brushes. if ((pi.overlap != null) && (pi.overlap.Count != 0)) { int j = pi.overlap[0]; // first overlapping primitive PrimitiveInfo pj = commands[j]; // Do not attempt to blend if both primitives cover exactly same area, since blending // one into the other provides no benefits. if ((pj.underlay[pj.underlay.Count - 1] == i) && pj.FullyCovers(pi) && !pi.FullyCovers(pj)) { if (BlendCommands(pi, pj)) { SwitchCommands(commands, i, pi, j, pj, true); goto repeat; // pj at position i needs to be processed } } } // Optimization: Delete white primitives with nothing underneath if ((pi.underlay == null) && DisplayList.IsWhitePrimitive(pi.primitive)) { DeleteCommand(i); continue; } // Optimization: If a transparent primitive is covered underneath by an opaque primitive, cut its ties with all primitives before it ReduceTie(pi, commands, i); // Transparent primitive if (!pi.primitive.IsOpaque) { // Optimization: If a transparent primitive is covered underneath immediately by an opaque primitive, // or has nothing underneath, convert it to opaque primitive if (!ConvertTransparentOnOpaque(commands, i)) { PushTransparencyDown(commands, i); } } } for (int i = 0; i < count; i++) { PrimitiveInfo pi = commands[i]; if (pi == null) { continue; } // Optimization: If a primitive is covered by all opaque primitives, cut its ties with primitive on top of it. // This check is also implemented in PrimitiveRender.FindIntersection, // in which it is on a remaing items in overlapping list. // With overlapHasTransparency flag, it can be moved forward. if ((pi.overlap != null) && (pi.overlapHasTransparency == 0)) { foreach (int j in pi.overlap) { commands[j].underlay.Remove(i); } pi.overlap = null; } // Optimization: If an opaque primitive is covered by all opaque primitives, cut its ties with primitives under it. if ((pi.underlay != null) && (pi.overlapHasTransparency == 0) && pi.primitive.IsOpaque) { foreach (int j in pi.underlay) { commands[j].overlap.Remove(i); } pi.underlay = null; } } } List <Cluster> transparentCluster = Cluster.CalculateCluster(commands, count, disjoint, oldUnderlay); Cluster.CheckForRasterization(transparentCluster, commands); #if DEBUG if (HasUnmanagedCodePermission()) { LogInterestingPrimitives(commands, count, transparentCluster); SaveInterestingPrimitives(commands, count, transparentCluster); } #endif }
public Flattener(bool disJoint, double width, double height) { _dl = new DisplayList(disJoint, width, height); }
/// <summary> /// Resolve object overlapping in a primitive tree. /// Send broken down drawing primitives to _dc. /// </summary> /// <param name="dc"></param> /// <param name="disjoint">True if all output primitives need to be disjoint</param> public void AlphaFlatten(IProxyDrawingContext dc, bool disjoint) { List <PrimitiveInfo> commands = _dl.Commands; if (commands == null) { return; } int count = commands.Count; _dc = dc; bool needFlattening = true; if (Configuration.BlendAlphaWithWhite || Configuration.ForceAlphaOpaque) { needFlattening = false; } else if (!disjoint) { needFlattening = false; for (int i = 0; i < count; i++) { PrimitiveInfo info = commands[i]; if (!info.primitive.IsOpaque) { needFlattening = true; break; } } } if (needFlattening) { #if DEBUG Console.WriteLine(); Console.WriteLine("Stage 2: Calculating intersections using bounding boxes"); Console.WriteLine(); #endif // Still need all the primitive, for removal by opaque covering and white primitive removal _dl.CalculateIntersections(count); } #if DEBUG if (Configuration.Verbose >= 2) { Console.WriteLine(); Console.WriteLine("Original display list"); Console.WriteLine(); DisplayList.PrintPrimitive(null, -1, true); for (int i = 0; i < count; i++) { DisplayList.PrintPrimitive(commands[i], i, true); } Console.WriteLine(); Console.WriteLine("Primitives in display list: {0}", count); Console.WriteLine(); } #endif if (needFlattening) { DisplayListOptimization(commands, count, disjoint); } #if DEBUG for (int i = 0; i < count; i++) { if (commands[i] != null) { commands[i].SetID(i); } } Console.WriteLine(); Console.WriteLine("Stage 4: Alpha flattening"); Console.WriteLine(); #endif for (int i = 0; i < count; i++) { PrimitiveInfo info = commands[i]; if (info == null) { continue; } String desp = null; #if DEBUG if (Configuration.Verbose >= 2) { Console.Write(i); Console.Write(": "); } desp = info.id; #endif if (info.m_cluster != null) { info.m_cluster.Render(commands, dc); } else { AlphaRender(info.primitive, info.overlap, info.overlapHasTransparency, disjoint, desp); } #if DEBUG if (Configuration.Verbose >= 2) { Console.WriteLine(""); } #endif } _dc = null; }