/// <summary> /// Use method to invert an input transform matrix about a given origin. /// Could help when combining transformation for stages with inverted axis. /// </summary> /// <param name="taskHandler">An implementation of the process configuration tasks</param> /// <param name="parentTransform">The parent transform in the chain of transformations</param> /// <param name="transform">The input transformation</param> /// <param name="ctIn">A cancellation token</param> /// <returns>The input matrix flip about it's parent's transform</returns> public async Task <Matrix4x4> InvertTransform(IProcessConfigurationTasksHandler taskHandler, Matrix4x4 parentTransform, Matrix4x4 transform, CancellationToken ctIn) { var origin = new MarkGeometryPoint(); origin.Transform(parentTransform); var inverts = await taskHandler.GetStageInverts(ctIn); return(GeometricArithmeticModule.CombineTransformations( // flip about the base transform's origin GeometricArithmeticModule.GetTranslationTransformationMatrix( -origin.X, -origin.Y, -origin.Z ), // apply the next transform transform, // flip the next transform on the requested x-axis GeometricArithmeticModule.GetScalingTransformationMatrix( inverts.InvertX ? -1 : 1, inverts.InvertY ? -1 : 1 ), // translate back to the base transform's origin GeometricArithmeticModule.GetTranslationTransformationMatrix( origin.X, origin.Y, origin.Z ) )); }
public static Matrix4x4 GetRelativeTransform(MRecipe recipe, MRecipeDevice device) { if (device.Parent == null) { recipe.UpdateParents(); } return(GeometricArithmeticModule.CombineTransformations( // add device's transform device.TransformInfo.ToMatrix4x4(), // add plate's transform (device.Parent as MRecipeBaseNode).TransformInfo.ToMatrix4x4() )); }
public static Matrix4x4 GetRelativeTransformFromParent(MRecipeBaseNode parentNode, MRecipeBaseNode recipeNode) { var transformChain = new List <Matrix4x4>(); var nodeParent = recipeNode; while ( nodeParent != null && nodeParent != parentNode ) { transformChain.Add(nodeParent.TransformInfo.ToMatrix4x4()); nodeParent = nodeParent.Parent as MRecipeBaseNode; } transformChain.Add(parentNode.TransformInfo.ToMatrix4x4()); return(GeometricArithmeticModule.CombineTransformations(transformChain.ToArray())); }
public virtual void AddRecipeNode(MRecipe recipe, MRecipeBaseNode recipeNode) { if (recipe == null || recipeNode == null) { return; } var extents = GeometryExtents <double> .CreateDefaultDouble(); MRecipe.BeginGetAllLayers_Parallel(recipeNode, (layer) => { extents = GeometryExtents <double> .Combine( extents, AddLayer(recipe, layer) ); }); // calculate size of fiducial relative to the node var fiducialSize = 0.025 * extents.Hypotenuse; // generate fiducial pattern GenerateFiducialPattern(fiducialSize); // get node's transform var parentsTransform = (recipeNode.Parent as MRecipeBaseNode)?.TransformInfo.ToMatrix4x4() ?? GeometricArithmeticModule.GetDefaultTransformationMatrix(); // render fiducials in parent's reference frame foreach (var fiducial in recipeNode.Fiducials) { var transform = GeometricArithmeticModule.CombineTransformations( GeometricArithmeticModule.GetTranslationTransformationMatrix( fiducial.X, fiducial.Y, fiducial.Z ), parentsTransform ); foreach (var geometry in _fiducialPattern) { var clone = (IMarkGeometry)geometry.Clone(); clone.Transform(transform); AddDefault(clone, FiducialColor); } } }
public Matrix4x4 ToMatrix4x4() { return(GeometricArithmeticModule.CombineTransformations( // apply scale GeometricArithmeticModule.GetScalingTransformationMatrix( ScaleX, ScaleY, ScaleZ ), // apply rotation - don't forget to convert degrees to radians GeometricArithmeticModule.GetRotationTransformationMatrix( GeometricArithmeticModule.ToRadians(RotationDegX), GeometricArithmeticModule.ToRadians(RotationDegY), GeometricArithmeticModule.ToRadians(RotationDegZ) ), // apply offset GeometricArithmeticModule.GetTranslationTransformationMatrix( OffsetX, OffsetY, OffsetZ ) )); }
// TODO: Implement mask angle align func // 1. The mask angle is measured directly by camera. // 2. The mask is rotated to compensate for the angle. // 3. The procedure continues until the angle is zero. public MVertex ConvertCADToMachineCoordinate(MVertex cadOrigin, MVertex pointOnCAD) { var transform = GeometricArithmeticModule.CombineTransformations( GeometricArithmeticModule.GetTranslationTransformationMatrix( -cadOrigin.X, -cadOrigin.Y ), GeometricArithmeticModule.GetRotationTransformationMatrix( 0, 0, Angle ), GeometricArithmeticModule.GetTranslationTransformationMatrix( OriginRelativeBEAM.X, OriginRelativeBEAM.Y ) ); var result = Vector3.Transform( new Vector3((float)pointOnCAD.X, (float)pointOnCAD.Y, 0), transform ); return(new MVertex(Inverts.X * result.X, Inverts.Y * result.Y)); }
public bool SaveImage(string filePath) { var bitmap = new Bitmap( 480, 480, PixelFormat.Format24bppRgb ); var shader = new GeometryShader2D(); shader.UpdateSettings(1, 1, Color.Red, Color.Green, Color.Transparent); shader.Reset(bitmap, Color.Black); var xScale = bitmap.Width / Boundary.Extents.Width; var yScale = bitmap.Height / Boundary.Extents.Height; // calculate transform var transformationMatrix = GeometricArithmeticModule.CombineTransformations( // centre geometries at origin before scaling GeometricArithmeticModule.GetTranslationTransformationMatrix( -Boundary.Extents.Centre.X, -Boundary.Extents.Centre.Y ), // scale geometries to fit the target bitmap GeometricArithmeticModule.GetScalingTransformationMatrix( xScale, -yScale ), // centre geometries in target bitmap GeometricArithmeticModule.GetTranslationTransformationMatrix( 0.5 * bitmap.Width, 0.5 * bitmap.Height ) ); Draw(bitmap, shader, transformationMatrix); return(shader.WriteToFile(bitmap, filePath, PixelFormat.Format24bppRgb, 120, 120, GeometryShader2D.OptimisationSetting.Default)); }
public async Task <Matrix4x4> GetAbsoluteTransformFromStageOrigin( IProcessConfigurationTasksHandler taskHandler, ICamToBeam cam2Beam, MRecipe recipe, MRecipeBaseNode recipeNode, CancellationToken ctIn ) { // firstly, find closest parent with fiducials var parent = recipeNode; while ( parent != null && parent.Fiducials.Count <= 0 ) { parent = parent.Parent as MRecipeBaseNode; } if (parent != null) { // recursion - get it's parent's transform var baseTransform = await GetAbsoluteTransformFromStageOrigin( taskHandler, cam2Beam, recipe, parent.Parent as MRecipeBaseNode, ctIn ); var fiducialPoints = new List <MarkGeometryPoint>(); var measuredPoints = new List <MarkGeometryPoint>(); for (int i = 0; i < parent.Fiducials.Count; i++) { var fiducial = parent.Fiducials[i]; if (!_cachedFiducials.ContainsKey(fiducial)) { // estimate stage position var stageLocation = new MarkGeometryPoint( fiducial.X, fiducial.Y, fiducial.Z ); stageLocation.Transform(baseTransform); // goto estimated position if (!await taskHandler.GotoXY( stageLocation.X, stageLocation.Y, ctIn )) { throw new Exception($"Failed to goto the estimated position; origin: {await taskHandler.GetStageOrigin(ctIn)}, fiducial: {(fiducial.X, fiducial.Y)} est: {stageLocation}"); } // find and centre { if (!await cam2Beam.MoveBeamToCamOffset(ctIn)) { throw new Exception($"Failed to move beam to cam offset"); } if (!await taskHandler.MovetoCameraFocus(ctIn)) { throw new Exception($"Failed to move camera to focus"); } await taskHandler.SwitchCameraLedOn(ctIn); // attempt to centre on fiducial if (!await taskHandler.CentreOnVisibleObject(ctIn)) { _logger.Info($"Failed to centre on fiducial"); // ask user to locate fiducials Application.Current.Dispatcher.Invoke( () => { var fidVm = taskHandler.GetNewFidFindViewModel(); var inspectionDialog = new InspectionWindow(fidVm, Application.Current.MainWindow); inspectionDialog.Owner = Application.Current.MainWindow; if (inspectionDialog.ShowDialog() != true) { throw new Exception("Failed to find fiducial"); } // TODO : think about updating the machine's origin } ); } if (!await cam2Beam.MoveCamToBeamOffset(ctIn)) { throw new Exception($"Failed to move camera to beam offset"); } } // read fiducial position var(stgX, stgY, success) = await taskHandler.GetStageXY(ctIn); if (!success) { throw new Exception("Failed to read the current stage position"); } // update cache _cachedFiducials[fiducial] = new MarkGeometryPoint(stgX, stgY); } // update measured points fiducialPoints.Add(new MarkGeometryPoint(fiducial.X, fiducial.Y)); measuredPoints.Add(_cachedFiducials[fiducial]); } // calculate transform from measured fiducials var parentTransform = GeometricArithmeticModule.EstimateTransformationMatrixFromPoints( fiducialPoints, measuredPoints ); return(GeometricArithmeticModule.CombineTransformations( parentTransform, await InvertTransform( taskHandler, parentTransform, MRecipe.GetRelativeTransformFromParent( parent, recipeNode ), ctIn ) )); } var origin = await taskHandler.GetStageOrigin(ctIn); var stageOrigin = GeometricArithmeticModule.GetTranslationTransformationMatrix( origin.X, origin.Y ); return(GeometricArithmeticModule.CombineTransformations( stageOrigin, await InvertTransform( taskHandler, stageOrigin, MRecipe.GetRelativeTransform( recipe, recipeNode ), ctIn ) )); }
public void Render() { _terminableTaskExecutor.CancelCurrentAndRun( (ctIn) => { // reset shader MShader.Reset(); if (_geometriesBuffer != null) { int count = 0; var extents = GeometryExtents <double> .CreateDefaultDouble(); foreach (var layerName in _geometriesBuffer.Keys) { MShader.AddDefault( _geometriesBuffer[layerName], _colours[(count++) % _colours.Length] ); extents = GeometryExtents <double> .Combine( extents, GeometricArithmeticModule.CalculateExtents(_geometriesBuffer[layerName]) ); } // overlay tiles foreach (var tile in MTileSettings.ToTiles(TileSettings, extents)) { MShader.AddDefault((MarkGeometryRectangle)tile, _tileColor); } // calculate size of fiducial relative to the node var fiducialSize = 0.05 * extents.Hypotenuse; // generate fiducial pattern GenerateFiducialPattern(fiducialSize); // get base transform var baseTransform = GeometricArithmeticModule.GetTranslationTransformationMatrix( extents.Centre.X, extents.Centre.Y ); // render fiducials in parent's reference frame foreach (var fiducial in Fiducials) { var transform = GeometricArithmeticModule.CombineTransformations( baseTransform, GeometricArithmeticModule.GetTranslationTransformationMatrix( fiducial.X, fiducial.Y, fiducial.Z ) ); foreach (var geometry in _fiducialPattern) { var clone = (IMarkGeometry)geometry.Clone(); clone.Transform(transform); MShader.AddDefault(clone, _fiducialColor); } } } // stop if new render is requested ctIn.ThrowIfCancellationRequested(); MShader.Render(); DrawingExtentsX = MShader.Width; DrawingExtentsY = MShader.Height; DrawingCount = MShader.Count; } ); }
public async Task <Matrix4x4> GetAbsoluteTransform(MRecipeBaseNode node, Matrix4x4 parentTransform, CancellationToken ctIn) { if (parentTransform == null) { parentTransform = GeometricArithmeticModule.GetDefaultTransformationMatrix(); } // node has no fiducials, hences it's position // is relative to its parent if (node.Fiducials.Count <= 0) { return(GeometricArithmeticModule.CombineTransformations( parentTransform, node.TransformInfo.ToMatrix4x4() )); } // attempt to retrieve tasksHandler; handles process specific stage motion // camera, find n centre, etc. IProcessConfigurationTasksHandler tasksHandler = GetClosestConfigurationTasksHandler(node); if (tasksHandler == null) { throw new Exception("Failed to retrieve a Task Handler for the closest process mode"); } // buffer to store estimated and measured fiducials var estimatedPoints = new List <MarkGeometryPoint>(); var measuredPoints = new List <MarkGeometryPoint>(); for (int i = 0; i < node.Fiducials.Count; i++) { var estimatedPosition = new MarkGeometryPoint( node.Fiducials[i].X, node.Fiducials[i].Y ); // transform to parents space estimatedPosition.Transform(parentTransform); estimatedPoints.Add(estimatedPosition); // move camera to position await tasksHandler.FocusCameraAtXY( estimatedPosition.X, estimatedPosition.Y, ctIn ); // get measured position var results = await tasksHandler.TakeMeasurement(); // handle results not found if (!results.Found) { throw new Exception("Failed to find fiducial"); } // store measured point measuredPoints.Add( new MarkGeometryPoint( results.Position.X, results.Position.Y ) ); } return(GeometricArithmeticModule.CombineTransformations( parentTransform, MAlignmentCalculator.GetAlignmentTransform( node.AlignmentType, estimatedPoints, measuredPoints ) )); }
public void Render() { _terminableTaskExecutor.CancelCurrentAndRun( (ctIn) => { // reset shader MShader.Reset(); // stop if new render is requested ctIn.ThrowIfCancellationRequested(); // as we only render one layer at a time // choose the closest layer in the recipe tree var layer = GetClosestLayer(); if (layer != null) { // extract device's absolute transform var baseTransform = MRecipe.GetRelativeTransform(RecipeVm, layer); // calculate layer's transform var transform = GeometricArithmeticModule.CombineTransformations( baseTransform, layer.TransformInfo.ToMatrix4x4() ); // fetch pattern var pattern = FetchDXF(layer); for (int j = 0; j < pattern?.Count; j++) { // get copy of geometry var geometry = (IMarkGeometry)pattern[j].Clone(); // apply transform to geometry geometry.Transform(transform); // add geometry MShader.AddDefault(geometry); } // overlay tiles foreach (var tile in layer.TileDescriptions) { var rect = (MarkGeometryRectangle)tile; // apply transform to rect rect.Transform(transform); // add geometry MShader.AddDefault(rect, MGLShader.Violet); } } // stop if new render is requested ctIn.ThrowIfCancellationRequested(); MShader.Render(); UpdateStats(); } ); }
public void Render() { _terminableTaskExecutor.CancelCurrentAndRun( (ctIn) => { // reset shader MShader.Reset(); if (_stlSlices != null) { // add geometries if (CurrentSlice >= 0 && CurrentSlice < _stlSlices.Count && _stlSlices[CurrentSlice] != null) { var stlSlice = _stlSlices[CurrentSlice]; // tiles for (int i = 0; i < stlSlice.Tiles?.Count; i++) { MShader.AddDefault(stlSlice.Tiles[i], _tileColor); } // contours for (int i = 0; i < stlSlice.ContourLines?.Count; i++) { MShader.AddDefault(stlSlice.ContourLines[i], MGLShader.Green); } // hatches for (int i = 0; i < stlSlice.HatchLines?.Count; i++) { MShader.AddDefault(stlSlice.HatchLines[i], MGLShader.White); } } // stop if new render is requested ctIn.ThrowIfCancellationRequested(); // get base transform var baseTransform = GeometricArithmeticModule.GetTranslationTransformationMatrix( StlModelReferencePoint.X, StlModelReferencePoint.Y ); // render fiducials in parent's reference frame foreach (var fiducial in Fiducials) { var transform = GeometricArithmeticModule.CombineTransformations( baseTransform, GeometricArithmeticModule.GetTranslationTransformationMatrix( fiducial.X, fiducial.Y, fiducial.Z ) ); foreach (var geometry in _fiducialPattern) { var clone = (IMarkGeometry)geometry.Clone(); clone.Transform(transform); MShader.AddDefault(clone, _fiducialColor); } } } // stop if new render is requested ctIn.ThrowIfCancellationRequested(); MShader.Render(); } ); }