public StrokeGenerator() { m_stroker = new StrokeMath(); vertexDistanceList = new VertexDistanceList(); m_out_vertices = new VertexStore(); m_status = StrokeMath.Status.Init; }
public override bool Move(int mouseX, int mouseY) { bool result = base.Move(mouseX, mouseY); myvxs = null; return result; }
public override void OnDraw(Graphics2D graphics2D) { if (myvxs == null) { var transform = Affine.NewMatix( AffinePlan.Translate(-lionShape.Center.x, -lionShape.Center.y), AffinePlan.Scale(spriteScale, spriteScale), AffinePlan.Rotate(angle + Math.PI), AffinePlan.Skew(skewX / 1000.0, skewY / 1000.0), AffinePlan.Translate(Width / 2, Height / 2) ); //create vertextStore again from origiinal path myvxs = transform.TransformToVxs(lionShape.Path.Vxs, new VertexStore()); } //--------------------------------------------------------------------------------------------- { int j = lionShape.NumPaths; int[] pathList = lionShape.PathIndexList; PixelFarm.Drawing.Color[] colors = lionShape.Colors; //graphics2D.UseSubPixelRendering = true; for (int i = 0; i < j; ++i) { graphics2D.Render(new VertexStoreSnap(myvxs, pathList[i]), colors[i]); } } //--------------------------------------------------------------------------------------------- }
public static VertexStore CreateVxs(IEnumerable<VertexData> iter, VertexStore vxs) { foreach (VertexData v in iter) { vxs.AddVertex(v.x, v.y, v.command); } return vxs; }
public static SKPath CreateGraphicsPath(VertexStore vxs) { //render vertice in store int vcount = vxs.Count; double prevX = 0; double prevY = 0; double prevMoveToX = 0; double prevMoveToY = 0; //var brush_path = new System.Drawing.Drawing2D.GraphicsPath(FillMode.Winding);//*** winding for overlapped path var brushPath = new SKPath(); //how to set widening mode for (int i = 0; i < vcount; ++i) { double x, y; PixelFarm.Agg.VertexCmd cmd = vxs.GetVertex(i, out x, out y); switch (cmd) { case PixelFarm.Agg.VertexCmd.MoveTo: prevMoveToX = prevX = x; prevMoveToY = prevY = y; //brush_path.StartFigure(); brushPath.MoveTo((float)x, (float)y); break; case PixelFarm.Agg.VertexCmd.LineTo: //brush_path.AddLine((float)prevX, (float)prevY, (float)x, (float)y); brushPath.LineTo((float)x, (float)y); prevX = x; prevY = y; break; case PixelFarm.Agg.VertexCmd.CloseAndEndFigure: //brush_path.AddLine((float)prevX, (float)prevY, (float)prevMoveToX, (float)prevMoveToY); brushPath.LineTo((float)prevMoveToX, (float)prevMoveToY); prevMoveToX = prevX = x; prevMoveToY = prevY = y; //brush_path.CloseFigure(); brushPath.Close(); break; case PixelFarm.Agg.VertexCmd.EndFigure: break; case PixelFarm.Agg.VertexCmd.Stop: i = vcount + 1;//exit from loop break; default: throw new NotSupportedException(); } } return brushPath; }
/// <summary> /// get processed/scaled vxs /// </summary> /// <returns></returns> public VertexStore GetVxs(VertexStore vxs) { float scale = TypeFace.CalculateScale(SizeInPoints);// (float)(SizeInPoints * Resolution) / (pointsPerInch * TypeFaceUnitPerEm); var mat = PixelFarm.Agg.Transform.Affine.NewMatix( //scale new PixelFarm.Agg.Transform.AffinePlan( PixelFarm.Agg.Transform.AffineMatrixCommand.Scale, scale, scale), //translate new PixelFarm.Agg.Transform.AffinePlan( PixelFarm.Agg.Transform.AffineMatrixCommand.Translate, 1, 1) ); var v1 = mat.TransformToVxs(ps.Vxs, s_vxsPool.GetFreeVxs()); curveFlattener.MakeVxs(v1, vxs); s_vxsPool.Release(ref v1); return vxs; }
Affine BuildImageBoundsPath( int srcW, int srcH, VertexStore drawImageRectPath, double destX, double destY) { AffinePlan plan = new AffinePlan(); if (destX != 0 || destY != 0) { plan = AffinePlan.Translate(destX, destY); } drawImageRectPath.Clear(); drawImageRectPath.AddMoveTo(0, 0); drawImageRectPath.AddLineTo(srcW, 0); drawImageRectPath.AddLineTo(srcW, srcH); drawImageRectPath.AddLineTo(0, srcH); drawImageRectPath.AddCloseFigure(); return Affine.NewMatix(plan); }
Affine BuildImageBoundsPath( int srcW, int srcH, VertexStore drawImageRectPath, double destX, double destY, double hotspotOffsetX, double hotSpotOffsetY, double scaleX, double scaleY, double angleRad) { AffinePlan[] plan = new AffinePlan[4]; int i = 0; if (hotspotOffsetX != 0.0f || hotSpotOffsetY != 0.0f) { plan[i] = AffinePlan.Translate(-hotspotOffsetX, -hotSpotOffsetY); i++; } if (scaleX != 1 || scaleY != 1) { plan[i] = AffinePlan.Scale(scaleX, scaleY); i++; } if (angleRad != 0) { plan[i] = AffinePlan.Rotate(angleRad); i++; } if (destX != 0 || destY != 0) { plan[i] = AffinePlan.Translate(destX, destY); i++; } drawImageRectPath.Clear(); drawImageRectPath.AddMoveTo(0, 0); drawImageRectPath.AddLineTo(srcW, 0); drawImageRectPath.AddLineTo(srcW, srcH); drawImageRectPath.AddLineTo(0, srcH); drawImageRectPath.AddCloseFigure(); return Affine.NewMatix(plan); }
public override void PaintSeries(VertexStore vxs, Color[] colors, int[] pathIndexs, int numPath) { sclineRasToBmp.RenderSolidAllPaths(this.gx.DestImage, this.sclineRas, this.scline, vxs, colors, pathIndexs, numPath); }
public override void Draw(VertexStore vxs) { var v1 = GetFreeVxs(); gx.Render(stroke.MakeVxs(vxs, v1), this.strokeColor); ReleaseVxs(ref v1); }
public abstract void PaintSeries(VertexStore vxs, Color[] colors, int[] pathIndexs, int numPath);
public override void Draw(VertexStore vxs) { VxsHelper.DrawVxsSnap(_gfx, new VertexStoreSnap(vxs), _strokeColor); }
public static VertexStore TransformToVxs(this Bilinear bilinearTx, VertexStore src, VertexStore vxs) { int count = src.Count; VertexCmd cmd; double x, y; for (int i = 0; i < count; ++i) { cmd = src.GetVertex(i, out x, out y); bilinearTx.Transform(ref x, ref y); vxs.AddVertex(x, y, cmd); } return(vxs); }
void ReleaseVxs(VertexStore vxs) { this.myTmpImgRectVxs = vxs; vxs.Clear(); }
public static void TransformToVertexSnap(this Affine affine, VertexStore src, VertexStore output) { affine.TransformToVxs(src, output); }
/// <summary> /// we do NOT store vxs, return original outputVxs /// </summary> /// <param name="src"></param> /// <param name="outputVxs"></param> /// <returns></returns> public static VertexStore TransformToVxs(this Affine aff, VertexStoreSnap src, VertexStore outputVxs) { var snapIter = src.GetVertexSnapIter(); VertexCmd cmd; double x, y; while ((cmd = snapIter.GetNextVertex(out x, out y)) != VertexCmd.NoMore) { aff.Transform(ref x, ref y); outputVxs.AddVertex(x, y, cmd); } return(outputVxs); }
public static VertexStore TransformToVxs(this Perspective perspecitveTx, VertexStore src, VertexStore vxs) { VertexCmd cmd; double x, y; int count = src.Count; for (int i = 0; i < count; ++i) { cmd = src.GetVertex(i, out x, out y); perspecitveTx.Transform(ref x, ref y); vxs.AddVertex(x, y, cmd); } return(vxs); }
public VertexStore MakeVxs(VertexStore sourceVxs, VertexStore vxs) { StrokeGenerator strkgen = _strokeGen; int j = sourceVxs.Count; strkgen.Reset(); VertexCmd cmd; double x = 0, y = 0, startX = 0, startY = 0; for (int i = 0; i < j; ++i) { cmd = sourceVxs.GetVertex(i, out x, out y); switch (cmd) { case VertexCmd.NoMore: break; case VertexCmd.Close: if (i < j) { //close command strkgen.Close(); strkgen.WriteTo(vxs); strkgen.Reset(); } else { } break; case VertexCmd.CloseAndEndFigure: if (i < j) { //close command strkgen.Close(); strkgen.WriteTo(vxs); strkgen.Reset(); } else { } break; case VertexCmd.LineTo: case VertexCmd.P2c: //user must flatten the curve before do stroke case VertexCmd.P3c: //user must flatten the curve before do stroke strkgen.AddVertex(x, y, cmd); break; case VertexCmd.MoveTo: strkgen.AddVertex(x, y, cmd); startX = x; startY = y; break; default: throw new System.NotSupportedException(); } } strkgen.WriteTo(vxs); strkgen.Reset(); return(vxs); }
public StrokeGenerator() { m_stroker = new StrokeMath(); m_out_vertices = new VertexStore(); m_status = Status.Init; }
internal VertexSnapIter(VertexStoreSnap vsnap) { this.vxs = vsnap.GetInternalVxs(); this.currentIterIndex = vsnap.StartAt; }
public static VertexStore TransformToVxs(this Perspective perspecitveTx, VertexStoreSnap snap, VertexStore vxs) { var vsnapIter = snap.GetVertexSnapIter(); double x, y; VertexCmd cmd; do { cmd = vsnapIter.GetNextVertex(out x, out y); perspecitveTx.Transform(ref x, ref y); vxs.AddVertex(x, y, cmd); } while (!VertexHelper.IsEmpty(cmd)); return(vxs); }
public VertexStoreSnap(VertexStore vxs, int startAt) { this.vxs = vxs; this.startAt = startAt; }
//------------------------------------------------------------------- public void AddPath(VertexStore vxs) { this.AddPath(new VertexStoreSnap(vxs)); }
protected void Release(ref VertexStore vxs) { _vxsPool.Release(ref vxs); }
public static bool GetBoundingRect(VertexStore vxs, int[] gi, int num, out RectD boundingRect) { return(GetBoundingRect(vxs, gi, num, out boundingRect.Left, out boundingRect.Bottom, out boundingRect.Right, out boundingRect.Top)); }
/// <summary> /// we do NOT store vxs /// </summary> /// <param name="vxs"></param> /// <param name="c"></param> public void Render(VertexStore vxs, Drawing.Color c) { Render(new VertexStoreSnap(vxs), c); }
//---------------------------------- static bool GetBoundingRect(VertexStore vxs, int[] gi, int num, out double x1, out double y1, out double x2, out double y2) { int i; double x = 0; double y = 0; bool first = true; x1 = 1; y1 = 1; x2 = 0; y2 = 0; int iterindex = 0; for (i = 0; i < num; i++) { VertexCmd flags; while ((flags = vxs.GetVertex(iterindex++, out x, out y)) != VertexCmd.Stop) { switch (flags) { //if is vertext cmd case VertexCmd.LineTo: case VertexCmd.MoveTo: case VertexCmd.P2c: case VertexCmd.P3c: { if (first) { x1 = x; y1 = y; x2 = x; y2 = y; first = false; } else { if (x < x1) { x1 = x; } if (y < y1) { y1 = y; } if (x > x2) { x2 = x; } if (y > y2) { y2 = y; } } } break; } } } return(x1 <= x2 && y1 <= y2); }
public abstract void Fill(VertexStore vxs);
public override void CreateStroke(VertexStore orgVxs, float strokeW, VertexStore output) { _stroke.Width = strokeW; _stroke.MakeVxs(orgVxs, output); }
void ReleaseVxs(ref VertexStore v) { VectorToolBox.ReleaseVxs(ref v); }
public abstract void Draw(VertexStore vxs);
void ReleaseVxs(ref VertexStore vxs) { _vxsPool.Release(ref vxs); }
/// <summary> /// fill vxs, we do NOT store vxs /// </summary> /// <param name="vxs"></param> public override void Fill(VertexStore vxs) { sclineRas.AddPath(vxs); sclineRasToBmp.RenderWithColor(this.gx.DestImage, sclineRas, scline, fillColor); }
/// <summary> /// we do NOT store vxs /// </summary> /// <param name="vxs"></param> /// <param name="spanGen"></param> public void Fill(VertexStore vxs, ISpanGenerator spanGen) { this.sclineRas.AddPath(vxs); sclineRasToBmp.RenderWithSpan(this.gx.DestImage, sclineRas, scline, spanGen); }
public VertexStoreSnap(VertexStore vxs) { this.vxs = vxs; this.startAt = 0; }
static void RelaseVxs(ref VertexStore vxs) { s_vxsPool.Release(ref vxs); }
public void Release(ref VertexStore vxs) { vxs.Clear(); _stack.Push(vxs); vxs = null; }
public void WriteTo(VertexStore outputVxs) { this.Rewind(); double x = 0, y = 0; for (;;) { var cmd = GetNextVertex(ref x, ref y); outputVxs.AddVertex(x, y, cmd); if (cmd == VertexCmd.Stop) { break; } } }
//======= Crossings Multiply algorithm of InsideTest ======================== // // By Eric Haines, 3D/Eye Inc, [email protected] // // This version is usually somewhat faster than the original published in // Graphics Gems IV; by turning the division for testing the X axis crossing // into a tricky multiplication test this part of the test became faster, // which had the additional effect of making the test for "both to left or // both to right" a bit slower for triangles than simply computing the // intersection each time. The main increase is in triangle testing speed, // which was about 15% faster; all other polygon complexities were pretty much // the same as before. On machines where division is very expensive (not the // case on the HP 9000 series on which I tested) this test should be much // faster overall than the old code. Your mileage may (in fact, will) vary, // depending on the machine and the test data, but in general I believe this // code is both shorter and faster. This test was inspired by unpublished // Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson. // Related work by Samosky is in: // // Samosky, Joseph, "SectionView: A system for interactively specifying and // visualizing sections through three-dimensional medical image data", // M.S. Thesis, Department of Electrical Engineering and Computer Science, // Massachusetts Institute of Technology, 1993. // // Shoot a test ray along +X axis. The strategy is to compare vertex Y values // to the testing point's Y and quickly discard edges which are entirely to one // side of the test ray. Note that CONVEX and WINDING code can be added as // for the CrossingsTest() code; it is left out here for clarity. // // Input 2D polygon _pgon_ with _numverts_ number of vertices and test point // _point_, returns 1 if inside, 0 if outside. public static bool IsPointInVxs(VertexStore vxs, double tx, double ty) { int m_num_points = vxs.Count; if (m_num_points < 3) { return(false); } // if (!m_in_polygon_check) return false; int j; bool yflag0, yflag1, inside_flag; double vtx0, vty0, vtx1, vty1; vxs.GetVertex(m_num_points, out vtx0, out vty0); //vtx0 = GetXN(m_num_points - 1); //vty0 = GetYN(m_num_points - 1); // get test bit for above/below X axis yflag0 = (vty0 >= ty); //vtx1 = GetXN(0); //vty1 = GetYN(0); vxs.GetVertex(0, out vtx1, out vty1); inside_flag = false; for (j = 1; j <= m_num_points; ++j) { yflag1 = (vty1 >= ty); // Check if endpoints straddle (are on opposite sides) of X axis // (i.e. the Y's differ); if so, +X ray could intersect this edge. // The old test also checked whether the endpoints are both to the // right or to the left of the test point. However, given the faster // intersection point computation used below, this test was found to // be a break-even proposition for most polygons and a loser for // triangles (where 50% or more of the edges which survive this test // will cross quadrants and so have to have the X intersection computed // anyway). I credit Joseph Samosky with inspiring me to try dropping // the "both left or both right" part of my code. if (yflag0 != yflag1) { // Check intersection of pgon segment with +X ray. // Note if >= point's X; if so, the ray hits it. // The division operation is avoided for the ">=" test by checking // the sign of the first vertex wrto the test point; idea inspired // by Joseph Samosky's and Mark Haigh-Hutchinson's different // polygon inclusion tests. if (((vty1 - ty) * (vtx0 - vtx1) >= (vtx1 - tx) * (vty0 - vty1)) == yflag1) { inside_flag = !inside_flag; } } // Move to the next pair of vertices, retaining info as possible. yflag0 = yflag1; vtx0 = vtx1; vty0 = vty1; int k = (j >= m_num_points) ? j - m_num_points : j; //vtx1 = GetXN(k); //vty1 = GetYN(k); vxs.GetVertex(k, out vtx1, out vty1); } return(inside_flag); }
static Affine BuildImageBoundsPath(int srcW, int srcH, VertexStore drawImageRectPath, AffinePlan[] affPlans) { drawImageRectPath.Clear(); drawImageRectPath.AddMoveTo(0, 0); drawImageRectPath.AddLineTo(srcW, 0); drawImageRectPath.AddLineTo(srcW, srcH); drawImageRectPath.AddLineTo(0, srcH); drawImageRectPath.AddCloseFigure(); return Affine.NewMatix(affPlans); }
/// <summary> /// we do NOT store vxs /// </summary> /// <param name="vxs"></param> /// <param name="spanGen"></param> void Render(VertexStore vxs, ISpanGenerator spanGen) { sclineRas.AddPath(vxs); sclineRasToBmp.RenderWithSpan( destImageReaderWriter, sclineRas, sclinePack8, spanGen); }
/// <summary> /// we do NOT store snap/vxs /// </summary> /// <param name="vxs"></param> public override void Fill(VertexStore vxs) { VxsHelper.FillVxsSnap(_skCanvas, new VertexStoreSnap(vxs), _fill); }
public static VertexStoreSnap MakeVertexSnap(this Ellipse ellipse, VertexStore vxs) { return(new VertexStoreSnap(MakeVxs(ellipse, vxs))); }
public override void Draw(VertexStore vxs) { VxsHelper.DrawVxsSnap(_skCanvas, new VertexStoreSnap(vxs), _stroke); }
public override void PaintSeries(VertexStore vxs, Color[] colors, int[] pathIndexs, int numPath) { var prevColor = FillColor; for (int i = 0; i < numPath; ++i) { _fill.Color = ConvToSkColor(colors[i]); VxsHelper.FillVxsSnap(_skCanvas, new VertexStoreSnap(vxs, pathIndexs[i]), _fill); } FillColor = prevColor; }
public override void Render(IImageReaderWriter source, double destX, double destY, double angleRadians, double inScaleX, double inScaleY) { { // exit early if the dest and source bounds don't touch. // TODO: <BUG> make this do rotation and scalling RectInt sourceBounds = source.GetBounds(); RectInt destBounds = this.destImageReaderWriter.GetBounds(); sourceBounds.Offset((int)destX, (int)destY); if (!RectInt.DoIntersect(sourceBounds, destBounds)) { if (inScaleX != 1 || inScaleY != 1 || angleRadians != 0) { throw new NotImplementedException(); } return; } } double scaleX = inScaleX; double scaleY = inScaleY; Affine graphicsTransform = this.CurrentTransformMatrix; if (!graphicsTransform.IsIdentity()) { if (scaleX != 1 || scaleY != 1 || angleRadians != 0) { throw new NotImplementedException(); } graphicsTransform.Transform(ref destX, ref destY); } #if false // this is an optomization that eliminates the drawing of images that have their alpha set to all 0 (happens with generated images like explosions). MaxAlphaFrameProperty maxAlphaFrameProperty = MaxAlphaFrameProperty::GetMaxAlphaFrameProperty(source); if ((maxAlphaFrameProperty.GetMaxAlpha() * color.A_Byte) / 256 <= ALPHA_CHANNEL_BITS_DIVISOR) { m_OutFinalBlitBounds.SetRect(0, 0, 0, 0); } #endif bool isScale = (scaleX != 1 || scaleY != 1); bool isRotated = true; if (Math.Abs(angleRadians) < (0.1 * MathHelper.Tau / 360)) { isRotated = false; angleRadians = 0; } //bool IsMipped = false; //double ox, oy; //source.GetOriginOffset(out ox, out oy); bool canUseMipMaps = isScale; if (scaleX > 0.5 || scaleY > 0.5) { canUseMipMaps = false; } bool renderRequriesSourceSampling = isScale || isRotated || destX != (int)destX || destY != (int)destY; VertexStore imgBoundsPath = GetFreeVxs(); // this is the fast drawing path if (renderRequriesSourceSampling) { // if the scalling is small enough the results can be improved by using mip maps //if(CanUseMipMaps) //{ // CMipMapFrameProperty* pMipMapFrameProperty = CMipMapFrameProperty::GetMipMapFrameProperty(source); // double OldScaleX = scaleX; // double OldScaleY = scaleY; // const CFrameInterface* pMippedFrame = pMipMapFrameProperty.GetMipMapFrame(ref scaleX, ref scaleY); // if(pMippedFrame != source) // { // IsMipped = true; // source = pMippedFrame; // sourceOriginOffsetX *= (OldScaleX / scaleX); // sourceOriginOffsetY *= (OldScaleY / scaleY); // } // HotspotOffsetX *= (inScaleX / scaleX); // HotspotOffsetY *= (inScaleY / scaleY); //} Affine destRectTransform = BuildImageBoundsPath(source, imgBoundsPath, destX, destY, ox, oy, scaleX, scaleY, angleRadians); // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] Affine sourceRectTransform = destRectTransform.CreateInvert(); var interpolator = new SpanInterpolatorLinear(sourceRectTransform); var imgSpanGen = new ImgSpanGenRGBA_BilinearClip(source, ColorRGBA.Black, interpolator); Render(destRectTransform.TransformToVxs(imgBoundsPath), imgSpanGen); // this is some debug you can enable to visualize the dest bounding box //LineFloat(BoundingRect.left, BoundingRect.top, BoundingRect.right, BoundingRect.top, WHITE); //LineFloat(BoundingRect.right, BoundingRect.top, BoundingRect.right, BoundingRect.bottom, WHITE); //LineFloat(BoundingRect.right, BoundingRect.bottom, BoundingRect.left, BoundingRect.bottom, WHITE); //LineFloat(BoundingRect.left, BoundingRect.bottom, BoundingRect.left, BoundingRect.top, WHITE); } else // TODO: this can be even faster if we do not use an intermediat buffer { Affine destRectTransform = BuildImageBoundsPath(source, imgBoundsPath, destX, destY); // We invert it because it is the transform to make the image go to the same position as the polygon. LBB [2/24/2004] Affine sourceRectTransform = destRectTransform.CreateInvert(); var interpolator = new SpanInterpolatorLinear(sourceRectTransform); ImgSpanGen imgSpanGen = null; switch (source.BitDepth) { case 32: imgSpanGen = new ImgSpanGenRGBA_NN_StepXBy1(source, interpolator); break; case 24: imgSpanGen = new ImgSpanGenRGB_NNStepXby1(source, interpolator); break; case 8: imgSpanGen = new ImgSpanGenGray_NNStepXby1(source, interpolator); break; default: throw new NotImplementedException(); } Render(destRectTransform.TransformToVxs(imgBoundsPath), imgSpanGen); unchecked { destImageChanged++; }; } ReleaseVxs(imgBoundsPath); }
/// <summary> /// we do NOT store snap/vxs /// </summary> /// <param name="vxs"></param> public override void Fill(VertexStore vxs) { VxsHelper.FillVxsSnap(_gfx, new VertexStoreSnap(vxs), _fillColor); }
public static VertexStore MakeVxs(this Ellipse ellipse, VertexStore vxs) { //TODO: review here return(VertexStoreBuilder.CreateVxs(GetVertexIter(ellipse), vxs)); }
public override void PaintSeries(VertexStore vxs, Color[] colors, int[] pathIndexs, int numPath) { for (int i = 0; i < numPath; ++i) { VxsHelper.FillVxsSnap(_gfx, new VertexStoreSnap(vxs, pathIndexs[i]), colors[i]); } }
public void CreateJoin(VertexStore output, Vertex2d v0, Vertex2d v1, Vertex2d v2, double len1, double len2) { double dx1 = m_width * (v1.y - v0.y) / len1; double dy1 = m_width * (v1.x - v0.x) / len1; double dx2 = m_width * (v2.y - v1.y) / len2; double dy2 = m_width * (v2.x - v1.x) / len2; output.Clear(); double cp = AggMath.Cross(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y); if (cp != 0 && (cp > 0) == (m_width > 0)) { // Inner join //--------------- double limit = ((len1 < len2) ? len1 : len2) / m_width_abs; if (limit < m_inner_miter_limit) { limit = m_inner_miter_limit; } switch (m_inner_join) { default: // inner_bevel AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x + dx2, v1.y - dy2); break; case InnerJoin.Miter: CreateMiter(output, v0, v1, v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0); break; case InnerJoin.Jag: case InnerJoin.Round: cp = (dx1 - dx2) * (dx1 - dx2) + (dy1 - dy2) * (dy1 - dy2); if (cp < len1 * len1 && cp < len2 * len2) { CreateMiter(output, v0, v1, v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0); } else { if (m_inner_join == InnerJoin.Jag) { AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x, v1.y); AddVertex(output, v1.x + dx2, v1.y - dy2); } else { AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x, v1.y); CreateArc(output, v1.x, v1.y, dx2, -dy2, dx1, -dy1); AddVertex(output, v1.x, v1.y); AddVertex(output, v1.x + dx2, v1.y - dy2); } } break; } } else { // Outer join //--------------- // Calculate the distance between v1 and // the central point of the bevel line segment //--------------- double dx = (dx1 + dx2) / 2; double dy = (dy1 + dy2) / 2; double dbevel = Math.Sqrt(dx * dx + dy * dy); if (m_line_join == LineJoin.Round || m_line_join == LineJoin.Bevel) { // This is an optimization that reduces the number of points // in cases of almost collinear segments. If there's no // visible difference between bevel and miter joins we'd rather // use miter join because it adds only one point instead of two. // // Here we calculate the middle point between the bevel points // and then, the distance between v1 and this middle point. // At outer joins this distance always less than stroke width, // because it's actually the height of an isosceles triangle of // v1 and its two bevel points. If the difference between this // width and this value is small (no visible bevel) we can // add just one point. // // The constant in the expression makes the result approximately // the same as in round joins and caps. You can safely comment // out this entire "if". //------------------- if (m_approx_scale * (m_width_abs - dbevel) < m_width_eps) { if (AggMath.CalcIntersect(v0.x + dx1, v0.y - dy1, v1.x + dx1, v1.y - dy1, v1.x + dx2, v1.y - dy2, v2.x + dx2, v2.y - dy2, out dx, out dy)) { AddVertex(output, dx, dy); } else { AddVertex(output, v1.x + dx1, v1.y - dy1); } return; } } switch (m_line_join) { case LineJoin.Miter: case LineJoin.MiterRevert: case LineJoin.MiterRound: CreateMiter(output, v0, v1, v2, dx1, dy1, dx2, dy2, m_line_join, m_miter_limit, dbevel); break; case LineJoin.Round: CreateArc(output, v1.x, v1.y, dx1, -dy1, dx2, -dy2); break; default: // Bevel join AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x + dx2, v1.y - dy2); break; } } }
//------------------------------------------------------------------------ /// <summary> /// we do NOT store vxs /// </summary> /// <param name="vxs"></param> /// <param name="c"></param> public void Render(VertexStore vxs, Drawing.Color c) { Render(new VertexStoreSnap(vxs), c); }
static void AddVertex(VertexStore output, double x, double y) { output.AddVertex(x, y, VertexCmd.LineTo); }
void CreateMiter(VertexStore output, Vertex2d v0, Vertex2d v1, Vertex2d v2, double dx1, double dy1, double dx2, double dy2, LineJoin lj, double mlimit, double dbevel) { double xi = v1.x; double yi = v1.y; double di = 1; double lim = m_width_abs * mlimit; bool miter_limit_exceeded = true; // Assume the worst bool intersection_failed = true; // Assume the worst if (AggMath.CalcIntersect(v0.x + dx1, v0.y - dy1, v1.x + dx1, v1.y - dy1, v1.x + dx2, v1.y - dy2, v2.x + dx2, v2.y - dy2, out xi, out yi)) { // Calculation of the intersection succeeded //--------------------- di = AggMath.calc_distance(v1.x, v1.y, xi, yi); if (di <= lim) { // Inside the miter limit //--------------------- AddVertex(output, xi, yi); miter_limit_exceeded = false; } intersection_failed = false; } else { // Calculation of the intersection failed, most probably // the three points lie one straight line. // First check if v0 and v2 lie on the opposite sides of vector: // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular // to the line determined by vertices v0 and v1. // This condition determines whether the next line segments continues // the previous one or goes back. //---------------- double x2 = v1.x + dx1; double y2 = v1.y - dy1; if ((AggMath.Cross(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0.0) == (AggMath.Cross(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0.0)) { // This case means that the next segment continues // the previous one (straight line) //----------------- AddVertex(output, v1.x + dx1, v1.y - dy1); miter_limit_exceeded = false; } } if (miter_limit_exceeded) { // Miter limit exceeded //------------------------ switch (lj) { case LineJoin.MiterRevert: // For the compatibility with SVG, PDF, etc, // we use a simple bevel join instead of // "smart" bevel //------------------- AddVertex(output, v1.x + dx1, v1.y - dy1); AddVertex(output, v1.x + dx2, v1.y - dy2); break; case LineJoin.MiterRound: CreateArc(output, v1.x, v1.y, dx1, -dy1, dx2, -dy2); break; default: // If no miter-revert, calculate new dx1, dy1, dx2, dy2 //---------------- if (intersection_failed) { mlimit *= m_width_sign; AddVertex(output, v1.x + dx1 + dy1 * mlimit, v1.y - dy1 + dx1 * mlimit); AddVertex(output, v1.x + dx2 - dy2 * mlimit, v1.y - dy2 - dx2 * mlimit); } else { double x1 = v1.x + dx1; double y1 = v1.y - dy1; double x2 = v1.x + dx2; double y2 = v1.y - dy2; di = (lim - dbevel) / (di - dbevel); AddVertex(output, x1 + (xi - x1) * di, y1 + (yi - y1) * di); AddVertex(output, x2 + (xi - x2) * di, y2 + (yi - y2) * di); } break; } } }
public VertexStore GetUnscaledVxs() { return(VertexStore.CreateCopy(ps.Vxs)); }