2011-06-27 55 views
1

我在繪製筆劃的多個小部分時使用不同的點大小時出現問題,我試圖根據寫入速度來模擬更改筆寬度,寫入時筆寬較大,寫得很快時很小。這是我的代碼(我用GLPaint的蘋果源代碼學習):根據寫入速度更改筆寬

static GLfloat*  vertexBuffer = NULL; 
static NSUInteger vertexMax = 64; 
NSUInteger   vertexCount = 0, 
        count, 
        i; 

[EAGLContext setCurrentContext:context]; 
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); 

// Convert locations from Points to Pixels 
CGFloat scale = self.contentScaleFactor; 
start.x *= scale; 
start.y *= scale; 
end.x *= scale; 
end.y *= scale; 

// Allocate vertex array buffer 
if(vertexBuffer == NULL) { 
    vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat)); 
} 

// Add points to the buffer so there are drawing points every X pixels 
count = MAX(ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y))/kBrushPixelStep), 1); 
for(i = 0; i < count; ++i) { 
    if(vertexCount == vertexMax) { 
     vertexMax = 2 * vertexMax; 
     vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat)); 
    } 

    vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((GLfloat)i/(GLfloat)count); 
    vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((GLfloat)i/(GLfloat)count); 
    vertexCount += 1; 
} 

if (vertexCount > 0) { 
    GLfloat rate = ((GLfloat)vertexCount)/MAX_BETWEEN_POINTS; 
    if (rate > 0.75) { 
     rate = 0.75; 
    } 
    GLfloat distract = penWidth * rate/vertexCount; 

    for (int i = 0; i < vertexCount; i++) { 
     GLfloat * smallBuf = malloc(4 * sizeof(GLfloat)); 
     smallBuf[0] = vertexBuffer[4*i + 0]; 
     smallBuf[1] = vertexBuffer[4*i + 1]; 
     smallBuf[2] = vertexBuffer[4*i + 2]; 
     smallBuf[3] = vertexBuffer[4*i + 3]; 

     if (lastPenWidth - distract < penWidth*0.75) { 
      lastPenWidth = penWidth*0.75; 
     } else { 
      lastPenWidth = lastPenWidth - distract; 
     } 
     glPointSize(lastPenWidth); 
     // Render the vertex array 
     glVertexPointer(2, GL_FLOAT, 0, smallBuf); 
     glDrawArrays(GL_POINTS, 0, 2); 
     free(smallBuf); 
    } 
    NSLog(@"Vertext count: %d --- Distract: %0.2f --- Rate: %0.2f", vertexCount, distract, rate); 
} else { 
    glPointSize(penWidth); 

    NSLog(@"Vertext count: %d", vertexCount); 
    // Render the vertex array 
    glVertexPointer(2, GL_FLOAT, 0, vertexBuffer); 
    glDrawArrays(GL_POINTS, 0, vertexCount); 
} 
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); 
[context presentRenderbuffer:GL_RENDERBUFFER_OES]; 

但我收到此結果與行程的中間許多點。你對這個問題有什麼想法嗎?

enter image description here

非常感謝你。

回答

0

Flash & Math - A Smooth and Responsive Drawing Application in AS3 Flash這優秀的AS3代碼是不難適應任何您選擇的語言。我附加它,因爲不是每個人都有Flash。

/* 
http://www.flashandmath.com/advanced/smoothdraw/index.html 
By Dan Gries 
www.flashandmath.com 
dan A T flashandmath D O T com 

*/ 

import com.flashandmath.dg.GUI.GradientSwatch; 
import com.flashandmath.dg.bitmapUtilities.BitmapSaver; 


