// emit gcode for an extrude move
        protected virtual void emit_extrude(QueuedExtrude p)
        {
            double write_x = p.toPos.x + PositionShift.x;
            double write_y = p.toPos.y + PositionShift.y;

            Builder.BeginGLine(1, p.comment).
            AppendF("X", write_x).AppendF("Y", write_y);

            if (OmitDuplicateZ == false || MathUtil.EpsilonEqual(p.toPos.z, currentPos.z, MoveEpsilon) == false)
            {
                Builder.AppendF("Z", p.toPos.z);
            }
            if (OmitDuplicateF == false || MathUtil.EpsilonEqual(p.feedRate, currentFeed, MoveEpsilon) == false)
            {
                Builder.AppendF("F", p.feedRate);
            }
            if (OmitDuplicateE == false || MathUtil.EpsilonEqual(p.extruderA, extruderA, MoveEpsilon) == false)
            {
                Builder.AppendF(p.extrudeChar.ToString(), p.extruderA);
            }

            currentPos  = p.toPos;
            currentFeed = p.feedRate;
            extruderA   = p.extruderA;
        }
        // push point onto queue and update accumulated length
        protected virtual void append_to_queue(QueuedExtrude p)
        {
            double dt = (next_queue_index == 0) ?
                        currentPos.xy.Distance(p.toPos.xy) : extrude_queue[next_queue_index - 1].toPos.xy.Distance(p.toPos.xy);

            extrude_queue_len += dt;

            extrude_queue[next_queue_index] = p;
            next_queue_index = Math.Min(next_queue_index + 1, extrude_queue.Length - 1);;
        }
            static public QueuedExtrude lerp(ref QueuedExtrude a, ref QueuedExtrude b, double t)
            {
                QueuedExtrude newp = new QueuedExtrude();

                newp.toPos     = Vector3d.Lerp(a.toPos, b.toPos, t);
                newp.feedRate  = Math.Max(a.feedRate, b.feedRate);
                newp.extruderA = MathUtil.Lerp(a.extruderA, b.extruderA, t);
                newp.comment   = (a.comment == null) ? a.comment : b.comment;
                return(newp);
            }
        // emit static retraction move (no xyz movement)
        protected virtual void emit_retract(QueuedExtrude p)
        {
            Builder.BeginGLine(1, p.comment);

            BuildFeedrateParameter(p.feedRate);
            BuildExtrudeParameter(p.extruderA);

            currentPos  = p.toPos;
            currentFeed = p.feedRate;
            extruderA   = p.extruderA;
        }
        // push an extrude move onto queue
        protected virtual void queue_extrude(Vector3d toPos, double feedRate, double e, string comment, bool bIsRetract)
        {
            Util.gDevAssert(InExtrude || bIsRetract);
            if (EnableBoundsChecking && PositionBounds.Contains(toPos.xy) == false)
            {
                throw new Exception("BaseDepositionAssembler.queue_extrude: tried to move outside of bounds!");
            }

            lastPos = toPos;

            QueuedExtrude p = new QueuedExtrude()
            {
                toPos     = toPos,
                feedRate  = feedRate,
                extruderA = e,
                comment   = comment
            };

            // we cannot queue a retract, so flush queue and emit the retract/unretract
            if (bIsRetract)
            {
                flush_extrude_queue();
                emit_retract(p);
                return;
            }
            else if (toPos.z != NozzlePosition.z)
            {
                flush_extrude_queue();
                emit_extrude(p);
                return;
            }

            // push this point onto queue. this will also update the extrude_queue_len
            double prev_len = extrude_queue_len;

            append_to_queue(p);

            // if we haven't moved far enough to emit a point, we wait
            if (extrude_queue_len < MinExtrudeStepDistance)
            {
                return;
            }
            // ok we moved far enough from last point to emit

            // if queue has one point, just emit it
            int last_i = next_queue_index - 1;

            if (last_i == 0)
            {
                flush_extrude_queue();
                return;
            }

            // otherwise we lerp between last two points so that we emit at
            // point where accumulated linear arclength is exactly MinExtrudeStepDistance.
            double a = prev_len, b = extrude_queue_len;
            double t = (MinExtrudeStepDistance - a) / (b - a);

            Util.gDevAssert(t > -0.0001 && t < 1.0001);
            t = MathUtil.Clamp(t, 0, 1);
            QueuedExtrude last_p = extrude_queue[next_queue_index - 1];
            QueuedExtrude emit_p = QueuedExtrude.lerp(ref extrude_queue[next_queue_index - 2], ref last_p, t);

            // emit and clear queue
            emit_extrude(emit_p);
            next_queue_index  = 0;
            extrude_queue_len = 0;

            // now we re-submit last point. This pushes the remaining bit of the last segment
            // back onto the queue. (should we skip this if t > nearly-one?)
            queue_extrude(last_p.toPos, last_p.feedRate, last_p.extruderA, last_p.comment, false);
        }