internal static float ConvertSweepAngle(float sweepAngle, float startAngle, SpatialTransform transform, CoordinateSystem targetSystem) { PointF x = new PointF(100, 0); PointF[] startVector = new PointF[] { x }; Matrix rotation = new Matrix(); rotation.Rotate(startAngle); rotation.TransformVectors(startVector); PointF[] sweepVector = (PointF[])startVector.Clone(); rotation.Reset(); rotation.Rotate(sweepAngle); rotation.TransformVectors(sweepVector); rotation.Dispose(); SizeF startVectorTransformed, sweepVectorTransformed; if (targetSystem == Graphics.CoordinateSystem.Destination) { startVectorTransformed = transform.ConvertToDestination(new SizeF(startVector[0])); sweepVectorTransformed = transform.ConvertToDestination(new SizeF(sweepVector[0])); } else { startVectorTransformed = transform.ConvertToSource(new SizeF(startVector[0])); sweepVectorTransformed = transform.ConvertToSource(new SizeF(sweepVector[0])); } // simply return the angle between the start and sweep angle, in the target system. return((int)Math.Round(Vector.SubtendedAngle(sweepVectorTransformed.ToPointF(), PointF.Empty, startVectorTransformed.ToPointF()))); }
partial void TransformVectorsImpl(PointF [] pts) { var tmp = ToSDDPoints(pts); t.TransformVectors(tmp); Copy(tmp, pts); }
// Draw the graph. private void DrawGraph(Graphics gr) { // Map to turn right-side up and center at the origin. const float wxmin = -10; const float wymin = -10; const float wxmax = 10; const float wymax = 10; RectangleF rect = new RectangleF(wxmin, wymin, wxmax - wxmin, wymax - wymin); PointF[] pts = { new PointF(0, graphPictureBox.ClientSize.Height), new PointF(graphPictureBox.ClientSize.Width, graphPictureBox.ClientSize.Height), new PointF(0, 0), }; Matrix transform = new Matrix(rect, pts); gr.Transform = transform; // See how far it is between horizontal pixels in world coordinates. pts = new PointF[] { new PointF(1, 0) }; transform.Invert(); transform.TransformVectors(pts); float dx = pts[0].X; // Generate points on the curve. List<PointF> points = new List<PointF>(); for (float x = wxmin; x <= wxmax; x += dx) points.Add(new PointF(x, TheFunction(x))); // Use a thin pen. using (Pen thin_pen = new Pen(Color.Gray, 0)) { // Draw the coordinate axes. gr.DrawLine(thin_pen, wxmin, 0, wxmax, 0); gr.DrawLine(thin_pen, 0, wymin, 0, wymax); for (float x = wxmin; x <= wxmax; x++) gr.DrawLine(thin_pen, x, -0.5f, x, 0.5f); for (float y = wymin; y <= wymax; y++) gr.DrawLine(thin_pen, -0.5f, y, 0.5f, y); // Draw the graph. thin_pen.Color = Color.Red; //thin_pen.Color = Color.Black; gr.DrawLines(thin_pen, points.ToArray()); } }
internal static float ConvertStartAngle(float angle, SpatialTransform transform, CoordinateSystem targetSystem) { PointF xVector = new PointF(100, 0); Matrix rotation = new Matrix(); PointF[] angleVector = new PointF[] { xVector }; rotation.Rotate(angle); rotation.TransformVectors(angleVector); rotation.Dispose(); SizeF xVectorTransformed, angleVectorTransformed; if (targetSystem == Graphics.CoordinateSystem.Destination) { xVectorTransformed = transform.ConvertToDestination(new SizeF(xVector)); angleVectorTransformed = transform.ConvertToDestination(new SizeF(angleVector[0])); } else { xVectorTransformed = transform.ConvertToSource(new SizeF(xVector)); angleVectorTransformed = transform.ConvertToSource(new SizeF(angleVector[0])); } float xRotationOffset = (int)Math.Round(Vector.SubtendedAngle(xVectorTransformed.ToPointF(), PointF.Empty, xVector)); float angleTransformed = (int)Math.Round(Vector.SubtendedAngle(angleVectorTransformed.ToPointF(), PointF.Empty, xVectorTransformed.ToPointF())); // have to figure out where x-axis moved to and then return the difference between the angle // and the x-axis, where both are in 'target' coordinates. float returnAngle = angleTransformed + xRotationOffset; if (returnAngle < 0) { returnAngle += 360; } return(returnAngle); }
public static PointF MatrixByVector(Matrix M, PointF P) { PointFSingleArray[0] = P; M.TransformVectors(PointFSingleArray); return PointFSingleArray[0]; }
private void updateImage() { List<PointF> boundingBox = null; List<PointF> hull = null; mOriginalImageReference = BrickLibrary.Instance.getImage(mPartNumber, ref boundingBox, ref hull); // check if the image is not in the library, create one if (mOriginalImageReference == null) { // add a default image in the library and ask it again BrickLibrary.Instance.AddUnknownBrick(mPartNumber, (int)(mDisplayArea.Width), (int)(mDisplayArea.Height)); mOriginalImageReference = BrickLibrary.Instance.getImage(mPartNumber, ref boundingBox, ref hull); } // normally now, we should have an image // transform the bounding box of the part PointF[] boundingPoints = boundingBox.ToArray(); Matrix rotation = new Matrix(); rotation.Rotate(mOrientation); rotation.TransformVectors(boundingPoints); // get the min, max and the size of the bounding box PointF boundingMin = new PointF(); PointF boundingMax = new PointF(); PointF boundingSize = sGetMinMaxAndSize(boundingPoints, ref boundingMin, ref boundingMax); // store computationnal variable for optimization const float PIXEL_TO_STUD_RATIO = 1.0f / NUM_PIXEL_PER_STUD_FOR_BRICKS; // transform the hull to get the selection area PointF[] hullArray = hull.ToArray(); rotation.TransformVectors(hullArray); // check if this picture has a specific hull if (hull != boundingBox) { // get the bounding size from the hull PointF hullMin = new PointF(); PointF hullMax = new PointF(); PointF hullSize = sGetMinMaxAndSize(hullArray, ref hullMin, ref hullMax); // compute the offset between the hull and the normal bounding box PointF deltaMin = new PointF(boundingMin.X - hullMin.X, boundingMin.Y - hullMin.Y); PointF deltaMax = new PointF(boundingMax.X - hullMax.X, boundingMax.Y - hullMax.Y); mOffsetFromOriginalImage = new PointF((deltaMax.X + deltaMin.X) * PIXEL_TO_STUD_RATIO * 0.5f, (deltaMax.Y + deltaMin.Y) * PIXEL_TO_STUD_RATIO * 0.5f); // overwrite the bounding size and min with the hull ones which are more precise boundingSize = hullSize; boundingMin = hullMin; } else { mOffsetFromOriginalImage = new PointF(0, 0); } // set the size of the display area with the new computed bounding size, and recompute the snap to grid offset mDisplayArea.Width = boundingSize.X * PIXEL_TO_STUD_RATIO; mDisplayArea.Height = boundingSize.Y * PIXEL_TO_STUD_RATIO; mTopLeftCornerInPixel = new PointF(-boundingMin.X, -boundingMin.Y); // adjust the selection area after computing the new display area size to have a correct center // first we add the translation of the top left corner in pixel to the hull point already in pixel // then convert the pixel to studs, and finally add the top left corner in stud Matrix translation = new Matrix(); translation.Translate(mTopLeftCornerInPixel.X, mTopLeftCornerInPixel.Y); translation.Scale(PIXEL_TO_STUD_RATIO, PIXEL_TO_STUD_RATIO, MatrixOrder.Append); translation.Translate(Center.X - (mDisplayArea.Width * 0.5f), Center.Y - (mDisplayArea.Height * 0.5f), MatrixOrder.Append); translation.TransformPoints(hullArray); // create the new selection area from the rotated hull mSelectionArea = new Tools.Polygon(hullArray); // clear the new images array for all the levels clearMipmapImages(0, mMipmapImages.Length - 1); }
//take a world vector and make it a relative vector public Vector WorldToRelative(Vector world) { Matrix mat = new Matrix(); PointF[] vectors = new PointF[1]; vectors[0].X = world.X; vectors[0].Y = world.Y; mat.Rotate(-m_angle / (float)Math.PI * 180.0f); mat.TransformVectors(vectors); return new Vector(vectors[0].X, vectors[0].Y); }
public void SetSteeringAngle(float newAngle) { Matrix mat = new Matrix(); PointF[] vectors = new PointF[2]; //foward vector vectors[0].X = 0; vectors[0].Y = 1; //side vector vectors[1].X = -1; vectors[1].Y = 0; mat.Rotate(newAngle / (float)Math.PI * 180.0f); mat.TransformVectors(vectors); m_forwardAxis = new Vector(vectors[0].X, vectors[0].Y); m_sideAxis = new Vector(vectors[1].X, vectors[1].Y); }
internal static float ConvertSweepAngle(float sweepAngle, float startAngle, SpatialTransform transform, CoordinateSystem targetSystem) { PointF x = new PointF(100, 0); PointF[] startVector = new PointF[] { x }; Matrix rotation = new Matrix(); rotation.Rotate(startAngle); rotation.TransformVectors(startVector); PointF[] sweepVector = (PointF[])startVector.Clone(); rotation.Reset(); rotation.Rotate(sweepAngle); rotation.TransformVectors(sweepVector); rotation.Dispose(); SizeF startVectorTransformed, sweepVectorTransformed; if (targetSystem == Graphics.CoordinateSystem.Destination) { startVectorTransformed = transform.ConvertToDestination(new SizeF(startVector[0])); sweepVectorTransformed = transform.ConvertToDestination(new SizeF(sweepVector[0])); } else { startVectorTransformed = transform.ConvertToSource(new SizeF(startVector[0])); sweepVectorTransformed = transform.ConvertToSource(new SizeF(sweepVector[0])); } // simply return the angle between the start and sweep angle, in the target system. return (int)Math.Round(Vector.SubtendedAngle(sweepVectorTransformed.ToPointF(), PointF.Empty, startVectorTransformed.ToPointF())); }
public void rotate(Matrix matrix) { PointF[] vector = { mLocalAttachOffsetFromCenter }; matrix.TransformVectors(vector); mWorldAttachOffsetFromCenter = vector[0]; }
// Transform X-distance via a transform. protected float TransformDistance(float distance, Matrix xform) { PointF[] vectors = { new PointF(distance, 0) }; xform.TransformVectors(vectors); return (float) Math.Sqrt(vectors[0].X * vectors[0].X + vectors[0].Y * vectors[0].Y); }
/// <summary> /// Draw some debug information like the chain bones /// </summary> /// <param name="g">the graphic context in which draw</param> /// <param name="areaInStud">the area in which draw</param> /// <param name="scalePixelPerStud">the current scale</param> public void draw(Graphics g, RectangleF areaInStud, double scalePixelPerStud) { int lastIndex = mBoneList.Count - 1; // draw the bones for (int i = 0; i < lastIndex; i++) { IKSolver.Bone_2D_CCD firstBone = mBoneList[i]; IKSolver.Bone_2D_CCD nextBone = mBoneList[i+1]; g.DrawLine(Pens.Black, (float)((firstBone.worldX - areaInStud.Left) * scalePixelPerStud), (float)((-firstBone.worldY - areaInStud.Top) * scalePixelPerStud), (float)((nextBone.worldX - areaInStud.Left) * scalePixelPerStud), (float)((-nextBone.worldY - areaInStud.Top) * scalePixelPerStud)); } // the last bone vector LayerBrick.Brick.ConnectionPoint endConnection = mBoneList[lastIndex].connectionPoint; if (!endConnection.IsFree) { // rotate the second target vector according to the orientation of the snapped connection PointF[] translation = { mLastBoneVector }; Matrix rotation = new Matrix(); rotation.Rotate(endConnection.ConnectedBrick.Orientation + endConnection.ConnectionLink.Angle + 180); rotation.TransformVectors(translation); // draw the translation IKSolver.Bone_2D_CCD lastBone = mBoneList[mBoneList.Count - 1]; float endX = (float)(lastBone.worldX - areaInStud.Left); float endY = (float)(-lastBone.worldY - areaInStud.Top); g.DrawLine(Pens.Green, endX * (float)scalePixelPerStud, endY * (float)scalePixelPerStud, (endX + translation[0].X) * (float)scalePixelPerStud, (endY + translation[0].Y) * (float)scalePixelPerStud); } }
protected void rotate(Layer.LayerItem item, Matrix rotation, float rotationAngle, bool adjustPivot) { // get the pivot point of the part before the rotation PointF pivot = item.Pivot; // change the orientation of the picture item.Orientation = (item.Orientation + rotationAngle); // for some items partially attached, we may don't want to adjust the pivot if (adjustPivot) { // adjust the position of the pivot for a group of items if (mItems.Count > 1) { PointF[] points = { new PointF(pivot.X - mCenter.X, pivot.Y - mCenter.Y) }; rotation.TransformVectors(points); // assign the new position pivot.X = mCenter.X + points[0].X; pivot.Y = mCenter.Y + points[0].Y; } // assign the new pivot position after rotation item.Pivot = pivot; } }
SizeF Transform(Matrix m, SizeF s) { PointF[] ps = new PointF[] { new PointF(s.Width, s.Height) }; m.TransformVectors(ps); return new SizeF(ps[0].X, ps[0].Y); }
public void TransformVectors () { Matrix matrix = new Matrix (2, 4, 6, 8, 10, 12); PointF [] pointsF = new PointF [] {new PointF (2, 4), new PointF (4, 8)}; matrix.TransformVectors (pointsF); Assert.AreEqual (28, pointsF[0].X, "N#1"); Assert.AreEqual (40, pointsF[0].Y, "N#2"); Assert.AreEqual (56, pointsF[1].X, "N#3"); Assert.AreEqual (80, pointsF[1].Y, "N#4"); Point [] points = new Point [] {new Point (2, 4), new Point (4, 8)}; matrix.TransformVectors (points); Assert.AreEqual (28, pointsF[0].X, "N#5"); Assert.AreEqual (40, pointsF[0].Y, "N#6"); Assert.AreEqual (56, pointsF[1].X, "N#7"); Assert.AreEqual (80, pointsF[1].Y, "N#8"); }
public bool RotateImage( string sourceImagePath, int rotationDegree ) { try { string fileExt = Path.GetExtension( sourceImagePath ).ToLowerInvariant().Trim( '.' ); // create file name of the temp file string tempFilePath = getTempFileName( sourceImagePath, fileExt ); using( Bitmap inputImage = new Bitmap( sourceImagePath ) ) { EncoderParameters encParams = null; var encoder = getEncoder( fileExt, out encParams ); // calculate width and height of the new image float iW = (float)inputImage.Width; float iH = (float)inputImage.Height; Matrix whRotation = new Matrix(); whRotation.Rotate( rotationDegree ); // rotate every vertex of our "image rectangle" var tmpDims = new PointF[] { new PointF(0,0), new PointF( iW, 0 ), new PointF( iW, iH ), new PointF( 0, iH ) }; whRotation.TransformVectors( tmpDims ); // find extends iW = Math.Abs( tmpDims.Max( x => x.X ) - tmpDims.Min( x => x.X ) ); iH = Math.Abs( tmpDims.Max( x => x.Y ) - tmpDims.Min( x => x.Y ) ); using( Bitmap tempBmp = new Bitmap( (int)Math.Ceiling( iW ), (int)Math.Ceiling( iH ) ) ) { // rotate image tempBmp.SetResolution( inputImage.HorizontalResolution, inputImage.VerticalResolution ); using( Graphics g = Graphics.FromImage( tempBmp ) ) { g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear; // rotate at the center g.TranslateTransform( tempBmp.Width/2, tempBmp.Height/2 ); g.RotateTransform( rotationDegree ); g.TranslateTransform( -tempBmp.Width / 2, -tempBmp.Height / 2 ); g.DrawImage( inputImage, new Point( ( tempBmp.Width - inputImage.Width ) / 2, ( tempBmp.Height - inputImage.Height ) / 2 ) ); } tempBmp.Save( tempFilePath, encoder, encParams ); } } // now replace images File.Delete( sourceImagePath ); File.Move( tempFilePath, sourceImagePath ); return true; } catch { return false; } }
private void updateBitmap(bool redrawImage) { // create a bitmap if the text is not empty if (mText != "") { // create a font to mesure the text Font textFont = new Font(mTextFont.FontFamily, mTextFont.Size, mTextFont.Style); Graphics graphics = Graphics.FromImage(mImage); SizeF textFontSize = graphics.MeasureString(mText, textFont); float halfWidth = textFontSize.Width * 0.5f; float halfHeight = textFontSize.Height * 0.5f; Matrix rotation = new Matrix(); rotation.Rotate(mOrientation); // compute the rotated corners PointF[] corners = new PointF[] { new PointF(-halfWidth, -halfHeight), new PointF(-halfWidth, halfHeight), new PointF(halfWidth, halfHeight), new PointF(halfWidth, -halfHeight) }; rotation.TransformVectors(corners); PointF min = corners[0]; PointF max = corners[0]; for (int i = 1; i < 4; ++i) { if (corners[i].X < min.X) min.X = corners[i].X; if (corners[i].Y < min.Y) min.Y = corners[i].Y; if (corners[i].X > max.X) max.X = corners[i].X; if (corners[i].Y > max.Y) max.Y = corners[i].Y; } // adjust the display area and selection area mDisplayArea.Width = Math.Abs(max.X - min.X); mDisplayArea.Height = Math.Abs(max.Y - min.Y); // adjust the selection area (after adjusting the display area such as the center properties is correct) Matrix translation = new Matrix(); translation.Translate(Center.X, Center.Y); translation.TransformPoints(corners); // then create the new selection area mSelectionArea = new Tools.Polygon(corners); if (redrawImage) { // now create a scaled font from the current one, to avoid aliasing Font scaledTextFont = new Font(mTextFont.FontFamily, mTextFont.Size * ANTI_ALIASING_FONT_SCALE, mTextFont.Style); mImage = new Bitmap(mImage, new Size((int)(textFontSize.Width * ANTI_ALIASING_FONT_SCALE), (int)(textFontSize.Height * ANTI_ALIASING_FONT_SCALE))); // compute the position where to draw according to the alignment (if centered == 0) float posx = 0; if (this.TextAlignment == StringAlignment.Far) posx = halfWidth; else if (this.TextAlignment == StringAlignment.Near) posx = -halfWidth; graphics = Graphics.FromImage(mImage); rotation = new Matrix(); rotation.Translate(mImage.Width / 2, mImage.Height / 2, MatrixOrder.Append); graphics.Transform = rotation; graphics.Clear(Color.Transparent); graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.DrawString(mText, scaledTextFont, mTextBrush, posx * ANTI_ALIASING_FONT_SCALE, 0, mTextStringFormat); graphics.Flush(); } } }
/// <summary> /// update the image used to draw the mesurement of the ruler correctly oriented /// The image is drawn with the current selected unit. /// This method should be called when the mesurement unit change or when one of the /// points change /// </summary> protected void updateMesurementImage() { // get the mesured distance in the current unit string distanceAsString = mMeasuredDistance.ToString("N2", mDisplayUnit); // draw the size Graphics graphics = Graphics.FromImage(mMeasureImage); SizeF textFontSize = graphics.MeasureString(distanceAsString, mMeasureFont); int width = (int)textFontSize.Width; int height = (int)textFontSize.Height; // after setting the text size, call the function to compute the scale mMeasureTextSizeInPixel.X = width; mMeasureTextSizeInPixel.Y = height; updateMesurementImageScale(MainForm.Instance.MapViewScale); // create an array with the 4 corner of the text (actually 3 if you exclude the origin) // and rotate them according to the orientation Matrix rotation = new Matrix(); rotation.Rotate(mOrientation); Point[] points = { new Point(width, 0), new Point(0, height), new Point(width, height) }; rotation.TransformVectors(points); // search the min and max of all the rotated corner to compute the necessary size of the bitmap Point min = new Point(0, 0); Point max = new Point(0, 0); for (int i = 0; i < 3; ++i) { if (points[i].X < min.X) min.X = points[i].X; if (points[i].Y < min.Y) min.Y = points[i].Y; if (points[i].X > max.X) max.X = points[i].X; if (points[i].Y > max.Y) max.Y = points[i].Y; } // create the bitmap and draw the text inside mMeasureImage = new Bitmap(mMeasureImage, new Size(Math.Abs(max.X - min.X), Math.Abs(max.Y - min.Y))); graphics = Graphics.FromImage(mMeasureImage); rotation.Translate(mMeasureImage.Width / 2, mMeasureImage.Height / 2, MatrixOrder.Append); graphics.Transform = rotation; graphics.Clear(Color.Transparent); graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.DrawString(distanceAsString, mMeasureFont, mMeasureBrush, 0, 0, mMeasureStringFormat); graphics.Flush(); }
private void updateConnectionPosition() { if (mConnectionPoints != null) { List<PointF> pointList = BrickLibrary.Instance.getConnectionPositionList(mPartNumber); if (pointList != null) { Matrix rotation = new Matrix(); rotation.Rotate(mOrientation); PointF[] pointArray = pointList.ToArray(); rotation.TransformVectors(pointArray); PointF center = this.Center; center.X += mOffsetFromOriginalImage.X; center.Y += mOffsetFromOriginalImage.Y; // in this function we assume the two arrays have the same size, // i.e. mConnectionPoints.Count == pointArray.Length // during the loading code we have created the mConnectionPoints with the // same size as the part library. for (int i = 0; i < pointList.Count; ++i) { mConnectionPoints[i].setPositionReservedForBrick(center.X + pointArray[i].X, center.Y + pointArray[i].Y); } } } }
internal static float ConvertStartAngle(float angle, SpatialTransform transform, CoordinateSystem targetSystem) { PointF xVector = new PointF(100, 0); Matrix rotation = new Matrix(); PointF[] angleVector = new PointF[] { xVector }; rotation.Rotate(angle); rotation.TransformVectors(angleVector); rotation.Dispose(); SizeF xVectorTransformed, angleVectorTransformed; if (targetSystem == Graphics.CoordinateSystem.Destination) { xVectorTransformed = transform.ConvertToDestination(new SizeF(xVector)); angleVectorTransformed = transform.ConvertToDestination(new SizeF(angleVector[0])); } else { xVectorTransformed = transform.ConvertToSource(new SizeF(xVector)); angleVectorTransformed = transform.ConvertToSource(new SizeF(angleVector[0])); } float xRotationOffset = (int)Math.Round(Vector.SubtendedAngle(xVectorTransformed.ToPointF(), PointF.Empty, xVector)); float angleTransformed = (int)Math.Round(Vector.SubtendedAngle(angleVectorTransformed.ToPointF(), PointF.Empty, xVectorTransformed.ToPointF())); // have to figure out where x-axis moved to and then return the difference between the angle // and the x-axis, where both are in 'target' coordinates. float returnAngle = angleTransformed + xRotationOffset; if (returnAngle < 0) returnAngle += 360; return returnAngle; }
//take a relative vector and make it a world vector public Vector RelativeToWorld(Vector relative) { Matrix mat = new Matrix(); PointF[] vectors = new PointF[1]; vectors[0].X = relative.X; vectors[0].Y = relative.Y; mat.Rotate(m_angle / (float)Math.PI * 180.0f); mat.TransformVectors(vectors); return new Vector(vectors[0].X, vectors[0].Y); }
/// <summary> /// Create a flex chain from a set of brick given a starting brick. From the free connection point /// of this starting brick go through all the linked connection points while the brick only have /// two connections. If the brick has only one or more than 2 connection (like a joint or crossing) /// we stop the chain. /// </summary> /// <param name="trackList">A set of track hopefully connected together, and hopefully containing flex track</param> /// <param name="grabbedTrack">The part which will try to reach the target. It should be part of the list.</param> /// <param name="currentFirstConnection">The first connection from which to start, which can be null and belongs to the grabbed track</param> public void ceateFlexChain(List<Layer.LayerItem> trackList, LayerBrick.Brick grabbedTrack, LayerBrick.Brick.ConnectionPoint currentFirstConnection) { // init all the arrays int boneCount = trackList.Count; mBoneList = new List<IKSolver.Bone_2D_CCD>(boneCount); mFlexChainList = new List<ChainLink>(boneCount * 2); mBricksInTheFlexChain = new List<Layer.LayerItem>(boneCount); // start to iterate from the grabbed track LayerBrick.Brick currentBrick = grabbedTrack; ChainLink hingedLink = null; addNewBone(currentFirstConnection, grabbedTrack, 0.0); // continue to iterate until we reach the end (no current brick) or the end of the selection // and continue only if the chain is made with track that exclusively has 2 connections to make a chain // (or one for the first iteration). while ((currentBrick != null) && trackList.Contains(currentBrick) && ((currentFirstConnection == null) || (currentBrick.ConnectionPoints.Count == 2))) { // get the other connection on the current brick int secondIndex = (currentBrick.ConnectionPoints[0] == currentFirstConnection) ? 1 : 0; LayerBrick.Brick.ConnectionPoint currentSecondConnection = currentBrick.ConnectionPoints[secondIndex]; LayerBrick.Brick nextBrick = currentSecondConnection.ConnectedBrick; LayerBrick.Brick.ConnectionPoint nextFirstConnection = currentSecondConnection.ConnectionLink; // add the two connections of the brick ChainLink link = new ChainLink(nextFirstConnection, currentSecondConnection); mFlexChainList.Insert(0, link); // check if the connection can rotate (if it is an hinge) float hingeAngle = BrickLibrary.Instance.getConnexionHingeAngle(currentSecondConnection.Type); if (hingeAngle != 0.0f) { // advance the hinge conncetion hingedLink = link; // add the link in the list addNewBone(currentSecondConnection, currentBrick, hingeAngle); // compute the current angle between the hinge connection and set it to the current bone // to do that we use the current angle between the connected brick and remove the static angle between them // to only get the flexible angle. float angleInDegree = 0.0f; if (nextBrick != null) angleInDegree = simplifyAngle(nextBrick.Orientation - currentBrick.Orientation - link.mAngleBetween); mBoneList[0].localAngleInRad = angleInDegree * (Math.PI / 180); // save the initial static cumulative orientation: start with the orientation of the root brick // if the hinge is connected to a brick, otherwise, if the hinge is free (connected to the world) // use the orientation of the hinge brick. // we set the value several time in the loop, in order to set it with the brick directly // connected to the last hinge in the chain if (nextBrick != null) mInitialStaticCumulativeOrientation = -nextBrick.Orientation; else mInitialStaticCumulativeOrientation = -currentBrick.Orientation; } // advance to the next link currentBrick = nextBrick; currentFirstConnection = nextFirstConnection; // check if the track is not a loop, otherwise we will have an infinite loop. // but don't add the test in the while because the first iteration must pass if (currentBrick == grabbedTrack) break; } // store the root hinge connection index (the last hinge found is the flexible root) if (hingedLink != null) mRootHingedLinkIndex = mFlexChainList.IndexOf(hingedLink); // compute the last bone vector if there is enough bones if (mBoneList.Count > 2) { int lastIndex = mBoneList.Count - 1; int beforeLastIndex = lastIndex - 1; mLastBoneVector = new PointF((float)(mBoneList[beforeLastIndex].worldX - mBoneList[lastIndex].worldX), (float)(mBoneList[beforeLastIndex].worldY - mBoneList[lastIndex].worldY)); // rotate the vector such as to compute it for a null angle // but if the last bone doesn't have connection, it can never snap, so the last bone will never be used if (mBoneList[lastIndex].connectionPoint != null) { PointF[] translation = { mLastBoneVector }; Matrix rotation = new Matrix(); rotation.Rotate(mBoneList[lastIndex].connectionPoint.mMyBrick.Orientation); rotation.TransformVectors(translation); mLastBoneVector = translation[0]; } } // add the current brick in the list of bricks, by not going further than the root brick LayerBrick.Brick.ConnectionPoint rootConnection = mFlexChainList[mRootHingedLinkIndex].mFirstConnection; if (rootConnection.mMyBrick != null) mBricksInTheFlexChain.Add(rootConnection.mMyBrick); for (int i = mRootHingedLinkIndex; i < mFlexChainList.Count; ++i) mBricksInTheFlexChain.Add(mFlexChainList[i].mSecondConnection.mMyBrick); }
public static PointF sComputeLocalOffsetFromLayerItem(LayerBrick.Brick brick, PointF worldPositionInStud) { // get the brick pivot in world coordinate PointF brickPivot = brick.Pivot; // compute the offset from the brick center in world coordinate PointF offset = new PointF(worldPositionInStud.X - brickPivot.X, worldPositionInStud.Y - brickPivot.Y); // compute the rotation matrix of the brick in order to find the local offset Matrix matrix = new Matrix(); matrix.Rotate(-brick.Orientation); PointF[] vector = { offset }; matrix.TransformVectors(vector); // return the local offset return vector[0]; }
/// <summary> /// Try to reach the specify target position with this chain /// </summary> /// <param name="targetInWorldStudCoord">the target position in world stud coord</param> /// <param name="targetConnection">If the end of the flex chain need to be connected to a connection, this parameter indicates which one, otherwise it is null</param> public void reachTarget(PointF targetInWorldStudCoord, LayerBrick.Brick.ConnectionPoint targetConnection) { // save the primary target mPrimaryTarget = targetInWorldStudCoord; // check if we need to compute a second target position mUseTwoTargets = (targetConnection != null); if (mUseTwoTargets) { // rotate the second target vector according to the orientation of the snapped connection PointF[] translation = { mLastBoneVector }; Matrix rotation = new Matrix(); rotation.Rotate(targetConnection.mMyBrick.Orientation + targetConnection.Angle + 180); rotation.TransformVectors(translation); // translate the target with the rotated vector for the second target mSecondaryTarget.X = targetInWorldStudCoord.X + translation[0].X; mSecondaryTarget.Y = targetInWorldStudCoord.Y + translation[0].Y; } // reset the status flag mCurrentUpdateStatus = IKSolver.CCDResult.Processing; // and call the update method update(); }
private void createGroupImageRecursive(Brick group, List<Brick> parentGroups) { if (group.mGroupInfo.mGroupSubPartList.Count == 0) throw new Exception("The group is empty. The tag <SubPartList> cannot be empty."); // check if we have a cyclic group if (parentGroups.Contains(group)) throw new Exception("Cyclic group detected. The group contains itself in its list or contains another group that contains it."); // use a temp list of transform to compute and store the transform of each sub part only for drawing purpose List<Matrix> subPartDrawingTransforms = new List<Matrix>(group.mGroupInfo.mGroupSubPartList.Count); // declare 4 variable to get the bounding box of the group part (in stud) float minX = float.MaxValue; float minY = float.MaxValue; float maxX = float.MinValue; float maxY = float.MinValue; // iterate on the bricks to compute the size of the image foreach (Brick.SubPart subPart in group.mGroupInfo.mGroupSubPartList) { // try to get the part from the library, otherwise add an unknown image if (!mBrickDictionary.TryGetValue(subPart.mSubPartNumber, out subPart.mSubPartBrick)) subPart.mSubPartBrick = AddUnknownBrick(subPart.mSubPartNumber, 32, 32); // check if the current sub part is a group not yet created in that case call the function recursively if ((subPart.mSubPartBrick.IsAGroup) && (subPart.mSubPartBrick.Image == null)) { List<Brick> newParentGroups = new List<Brick>(parentGroups); newParentGroups.Add(group); createGroupImageRecursive(subPart.mSubPartBrick, newParentGroups); } // create the drawing transform and add it to the list Matrix drawingTransform = new Matrix(); subPartDrawingTransforms.Add(drawingTransform); // add the rotation to the transform drawingTransform.Rotate(subPart.mOrientation); // transform the bounding box (which is in pixel) after that the image is created otherwise the hull may be null PointF[] hullPoints = subPart.mSubPartBrick.mHull.ToArray(); drawingTransform.TransformVectors(hullPoints); // get the local min and max for this sub part PointF hullMin = new PointF(); PointF hullMax = new PointF(); PointF hullHalfSize = LayerBrick.Brick.sGetMinMaxAndSize(hullPoints, ref hullMin, ref hullMax); hullHalfSize.X *= 0.5f; hullHalfSize.Y *= 0.5f; // compute the part translation in pixel float translateX = (subPart.mLocalTransformInStud.OffsetX * Layer.NUM_PIXEL_PER_STUD_FOR_BRICKS); float translateY = (subPart.mLocalTransformInStud.OffsetY * Layer.NUM_PIXEL_PER_STUD_FOR_BRICKS); // compute the hull min and max inside the whole group (so with the translation of the part) PointF hullMinInsideGroup = new PointF(translateX - hullHalfSize.X, translateY - hullHalfSize.Y); PointF hullMaxInsideGroup = new PointF(translateX + hullHalfSize.X, translateY + hullHalfSize.Y); // add the part transaltion to the transform for drawing. // The drawing transform want the top left corner of the image as a reference. So to compute it // we use the center position of the part inside the group (translateX) from which we remove the half size // of the part (hullHallSize.X) i.e. it is hullMinInsideGroup.X. But we also need to remove hullMin of the sub part // to but I don't know why. So now the translation of the sub part is relative to the XML origin of the group // but we will need to recentrate this, after finishing computing the whole size of the group, we can know where // is the origin of the group compare to the center of the group, but this will be added after this loop drawingTransform.Translate(hullMinInsideGroup.X - hullMin.X, hullMinInsideGroup.Y - hullMin.Y, MatrixOrder.Append); // check the local min and max with the global ones if (hullMinInsideGroup.X < minX) minX = hullMinInsideGroup.X; if (hullMaxInsideGroup.X > maxX) maxX = hullMaxInsideGroup.X; if (hullMinInsideGroup.Y < minY) minY = hullMinInsideGroup.Y; if (hullMaxInsideGroup.Y > maxY) maxY = hullMaxInsideGroup.Y; } // compute the position of the center of the group inside the coordinate system defined in the XML file float centerPositionRelativeToGroupOriginX = (maxX + minX) * 0.5f; float centerPositionRelativeToGroupOriginY = (maxY + minY) * 0.5f; // adjust the translate of each sub part to make the center of the group, the origin of the translate // This is easier when later we create an instance of this group from drag'n'droping from the library foreach (Brick.SubPart subPart in group.mGroupInfo.mGroupSubPartList) subPart.mLocalTransformInStud.Translate(-centerPositionRelativeToGroupOriginX / Layer.NUM_PIXEL_PER_STUD_FOR_BRICKS, -centerPositionRelativeToGroupOriginY / Layer.NUM_PIXEL_PER_STUD_FOR_BRICKS, MatrixOrder.Append); // also compute the vector between the center expressed in the drawing coord system (whose origin is in the // top left corner of the image) and the center expressed in the XML coord system float centerXMLToCenterImageX = ((maxX - minX) * 0.5f) - centerPositionRelativeToGroupOriginX; float centerXMLToCenterImageY = ((maxY - minY) * 0.5f) - centerPositionRelativeToGroupOriginY; // create a new image with the correct size int width = (int)(maxX - minX); int height = (int)(maxY - minY); if ((width > 0) && (height > 0)) { group.Image = new Bitmap(width, height); // get the graphic context and draw the referenc image in it with the correct transform and scale Graphics graphics = Graphics.FromImage(group.Image); graphics.Clear(Color.Transparent); graphics.CompositingMode = CompositingMode.SourceOver; graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.CompositingQuality = CompositingQuality.HighSpeed; graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; // we need it for the high scale down version for (int i = 0; i < group.mGroupInfo.mGroupSubPartList.Count; ++i) { // get the sub part and its transform Brick.SubPart subPart = group.mGroupInfo.mGroupSubPartList[i]; Matrix drawingTransform = subPartDrawingTransforms[i]; // add a final translate to the drawing transform drawingTransform.Translate(centerXMLToCenterImageX, centerXMLToCenterImageY, MatrixOrder.Append); // set the transform to the graphics context and draw graphics.Transform = drawingTransform; graphics.DrawImage(subPart.mSubPartBrick.Image, 0, 0, subPart.mSubPartBrick.Image.Width, subPart.mSubPartBrick.Image.Height); } graphics.Flush(); } else { throw new Exception("The group is too small to be visible."); } }