var lineLayer:Sprite; 
var lastSmoothedMouseX:Number; 
var lastSmoothedMouseY:Number; 
var lastMouseX:Number; 
var lastMouseY:Number; 
var lastThickness:Number; 
var lastRotation:Number; 
var lineColor:uint; 
var lineThickness:Number; 
var lineRotation:Number; 
var L0Sin0:Number; 
var L0Cos0:Number; 
var L1Sin1:Number; 
var L1Cos1:Number; 
var sin0:Number; 
var cos0:Number; 
var sin1:Number; 
var cos1:Number; 
var dx:Number; 
var dy:Number; 
var dist:Number; 
var targetLineThickness:Number; 
var colorLevel:Number; 
var targetColorLevel:Number; 
var smoothedMouseX:Number; 
var smoothedMouseY:Number; 
var tipLayer:Sprite; 
var boardBitmap:Bitmap; 
var boardBitmapData:BitmapData; 
var bitmapHolder:Sprite; 
var boardWidth:Number; 
var boardHeight:Number; 
var smoothingFactor:Number; 
var mouseMoved:Boolean; 
var dotRadius:Number; 
var startX:Number; 
var startY:Number; 
var undoStack:Vector.<BitmapData>; 
var minThickness:Number; 
var thicknessFactor:Number; 
var mouseChangeVectorX:Number; 
var mouseChangeVectorY:Number; 
var lastMouseChangeVectorX:Number; 
var lastMouseChangeVectorY:Number; 

var thicknessSmoothingFactor:Number; 

var bitmapSaver:BitmapSaver; 

var controlVecX:Number; 
var controlVecY:Number; 
var controlX1:Number; 
var controlY1:Number; 
var controlX2:Number; 
var controlY2:Number; 

var tipTaperFactor:Number; 

var numUndoLevels:Number; 

var controlPanel:Sprite; 
var swatches:Vector.<GradientSwatch>; 
var swatchColors:Vector.<uint>; 

var paintColorR1:Number; 
var paintColorG1:Number; 
var paintColorB1:Number; 
var paintColorR2:Number; 
var paintColorG2:Number; 
var paintColorB2:Number; 

var red:Number; 
var green:Number; 
var blue:Number; 

var colorChangeRate:Number; 

var panelColor:uint; 

var boardMask:Sprite; 

//// 

//Setting the following NO_SCALE parameter helps avoid strange artifacts 
//in the displayed bitmaps caused by repositioning of the swf within the html page. 
stage.scaleMode=StageScaleMode.NO_SCALE; 

init(); 

//// 

