/// <summary> /// Creates an AffineTransformation defined by a mapping between two baselines. /// The computed transformation consists of: /// <list type="bullet"> /// <item><description>a translation from the start point of the source baseline to the start point of the destination baseline,</description></item> /// <item><description>a rotation through the angle between the baselines about the destination start point,</description></item> /// <item><description>and a scaling equal to the ratio of the baseline lengths.</description></item> /// </list> /// If the source baseline has zero length, an identity transformation is returned. /// </summary> /// <param name="src0">The start point of the source baseline</param> /// <param name="src1">The end point of the source baseline</param> /// <param name="dest0">The start point of the destination baseline</param> /// <param name="dest1">The end point of the destination baseline</param> /// <returns></returns> public static AffineTransformation CreateFromBaseLines( Coordinate src0, Coordinate src1, Coordinate dest0, Coordinate dest1) { var rotPt = new Coordinate(src0.X + dest1.X - dest0.X, src0.Y + dest1.Y - dest0.Y); double ang = AngleUtility.AngleBetweenOriented(src1, src0, rotPt); double srcDist = src1.Distance(src0); double destDist = dest1.Distance(dest0); // return identity if transformation would be degenerate if (srcDist == 0.0) { return(new AffineTransformation()); } double scale = destDist / srcDist; var trans = AffineTransformation.TranslationInstance( -src0.X, -src0.Y); trans.Rotate(ang); trans.Scale(scale, scale); trans.Translate(dest0.X, dest0.Y); return(trans); }
/// <summary> /// Creates an AffineTransformation defined by a pair of control vectors. A /// control vector consists of a source point and a destination point, which is /// the image of the source point under the desired transformation. The /// computed transformation is a combination of one or more of a uniform scale, /// a rotation, and a translation (i.e. there is no shear component and no /// reflection) /// </summary> /// <param name="src0"></param> /// <param name="src1"></param> /// <param name="dest0"></param> /// <param name="dest1"></param> /// <returns>The computed transformation</returns> /// <returns><c>null</c> if the control vectors do not determine a well-defined transformation</returns> public static AffineTransformation CreateFromControlVectors(Coordinate src0, Coordinate src1, Coordinate dest0, Coordinate dest1) { var rotPt = new Coordinate(dest1.X - dest0.X, dest1.Y - dest0.Y); double ang = AngleUtility.AngleBetweenOriented(src1, src0, rotPt); double srcDist = src1.Distance(src0); double destDist = dest1.Distance(dest0); if (srcDist == 0.0) { return(null); } double scale = destDist / srcDist; var trans = AffineTransformation.TranslationInstance( -src0.X, -src0.Y); trans.Rotate(ang); trans.Scale(scale, scale); trans.Translate(dest0.X, dest0.Y); return(trans); }
///<summary> /// Adds a limited mitre join connecting the two reflex offset segments. ///</summary> /// <remarks> /// A limited mitre is a mitre which is beveled at the distance /// determined by the mitre ratio limit. /// </remarks> /// <param name="offset0">The first offset segment</param> /// <param name="offset1">The second offset segment</param> /// <param name="distance">The offset distance</param> /// <param name="mitreLimit">The mitre limit ratio</param> private void AddLimitedMitreJoin( LineSegment offset0, LineSegment offset1, double distance, double mitreLimit) { Coordinate basePt = _seg0.P1; double ang0 = AngleUtility.Angle(basePt, _seg0.P0); double ang1 = AngleUtility.Angle(basePt, _seg1.P1); // oriented angle between segments double angDiff = AngleUtility.AngleBetweenOriented(_seg0.P0, basePt, _seg1.P1); // half of the interior angle double angDiffHalf = angDiff / 2; // angle for bisector of the interior angle between the segments double midAng = AngleUtility.Normalize(ang0 + angDiffHalf); // rotating this by PI gives the bisector of the reflex angle double mitreMidAng = AngleUtility.Normalize(midAng + Math.PI); // the miterLimit determines the distance to the mitre bevel double mitreDist = mitreLimit * distance; // the bevel delta is the difference between the buffer distance // and half of the length of the bevel segment double bevelDelta = mitreDist * Math.Abs(Math.Sin(angDiffHalf)); double bevelHalfLen = distance - bevelDelta; // compute the midpoint of the bevel segment double bevelMidX = basePt.X + mitreDist * Math.Cos(mitreMidAng); double bevelMidY = basePt.Y + mitreDist * Math.Sin(mitreMidAng); Coordinate bevelMidPt = new Coordinate(bevelMidX, bevelMidY); // compute the mitre midline segment from the corner point to the bevel segment midpoint LineSegment mitreMidLine = new LineSegment(basePt, bevelMidPt); // finally the bevel segment endpoints are computed as offsets from // the mitre midline Coordinate bevelEndLeft = mitreMidLine.PointAlongOffset(1.0, bevelHalfLen); Coordinate bevelEndRight = mitreMidLine.PointAlongOffset(1.0, -bevelHalfLen); if (_side == Positions.Left) { _vertexList.AddPt(bevelEndLeft); _vertexList.AddPt(bevelEndRight); } else { _vertexList.AddPt(bevelEndRight); _vertexList.AddPt(bevelEndLeft); } }