package org.goko.tools.autoleveler.modifier; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import org.goko.core.common.exception.GkException; import org.goko.core.common.measure.Units; import org.goko.core.common.measure.quantity.Length; import org.goko.core.gcode.element.GCodeLine; import org.goko.core.gcode.element.IGCodeProvider; import org.goko.core.gcode.element.IInstructionSetIterator; import org.goko.core.gcode.rs274ngcv3.context.GCodeContext; import org.goko.core.gcode.rs274ngcv3.element.GCodeProvider; import org.goko.core.gcode.rs274ngcv3.element.IModifier; import org.goko.core.gcode.rs274ngcv3.element.InstructionProvider; import org.goko.core.gcode.rs274ngcv3.element.InstructionSet; import org.goko.core.gcode.rs274ngcv3.element.InstructionType; import org.goko.core.gcode.rs274ngcv3.instruction.AbstractInstruction; import org.goko.core.gcode.rs274ngcv3.instruction.AbstractStraightInstruction; import org.goko.core.gcode.rs274ngcv3.instruction.StraightFeedInstruction; import org.goko.core.gcode.rs274ngcv3.instruction.StraightTraverseInstruction; import org.goko.core.gcode.rs274ngcv3.modifier.AbstractModifier; import org.goko.core.math.Tuple6b; import org.goko.tools.autoleveler.bean.IHeightMap; public abstract class AutoLevelerModifier<T extends IHeightMap> extends AbstractModifier<GCodeProvider> implements IModifier<GCodeProvider>{ /** The offset map builder */ private T heightMap; /** The theoric height to compare with the probed height */ private Length theoricHeight; public AutoLevelerModifier() { super("Auto leveler"); this.theoricHeight = Length.ZERO; } /** (inheritDoc) * @see org.goko.core.gcode.rs274ngcv3.element.IModifier#isConfigured() */ @Override public boolean isConfigured() { return heightMap != null && heightMap.isProbed(); } /** (inheritDoc) * @see org.goko.core.gcode.rs274ngcv3.modifier.AbstractModifier#applyModifier(org.goko.core.gcode.element.IGCodeProvider, org.goko.core.gcode.rs274ngcv3.element.GCodeProvider) */ @Override protected void applyModifier(IGCodeProvider source, GCodeProvider target) throws GkException { if(heightMap == null){ return;//throw new GkTechnicalException("No valid height map"); } GCodeContext localContext = new GCodeContext(); InstructionProvider sourceInstructionSet = getRS274NGCService().getInstructions(localContext, source); InstructionProvider resultInstructionProvider = new InstructionProvider(); IInstructionSetIterator<GCodeContext, AbstractInstruction> iterator = getRS274NGCService().getIterator(sourceInstructionSet, localContext); while(iterator.hasNext()){ localContext = new GCodeContext(iterator.getContext()); // Get the context before applying the command AbstractInstruction instr = iterator.next(); if(instr.getType() == InstructionType.STRAIGHT_FEED || instr.getType() == InstructionType.STRAIGHT_TRAVERSE){ resultInstructionProvider.addInstructionSet(applyModifier(localContext, (AbstractStraightInstruction) instr)); // }else if(instr.getType() == InstructionType.ARC_FEED){ // throw new GkTechnicalException("ARC_FEED command are not supported in auto leveler modifier"); }else{ // Other non modified instruction InstructionSet resultInstructionSet = new InstructionSet(); resultInstructionSet.addInstruction(instr); resultInstructionProvider.addInstructionSet(resultInstructionSet); } } GCodeProvider result = getRS274NGCService().getGCodeProvider(new GCodeContext(), resultInstructionProvider); for (GCodeLine line : result.getLines()) { target.addLine(line); } } /** * Apply the modifier to a straight instruction * @param source the source instruction * @return an AbstractStraightInstruction * @throws GkException GkException */ private List<InstructionSet> applyModifier(GCodeContext localContext, AbstractStraightInstruction source) throws GkException{ Tuple6b start = new Tuple6b(Units.MILLIMETRE, localContext.getX(), localContext.getY(), localContext.getZ() ); //Tuple6b end = new Tuple6b(Units.MILLIMETRE, source.getX(), source.getY(), source.getZ() ); GCodeContext postContext = new GCodeContext(localContext); source.apply(postContext); Tuple6b end = postContext.getPosition(); List<InstructionSet> sets = new ArrayList<InstructionSet>(); List<Tuple6b> lstPoints = heightMap.splitSegment(start, end); int nbPoints = lstPoints.size(); for(int i = 0;i < nbPoints - 1; i++){ Tuple6b target = lstPoints.get(i+1); InstructionSet instructionSet = new InstructionSet(); Length probedHeight = heightMap.getHeight(target.getX(), target.getY()); if(probedHeight == null){ probedHeight = Length.ZERO; } // Let's compute the theorical height in the given segment BigDecimal factor = BigDecimal.ONE; if(!start.getX().equals(end.getX())){ factor = ( target.getX().subtract(start.getX()) ).divide(end.getX().subtract(start.getX())); }else if(!start.getY().equals(end.getY())){ factor = ( target.getY().subtract(start.getY()) ).divide(end.getY().subtract(start.getY())); } Length interpolatedHeight = start.getZ().add(end.getZ().subtract(start.getZ()).multiply( factor )); // Now we can apply the computed offset to the interpolated height Length fixedHeight = interpolatedHeight.subtract( theoricHeight.subtract(probedHeight)); if( source.getType() == InstructionType.STRAIGHT_TRAVERSE ){ instructionSet.addInstruction(new StraightTraverseInstruction(target.getX(), target.getY(), fixedHeight, source.getA(), source.getB(), source.getC())); }else if(source.getType() == InstructionType.STRAIGHT_FEED){ instructionSet.addInstruction(new StraightFeedInstruction(target.getX(), target.getY(), fixedHeight, source.getA(), source.getB(), source.getC())); } sets.add(instructionSet); } return sets; } /** * @return the theoricHeight */ public Length getTheoricHeight() { return theoricHeight; } /** * @param theoricHeight the theoricHeight to set */ public void setTheoricHeight(Length theoricHeight) { this.theoricHeight = theoricHeight; } /** * @return the heightMap */ public T getHeightMap() { return heightMap; } /** * @param heightMap the heightMap to set */ public void setHeightMap(T heightMap) { this.heightMap = heightMap; } }