function init():void { 

    boardWidth = 700; 
    boardHeight = 500; 

    minThickness = 0.2; 
    thicknessFactor = 0.25; 

    smoothingFactor = 0.3; //Should be set to something between 0 and 1. Higher numbers mean less smoothing. 
    thicknessSmoothingFactor = 0.3; 

    dotRadius = 2; //radius for drawn dot if there is no mouse movement between mouse down and mouse up. 

    tipTaperFactor = 0.8; 

    numUndoLevels = 10; 

    colorChangeRate = 0.05; 

    panelColor = 0xAAAAAA; 

    paintColorR1 = 16; 
    paintColorG1 = 0; 
    paintColorB1 = 0; 
    paintColorR2 = 128; 
    paintColorG2 = 0; 
    paintColorB2 = 0; 

    swatchColors = Vector.<uint>([0x100000, 0x800000, 
            darkenColor(0xA24F31,0.5), 0xA24F31, 
            darkenColor(0x906000,0.33), 0x906000, 
            darkenColor(0xB48535,0.5), 0xB48535, 
            darkenColor(0x938E60,0.75),0x938E60, 
            darkenColor(0x6F7D4F,0.4),0x6F7D4F, 
            0x000000, 0x226600, 
            darkenColor(0x8FAD81, 0.75), 0x8FAD81, 
            0x000000, 0x005077,         
            darkenColor(0x4F848A,0.5),0x4F848A, 
            darkenColor(0x646077,0.5),0x646077, 
            darkenColor(0x784B67,0.4),0x784B67, 
            darkenColor(0x9A659A, 0.4), 0x9A659A, 
            0x000000, 0x606060, 
            0x000000, 0x000000, 
            0xD0D0D0, 0xFFFFFF]); 
    swatches = new Vector.<GradientSwatch>; 

    boardBitmapData = new BitmapData(boardWidth, boardHeight, false); 
    boardBitmap = new Bitmap(boardBitmapData); 


    //The undo buffer will hold the previous drawing. 
    //If we want more levels of undo, we would have to record several undo buffers. We only use one 
    //here for simplicity. 
    undoStack = new Vector.<BitmapData>; 
    bitmapHolder = new Sprite(); 
    lineLayer = new Sprite(); 

    boardMask = new Sprite(); 
    boardMask.graphics.beginFill(0xFF0000); 
    boardMask.graphics.drawRect(0,0,boardWidth,boardHeight); 
    boardMask.graphics.endFill(); 

    drawBackground(); 

    /* 
    The tipLayer holds the tip portion of the line. 
    Because of the smoothing technique we are using, while the user is drawing the drawn line will not 
    extend all the way from the last position to the current mouse position. We use a small 'tip' to 
    complete this line all the way to the current mouse position. 
    */ 
    tipLayer = new Sprite(); 
    tipLayer.mouseEnabled = false; 

    /* 
    Bitmaps cannot receive mouse events. so we add it to a holder sprite. 
    */ 
    this.addChild(bitmapHolder); 
    bitmapHolder.x = 5; 
    bitmapHolder.y = 5; 
    bitmapHolder.addChild(boardBitmap); 
    bitmapHolder.addChild(tipLayer); 
    bitmapHolder.addChild(boardMask); 
    bitmapHolder.mask = boardMask; 

    //We add the panel at the bottom which will hold color swatches 
    controlPanel = new Sprite(); 
    controlPanel.graphics.beginFill(panelColor); 
    controlPanel.graphics.drawRect(0, 0, boardWidth, 40); 
    controlPanel.graphics.endFill(); 
    controlPanel.x = bitmapHolder.x; 
    controlPanel.y = bitmapHolder.y + boardHeight+2; 
    this.addChild(controlPanel); 
    createSwatches(); 

    var btnErase:BtnErase = new BtnErase(); 
    btnErase.x = 690; 
    btnErase.y = controlPanel.height/2;; 
    btnErase.addEventListener(MouseEvent.CLICK, erase); 
    controlPanel.addChild(btnErase); 

    var btnUndo:BtnUndo = new BtnUndo(); 
    btnUndo.x = 620; 
    btnUndo.y = controlPanel.height/2;; 
    btnUndo.addEventListener(MouseEvent.CLICK, undoButtonHandler); 
    controlPanel.addChild(btnUndo); 

    var btnExport:BtnExport = new BtnExport(); 
    btnExport.x = 550; 
    btnExport.y = controlPanel.height/2;; 
    btnExport.addEventListener(MouseEvent.CLICK, exportHandler); 
    controlPanel.addChild(btnExport); 

    bitmapSaver = new BitmapSaver(boardBitmapData); 
    bitmapSaver.x = 0.5*(boardWidth - bitmapSaver.width); 
    bitmapSaver.y = 0.5*(boardHeight - bitmapSaver.height); 

    bitmapHolder.addEventListener(MouseEvent.MOUSE_DOWN, startDraw); 
    stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownListener); 
} 

function createSwatches():void { 
    var swatchLength = Math.floor(0.8*controlPanel.height); 
    var space:Number = 5; 
    for (var i:Number = 0; i< swatchColors.length; i=i+2) { 
     var thisSwatch:GradientSwatch = new GradientSwatch(swatchColors[i], swatchColors[i+1], 0.75*swatchLength, swatchLength); 
     thisSwatch.x = (space + 0.75*swatchLength)*(i+1)/2; 
     thisSwatch.y = controlPanel.height/2; 
     controlPanel.addChild(thisSwatch); 
     swatches.push(thisSwatch); 
     thisSwatch.addEventListener(MouseEvent.CLICK, swatchClickHandler); 
    } 
} 

