private DiscreteInlineOperation handle(DiscreteInlineOperation operation, boolean addToRowWidth) { int advanceCount = operation.getAdvanceCount(); int increaseCount = operation.getIncreaseCount(); // can't handle colors at this point if (operation instanceof YarnReferenceHolder && ((YarnReferenceHolder) operation).getYarnIdRef() != null) { return null; } if (dynamicFirstRowCastOn) { // dynamically cast on the advance count (since all stitches are // unaccounted for) // then slip them in reverse so that we can work them "normally" renderingContext.getEngine().castOn(new InlineCastOn(advanceCount)); renderingContext.getEngine().reverseSlip(advanceCount); } StitchNature stitchNature = operation instanceof StitchNatureProducer ? ((StitchNatureProducer) operation).getStitchNatureProduced() : null; if (addToRowWidth) { this.currentRowInfo.addToRowWidth(advanceCount + increaseCount); } advanceAndIncrease(advanceCount, increaseCount, stitchNature); return operation; }
protected InlineOperation handle(WorkEven object) { // A WorkEven means to knit knits and purl purls int numberOfTimes = object.getNumberOfTimes() == null ? 1 : object.getNumberOfTimes(); List<DiscreteInlineOperation> newOperations = new ArrayList<DiscreteInlineOperation>(numberOfTimes); for (int i = 0; i < numberOfTimes; i++) { StitchNature currentStitchNature = renderingContext.getEngine().peekAtNextStitch().getCurrentNature(); // reverse the stitch nature if working from the wrong side if (renderingContext.getEngine().getDirection() == Direction.BACKWARDS) { currentStitchNature = StitchNature.reverse(currentStitchNature); } // knit the knits and purl the purls if (currentStitchNature == StitchNature.KNIT) { newOperations.add(new Knit(1, null, object.getYarnIdRef())); } else { // lastOperation == StitchOperation.PURL newOperations.add(new Purl(1, null, object.getYarnIdRef())); } this.currentRowInfo.addToRowWidth(1); advanceAndIncrease(1, 0, currentStitchNature); } return newOperations.size() == 1 ? newOperations.get(0) : new OperationGroup(newOperations); }
protected void advanceAndIncrease(int advanceCount, int increaseCount, StitchNature nature) { if (increaseCount < 0) { // decrease the number specified by increaseCount for (int i = 0; i > increaseCount; i--) { renderingContext.getEngine().knitTwoTogether(); renderingContext.getEngine().reverseSlip(); } } else if (increaseCount > 0) { // increase the number specified by increaseCount for (int i = 0; i < increaseCount; i++) { renderingContext.getEngine().increase(new Increase(1)); renderingContext.getEngine().reverseSlip(); } } // advance the engine the number that this operations says to advance // plus the increases if (nature == null) { renderingContext.getEngine().slip(advanceCount + increaseCount); } else if (nature == StitchNature.PURL) { renderingContext.getEngine().purl(advanceCount + increaseCount); } else { // nature == StitchNature.KNIT renderingContext.getEngine().knit(advanceCount + increaseCount); } }
public Analysis analyzeInstruction( Instruction originalInstruction, RepeatInstruction repeatInstruction, boolean fromInstructionDefinition) { this.maxWidth = 0; this.rowInfos.clear(); this.currentRowInfo = null; this.currentlyRepeating = false; this.shape = KnittingShape.FLAT; Object engineState = renderingContext.getEngine().save(); if (fromInstructionDefinition && originalInstruction.getStartingStitchCount() != null) { renderingContext.getEngine().castOn(originalInstruction.getStartingStitchCount(), false); } else { this.dynamicFirstRowCastOn = fromInstructionDefinition; } int startingStitches = renderingContext.getEngine().getTotalNumberOfStitchesInRow(); try { if (!originalInstruction.hasRows()) { // cannot handle for-each-row-in-instruction yet. Possibly this // would be expanded ahead of time. log.info("Cannot handle an Instruction whose operations are not made up entirely of Rows"); return new Analysis(); } List<Row> newRows = analyzeInstructionOnce(originalInstruction); if (newRows == null) { return new Analysis(); } // TODO RepeatInstruction analysis here.... // FIXME for now, simply refuse to chart if the chart is unbalanced // and there is a known repeat-instruction if (!fromInstructionDefinition && repeatInstruction != null) { int endingStitches = renderingContext.getEngine().getTotalNumberOfStitchesInRow(); if (startingStitches != endingStitches) { return new Analysis(); } } // if we do tack on rows as the result of a repeat-instruction, // make sure we give the new instruction an unclassified ID instead // of the original ID. We don't want someone else adding a // repeat-instruction to the original + repeat-instruction chart. // end RepeatInstruction analysis Instruction newInstruction = new Instruction(originalInstruction, newRows); Analysis result = new Analysis(); result.setInstructionToUse(newInstruction); result.setChartable(true); result.setMaxWidth(this.maxWidth); result.setRowInfos(this.rowInfos); result.setContainsNoStitchOperations(this.containsNoStitchOperations); result.setKnittingShape(this.shape); result.setStartingSide(this.startingSide); return result; } finally { renderingContext.getEngine().restore(engineState); } }
protected InlineOperation handle(PlaceMarker object) { renderingContext.getEngine().placeMarker(); return object; }
protected InlineOperation handle(Repeat oldRepeat) { // initialize variables for this method KnittingEngine engine = renderingContext.getEngine(); Until until = oldRepeat.getUntil(); Integer value = oldRepeat.getValue(); // ensure that we can continue to analyze this instruction for charting if (dynamicFirstRowCastOn && until != Until.TIMES) { log.info( "Cannot chart this instruction because it is both defined in the header (i.e. without a knitting context) " + "and the first row has a repeat section that requires a knitting context." + "This can be fixed either by declaring the 'until' attribute of the repeat in the first row to be of type 'times', " + "or by including the repeats in the directions " + "section of the pattern instead of in the header."); return null; } if (currentlyRepeating) { log.info("Cannot currently chart nested repeats. This feature may be expanded in the future"); return null; } // see if we need to repeat at all. If not, just return "0 repeats" if (!isRepeatNecessary(oldRepeat, engine)) { return new Repeat(Until.TIMES, 0); } // at this point, we have to do the repeat (1 or more times) currentlyRepeating = true; // start gathering up information for the new repeat. Our goal is to // convert all repeats // into an until value that represents TIMES. This is so we won't need // the engine to chart. List<DiscreteInlineOperation> newOperations = new ArrayList<DiscreteInlineOperation>(); // gather current (pre-repeat) state Object engineState = engine.save(); int originalChartWidth = this.currentRowInfo.getRowWidth(); // walk through all the instructions of this repeat ONCE to calculate // the advance and increase count. If we find a work-even, remove // the repeat all together and completely expand the row boolean contextualOperationFound = false; for (InlineOperation operation : oldRepeat.getOperations()) { if (!contextualOperationFound && operation instanceof WorkEven) { contextualOperationFound = true; // if we find out there are contextual operations, clear out // newOperations // and wait for the actual repeat. We'll gather that info then. newOperations.clear(); break; } InlineOperation newOperation = handle(operation); // if a non-discrete inline operation or null is returned, we cannot // chart, so return null up the chain if (!(newOperation instanceof DiscreteInlineOperation)) { return null; } newOperations.add((DiscreteInlineOperation) newOperation); } // reset the engine to the state before we repeated once engine.restore(engineState); this.currentRowInfo.setRowWidth(originalChartWidth); int counter = 0; switch (until) { case TIMES: for (int i = 0; i < value; i++) { // advanceAndIncrease(advanceCount, increaseCount); for (InlineOperation operation : oldRepeat.getOperations()) { DiscreteInlineOperation newOperation = (DiscreteInlineOperation) handle(operation); if (contextualOperationFound) { newOperations.add(newOperation); } } } counter = value; break; case BEFORE_END: while (engine.getStitchesRemainingInRow() > value) { // advanceAndIncrease(advanceCount, increaseCount); for (InlineOperation operation : oldRepeat.getOperations()) { DiscreteInlineOperation newOperation = (DiscreteInlineOperation) handle(operation); if (contextualOperationFound) { newOperations.add(newOperation); } } counter++; } break; case END: while (engine.getStitchesRemainingInRow() > 0) { // advanceAndIncrease(advanceCount, increaseCount); for (InlineOperation operation : oldRepeat.getOperations()) { DiscreteInlineOperation newOperation = (DiscreteInlineOperation) handle(operation); if (contextualOperationFound) { newOperations.add(newOperation); } } counter++; } break; case BEFORE_GAP: while (engine.getStitchesToGap() > value) { // advanceAndIncrease(advanceCount, increaseCount); for (InlineOperation operation : oldRepeat.getOperations()) { DiscreteInlineOperation newOperation = (DiscreteInlineOperation) handle(operation); if (contextualOperationFound) { newOperations.add(newOperation); } } counter++; } break; case BEFORE_MARKER: while (engine.getStitchesToNextMarker() > value) { // advanceAndIncrease(advanceCount, increaseCount); for (InlineOperation operation : oldRepeat.getOperations()) { DiscreteInlineOperation newOperation = (DiscreteInlineOperation) handle(operation); if (contextualOperationFound) { newOperations.add(newOperation); } } counter++; } break; case MARKER: while (engine.getStitchesToNextMarker() > 0) { // advanceAndIncrease(advanceCount, increaseCount); for (InlineOperation operation : oldRepeat.getOperations()) { DiscreteInlineOperation newOperation = (DiscreteInlineOperation) handle(operation); if (contextualOperationFound) { newOperations.add(newOperation); } } counter++; } break; default: throw new RuntimeException("The until value [" + until + "] has not been properly coded"); } currentlyRepeating = false; if (contextualOperationFound) { // make a stitch group out of all of the gathered operations if // we are dealing with work even operations return new OperationGroup(newOperations); } else { Repeat newRepeat = new Repeat(); newRepeat.setOperations(newOperations); newRepeat.setUntil(Until.TIMES); newRepeat.setValue(counter); return newRepeat; } }
protected Row analyzeRow(Row originalRow) { KnittingEngine engine = renderingContext.getEngine(); // can't handle short rows if (originalRow.isShortRow()) { return null; } // can't handle colors (right now) if (originalRow.getYarnIdRef() != null) { return null; } List<InlineOperation> newOperations = new ArrayList<InlineOperation>(); Row newRow = new Row(originalRow, newOperations); this.currentRowInfo = new RowInfo(); // Begin the row for the engine if (dynamicFirstRowCastOn) { // if this is an instruction definition, infer the intended shape // from what is specified by the first row's shape. // If nothing is specified, flat knitting is assumed. if (originalRow.getType() != null) { this.shape = originalRow.getType(); } // cast on one stitch so that we can start a new row below engine.castOn(1, false); } engine.startNewRow(); if (this.startingSide == null) { this.startingSide = Side.RIGHT; if (dynamicFirstRowCastOn && originalRow.getSide() == Side.WRONG) { this.startingSide = Side.WRONG; engine.knit(); engine.endRow(); engine.startNewRow(); } else if (engine.getDirection() == Direction.BACKWARDS) { this.startingSide = Side.WRONG; } } // Walk through all of the row's operations and see how it affects the // counts for (InlineOperation operation : originalRow.getOperations()) { InlineOperation newOperation = handle(operation); // if null is returned, we cannot chart, so return null up the chain if (newOperation == null) { return null; } newOperations.add(newOperation); } // End the row for the engine if (dynamicFirstRowCastOn) { // remove the "extra" stitch we created in order to start the row. engine.reverseSlip(1); // don't swallow the real stitch's nature StitchNature stitchNature = engine.peekAtNextStitch().getCurrentNature(); if (stitchNature == StitchNature.PURL) { engine.purlTwoTogether(); } else { engine.knitTwoTogether(); } // now pretend like all is normal dynamicFirstRowCastOn = false; } engine.endRow(); // if this row pushed the bounds of the previously recorded maximum // width, record the new max width if (currentRowInfo.getRowWidth() > maxWidth) { maxWidth = currentRowInfo.getRowWidth(); } // if (!repeatInCurrentRow) { // resetGlobalRepeatTracker(); // } this.rowInfos.add(currentRowInfo); return newRow; }
@Override public void end(Object element, Renderer renderer) { RenderingContext context = renderer.getRenderingContext(); // retrieve the "before" and "after" visiting states of the knitting // engine so that we have something to go back to Object beforeVisitingEngineState = context.getPatternState().retrieveSavedEngineState(); Object afterVisitingEngineState = context.getEngine().save(); // start with the state before the instruction was sent through the // engine renderer.getRenderingContext().getEngine().restore(beforeVisitingEngineState); // retrieve the instruction from the knitting context's repository // rather than the instruction from the document, as the rows // may have been expanded. PatternRepository repository = context.getPatternRepository(); Instruction instruction = (Instruction) element; String id = instruction.getId(); Instruction candidateInstruction = context.getKnittingContext().getPatternRepository().getBlockInstruction(id); // have the renderer evaluate the instruction and return a different // instruction if it wants to change anything. Pass it a sibling // repeat-instruction // if it can be found. Instruction instructionToUse = renderer.evaluateInstruction( candidateInstruction, findSiblingRepeatInstruction(instruction, renderer)); // if the renderer didn't alter anything, it returns null if (instructionToUse == null) { instructionToUse = candidateInstruction; } InstructionInfo instructionInfo; if (instructionToUse.hasLabelOrMessageKey()) { instructionInfo = deriveInstructionInfo(instructionToUse, deriveLabel(instruction, repository)); repository.addGlobalInstruction(instructionInfo); } else { instructionInfo = deriveInstructionInfo(instructionToUse, null); repository.addLocalInstruction(instructionInfo); } context.getPatternState().setCurrentInstructionInfo(instructionInfo); if (instructionInfo.getRowRange() != null) { setLastExpressedRowNumber(instructionInfo.getRowRange().getMaximumInteger(), context); // if setLastExpressedRowNumber resets the local instructions, this // would also clear out the currently executing instruction. We // don't want that to happen, so add it back in. // FIXME this is really backwards; maybe we should redo this if (repository.getInstruction(instruction.getId()) == null) { repository.addLocalInstruction(instructionInfo); } } renderer.beginInstruction(instructionInfo); InstructionController embeddedController = new InstructionController(getEventHandlerFactory()); embeddedController.visitInstruction(instructionToUse, renderer); context.getPatternState().clearCurrentInstructionInfo(); renderer.endInstruction(); // restore to the way things were (after engine processing but before // this method was called) renderer.getRenderingContext().getEngine().restore(afterVisitingEngineState); }