function swatchClickHandler(evt:MouseEvent):void { 
    var thisSwatch = evt.currentTarget; 
    paintColorR1 = thisSwatch.red1; 
    paintColorG1 = thisSwatch.green1; 
    paintColorB1 = thisSwatch.blue1; 
    paintColorR2 = thisSwatch.red2; 
    paintColorG2 = thisSwatch.green2; 
    paintColorB2 = thisSwatch.blue2; 
} 

function startDraw(evt:MouseEvent):void { 
    stage.addEventListener(MouseEvent.MOUSE_UP, stopDraw); 

    startX = lastMouseX = smoothedMouseX = lastSmoothedMouseX = bitmapHolder.mouseX; 
    startY = lastMouseY = smoothedMouseY = lastSmoothedMouseY = bitmapHolder.mouseY; 
    lastThickness = 0; 
    lastRotation = Math.PI/2; 
    colorLevel = 0; 
    lastMouseChangeVectorX = 0; 
    lastMouseChangeVectorY = 0; 

    //We will keep track of whether the mouse moves in between a mouse down and a mouse up. If not, 
    //a small dot will be drawn. 
    mouseMoved = false; 

    stage.addEventListener(MouseEvent.MOUSE_MOVE, drawLine); 
    //this.addEventListener(Event.ENTER_FRAME, drawLine); 
} 

function drawLine(evt:MouseEvent):void { 
    mouseMoved = true; 

    lineLayer.graphics.clear(); 

    mouseChangeVectorX = bitmapHolder.mouseX - lastMouseX; 
    mouseChangeVectorY = bitmapHolder.mouseY - lastMouseY; 


    //Cusp detection - if the mouse movement is more than 90 degrees 
    //from the last motion, we will draw all the way out to the last 
    //mouse position before proceeding. We handle this by drawing the 
    //previous tipLayer, and resetting the last smoothed mouse position 
    //to the last actual mouse position. 
    //We use a dot product to determine whether the mouse movement is 
    //more than 90 degrees from the last motion. 
    if (mouseChangeVectorX*lastMouseChangeVectorX + mouseChangeVectorY*lastMouseChangeVectorY < 0) { 
     boardBitmapData.draw(tipLayer); 
     smoothedMouseX = lastSmoothedMouseX = lastMouseX; 
     smoothedMouseY = lastSmoothedMouseY = lastMouseY; 
     lastRotation += Math.PI; 
     lastThickness = tipTaperFactor*lastThickness; 
    } 


    //We smooth out the mouse position. The drawn line will not extend to the current mouse position; instead 
    //it will be drawn only a portion of the way towards the current mouse position. This creates a nice 
    //smoothing effect. 
    smoothedMouseX = smoothedMouseX + smoothingFactor*(bitmapHolder.mouseX - smoothedMouseX); 
    smoothedMouseY = smoothedMouseY + smoothingFactor*(bitmapHolder.mouseY - smoothedMouseY); 

    //We determine how far the mouse moved since the last position. We use this distance to change 
    //the thickness and brightness of the line. 
    dx = smoothedMouseX - lastSmoothedMouseX; 
    dy = smoothedMouseY - lastSmoothedMouseY; 
    dist = Math.sqrt(dx*dx + dy*dy); 

    if (dist != 0) { 
     lineRotation = Math.PI/2 + Math.atan2(dy,dx); 
    } 
    else { 
     lineRotation = 0; 
    } 

    //We use a similar smoothing technique to change the thickness of the line, so that it doesn't 
    //change too abruptly. 
    targetLineThickness = minThickness+thicknessFactor*dist; 
    lineThickness = lastThickness + thicknessSmoothingFactor*(targetLineThickness - lastThickness); 

    /* 
    The "line" being drawn is actually composed of filled in shapes. This is what allows 
    us to create a varying thickness of the line. 
    */ 
    sin0 = Math.sin(lastRotation); 
    cos0 = Math.cos(lastRotation); 
    sin1 = Math.sin(lineRotation); 
    cos1 = Math.cos(lineRotation); 
    L0Sin0 = lastThickness*sin0; 
    L0Cos0 = lastThickness*cos0; 
    L1Sin1 = lineThickness*sin1; 
    L1Cos1 = lineThickness*cos1; 
    targetColorLevel = Math.min(1,colorChangeRate*dist); 
    colorLevel = colorLevel + 0.2*(targetColorLevel - colorLevel); 

    red = paintColorR1 + colorLevel*(paintColorR2 - paintColorR1); 
    green = paintColorG1 + colorLevel*(paintColorG2 - paintColorG1); 
    blue = paintColorB1 + colorLevel*(paintColorB2 - paintColorB1); 

    lineColor = (red << 16) | (green << 8) | (blue); 

    controlVecX = 0.33*dist*sin0; 
    controlVecY = -0.33*dist*cos0; 
    controlX1 = lastSmoothedMouseX + L0Cos0 + controlVecX; 
    controlY1 = lastSmoothedMouseY + L0Sin0 + controlVecY; 
    controlX2 = lastSmoothedMouseX - L0Cos0 + controlVecX; 
    controlY2 = lastSmoothedMouseY - L0Sin0 + controlVecY; 

    lineLayer.graphics.lineStyle(1,lineColor); 
    lineLayer.graphics.beginFill(lineColor); 
    lineLayer.graphics.moveTo(lastSmoothedMouseX + L0Cos0, lastSmoothedMouseY + L0Sin0); 
    lineLayer.graphics.curveTo(controlX1,controlY1,smoothedMouseX + L1Cos1, smoothedMouseY + L1Sin1); 
    lineLayer.graphics.lineTo(smoothedMouseX - L1Cos1, smoothedMouseY - L1Sin1); 
    lineLayer.graphics.curveTo(controlX2, controlY2, lastSmoothedMouseX - L0Cos0, lastSmoothedMouseY - L0Sin0); 
    lineLayer.graphics.lineTo(lastSmoothedMouseX + L0Cos0, lastSmoothedMouseY + L0Sin0); 
    lineLayer.graphics.endFill(); 
    boardBitmapData.draw(lineLayer); 

    //We draw the tip, which completes the line from the smoothed mouse position to the actual mouse position. 
    //We won't actually add this to the drawn bitmap until a mouse up completes the drawing of the current line. 

    //round tip: 
    var taperThickness:Number = tipTaperFactor*lineThickness; 
    tipLayer.graphics.clear(); 
    tipLayer.graphics.beginFill(lineColor); 
    tipLayer.graphics.drawEllipse(bitmapHolder.mouseX - taperThickness, bitmapHolder.mouseY - taperThickness, 2*taperThickness, 2*taperThickness); 
    tipLayer.graphics.endFill(); 
    //quad segment 
    tipLayer.graphics.lineStyle(1,lineColor); 
    tipLayer.graphics.beginFill(lineColor); 
    tipLayer.graphics.moveTo(smoothedMouseX + L1Cos1, smoothedMouseY + L1Sin1); 
    tipLayer.graphics.lineTo(bitmapHolder.mouseX + tipTaperFactor*L1Cos1, bitmapHolder.mouseY + tipTaperFactor*L1Sin1); 
    tipLayer.graphics.lineTo(bitmapHolder.mouseX - tipTaperFactor*L1Cos1, bitmapHolder.mouseY - tipTaperFactor*L1Sin1); 
    tipLayer.graphics.lineTo(smoothedMouseX - L1Cos1, smoothedMouseY - L1Sin1); 
    tipLayer.graphics.lineTo(smoothedMouseX + L1Cos1, smoothedMouseY + L1Sin1); 
    tipLayer.graphics.endFill(); 

    lastSmoothedMouseX = smoothedMouseX; 
    lastSmoothedMouseY = smoothedMouseY; 
    lastRotation = lineRotation; 
    lastThickness = lineThickness; 
    lastMouseChangeVectorX = mouseChangeVectorX; 
    lastMouseChangeVectorY = mouseChangeVectorY; 
    lastMouseX = bitmapHolder.mouseX; 
    lastMouseY = bitmapHolder.mouseY; 

    evt.updateAfterEvent(); 

} 

function stopDraw(evt:MouseEvent):void { 
    //If the mouse didn't move, we will draw just a dot. Its size will be randomized. 
    if (!mouseMoved) { 
     var randRadius = dotRadius*(0.75+0.75*Math.random()); 
     var dotColor:uint = (paintColorR1 << 16) | (paintColorG1 << 8) | (paintColorB1); 
     var dot:Sprite = new Sprite(); 
     dot.graphics.beginFill(dotColor) 
     dot.graphics.drawEllipse(startX - randRadius, startY - randRadius, 2*randRadius, 2*randRadius); 
     dot.graphics.endFill(); 
     boardBitmapData.draw(dot); 
    } 

    stage.removeEventListener(MouseEvent.MOUSE_MOVE, drawLine); 
    stage.removeEventListener(MouseEvent.MOUSE_UP, stopDraw); 

    //We add the tipLayer to complete the line all the way to the current mouse position: 
    boardBitmapData.draw(tipLayer); 

    //record undo bitmap and add to undo stack 
    var undoBuffer:BitmapData = new BitmapData(boardWidth, boardHeight, false); 
    undoBuffer.copyPixels(boardBitmapData,undoBuffer.rect,new Point(0,0)); 
    undoStack.push(undoBuffer); 
    if (undoStack.length > numUndoLevels + 1) { 
     undoStack.splice(0,1); 
    } 

} 

function erase(evt:MouseEvent):void { 
    tipLayer.graphics.clear(); 
    drawBackground(); 
} 

function drawBackground():void { 
    //We draw a background with a very subtle gradient effect so that the canvas darkens towards the edges. 
    var gradMat:Matrix = new Matrix(); 
    gradMat.createGradientBox(700,500,0,0,0); 
    var bg:Sprite = new Sprite(); 
    bg.graphics.beginGradientFill("radial",[0xDDD0AA,0xC6B689],[1,1],[1,255],gradMat); 
    bg.graphics.drawRect(0,0,700,500); 
    bg.graphics.endFill(); 
    boardBitmapData.draw(bg); 

    //We clear out the undo buffer with a copy of just a blank background: 
    undoStack = new Vector.<BitmapData>; 
    var undoBuffer:BitmapData = new BitmapData(boardWidth, boardHeight, false); 
    undoBuffer.copyPixels(boardBitmapData,undoBuffer.rect,new Point(0,0)); 
    undoStack.push(undoBuffer); 
} 

function undoButtonHandler(evt:MouseEvent):void { 
    undo(); 
} 

function keyDownListener(evt:KeyboardEvent):void { 
    //Listening for Z, which will be a keyboard shortcut for undo. 
    if ((evt.keyCode == 90)) { 
     undo(); 
    } 
} 

function undo():void { 
    if (undoStack.length > 1) { 
     boardBitmapData.copyPixels(undoStack[undoStack.length - 2],boardBitmapData.rect,new Point(0,0)); 
     undoStack.splice(undoStack.length - 1, 1); 
    } 
    tipLayer.graphics.clear(); 
} 

//this function assists with creating colors for the gradients. 
function darkenColor(c:uint, factor:Number):uint { 
    var r:Number = (c >> 16); 
    var g:Number = (c >> 8) & 0xFF; 
    var b:Number = c & 0xFF; 

    var newRed:Number = Math.min(255, r*factor); 
    var newGreen:Number = Math.min(255, g*factor); 
    var newBlue:Number = Math.min(255, b*factor); 

    return (newRed << 16) | (newGreen << 8) | (newBlue); 
} 

function exportHandler(evt:MouseEvent):void { 
    this.addChild(bitmapSaver); 
    bitmapSaver.addEventListener(BitmapSaver.BUTTON_CLICKED, closeWindow); 
} 

function closeWindow(evt:Event):void { 
    this.removeChild(bitmapSaver); 
    bitmapSaver.removeEventListener(BitmapSaver.BUTTON_CLICKED, closeWindow); 
} 
3

您看過this link平滑圖嗎?你最好根據你的需要修改它,它在cocos2d中。