mirror of
https://github.com/igorski/bitmappery.git
synced 2026-06-17 03:34:56 +02:00
Added pixel precies grid overlay for pixel art mode
This commit is contained in:
@@ -310,10 +310,19 @@
|
||||
@click="close()"
|
||||
>
|
||||
<li>
|
||||
<button v-t="'snapAlign'" :class="{ checked: snapAlign }" type="button" @click="canSnapAndAlign = !canSnapAndAlign"></button>
|
||||
<button v-t="'snapAlign'" type="button" :class="{ checked: snapAlign }" @click="canSnapAndAlign = !canSnapAndAlign"></button>
|
||||
</li>
|
||||
<li>
|
||||
<button v-t="'antiAlias'" :class="{ checked: antiAlias }" type="button" @click="useAntiAlias = !useAntiAlias"></button>
|
||||
<button v-t="'antiAlias'" type="button" :class="{ checked: antiAlias }" @click="useAntiAlias = !useAntiAlias"></button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
v-t="'pixelGrid'"
|
||||
type="button"
|
||||
:class="{ checked: pixelGrid }"
|
||||
:disabled="!canUsePixelGrid"
|
||||
@click="usePixelGrid = !usePixelGrid"
|
||||
></button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -366,6 +375,7 @@
|
||||
<script>
|
||||
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
import { MAX_SPRITESHEET_WIDTH } from "@/definitions/editor-properties";
|
||||
import {
|
||||
CREATE_DOCUMENT, RESIZE_DOCUMENT, EXPORT_DOCUMENT, EXPORT_IMAGE, LOAD_SELECTION, SAVE_SELECTION,
|
||||
DROPBOX_FILE_SELECTOR, SAVE_DROPBOX_DOCUMENT, PREFERENCES, RESIZE_CANVAS, GRID_TO_LAYERS
|
||||
@@ -406,6 +416,7 @@ export default {
|
||||
"getPreferences",
|
||||
"hasSelection",
|
||||
"snapAlign",
|
||||
"pixelGrid",
|
||||
]),
|
||||
supportsFullscreen,
|
||||
noDocumentsAvailable() {
|
||||
@@ -440,16 +451,32 @@ export default {
|
||||
await this.storePreferences();
|
||||
}
|
||||
},
|
||||
usePixelGrid: {
|
||||
get() {
|
||||
return this.pixelGrid;
|
||||
},
|
||||
set( value ) {
|
||||
this.setPixelGrid( value );
|
||||
},
|
||||
},
|
||||
fullscreenTooltip() {
|
||||
return `${this.isFullscreen ? this.$t( "minimize" ) : this.$t( "maximize" )} (Shift + F)`;
|
||||
},
|
||||
canUsePixelGrid() {
|
||||
return this.activeDocument?.width <= MAX_SPRITESHEET_WIDTH;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
blindActive( isOpen, wasOpen ) {
|
||||
if ( !isOpen && wasOpen === true ) {
|
||||
this.setMenuOpened( false );
|
||||
}
|
||||
}
|
||||
},
|
||||
canUsePixelGrid( value ) {
|
||||
if ( !value && this.usePixelGrid ) {
|
||||
this.usePixelGrid = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if ( this.$refs.fullscreenBtn ) {
|
||||
@@ -472,6 +499,7 @@ export default {
|
||||
"setPreferences",
|
||||
"setSnapAlign",
|
||||
"setAntiAlias",
|
||||
"setPixelGrid",
|
||||
"updateLayer",
|
||||
]),
|
||||
...mapActions([
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"view": "View",
|
||||
"snapAlign": "Snap / auto align",
|
||||
"antiAlias": "Anti-alias",
|
||||
"pixelGrid": "Pixel grid",
|
||||
"window": "Window",
|
||||
"noDocumentsOpen": "There are no open documents",
|
||||
"windowNumName": "Window #{num}: {name}",
|
||||
|
||||
@@ -111,6 +111,7 @@ export default {
|
||||
"antiAlias",
|
||||
"canvasDimensions",
|
||||
"snapAlign",
|
||||
"pixelGrid",
|
||||
"zoomOptions",
|
||||
]),
|
||||
documentTitle() {
|
||||
@@ -120,6 +121,9 @@ export default {
|
||||
}
|
||||
return `${name}.${PROJECT_FILE_EXTENSION}`;
|
||||
},
|
||||
hasGuideRenderer() {
|
||||
return this.snapAlign || this.pixelGrid;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
windowSize() {
|
||||
@@ -194,7 +198,7 @@ export default {
|
||||
zCanvas.addChild( sprite );
|
||||
});
|
||||
zCanvas?.interactionPane.stayOnTop();
|
||||
this.snapAlign && guideRenderer.stayOnTop();
|
||||
this.hasGuideRenderer && guideRenderer.stayOnTop();
|
||||
},
|
||||
},
|
||||
activeLayer( layer ) {
|
||||
@@ -242,9 +246,15 @@ export default {
|
||||
selectMode( value ) {
|
||||
this.updateInteractionPane();
|
||||
},
|
||||
snapAlign( value ) {
|
||||
hasGuideRenderer( value ) {
|
||||
getCanvasInstance()?.[ value ? "addChild" : "removeChild" ]( guideRenderer );
|
||||
},
|
||||
snapAlign( value ) {
|
||||
this.updateGuideModes();
|
||||
},
|
||||
pixelGrid( value ) {
|
||||
this.updateGuideModes();
|
||||
},
|
||||
antiAlias( value ) {
|
||||
getCanvasInstance()?.setSmoothing( value );
|
||||
},
|
||||
@@ -287,7 +297,8 @@ export default {
|
||||
handler: this.handleCanvasEvent.bind( this ),
|
||||
}, this.$store, this.calcIdealDimensions );
|
||||
setCanvasInstance( zCanvas );
|
||||
guideRenderer = new GuideRenderer( this.snapAlign ? zCanvas : null );
|
||||
guideRenderer = new GuideRenderer( this.hasGuideRenderer ? zCanvas : null );
|
||||
this.updateGuideModes();
|
||||
return zCanvas;
|
||||
},
|
||||
cacheContainerSize() {
|
||||
@@ -389,6 +400,9 @@ export default {
|
||||
break;
|
||||
}
|
||||
},
|
||||
updateGuideModes() {
|
||||
guideRenderer.setModes( this.snapAlign, this.pixelGrid );
|
||||
},
|
||||
handleGuides() {
|
||||
if ( !this.snapAlign ) {
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Igor Zinken 2021 - https://www.igorski.nl
|
||||
* Igor Zinken 2021-2022 - https://www.igorski.nl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
@@ -26,6 +26,8 @@ import { fastRound } from "@/math/unit-math";
|
||||
import { isCoordinateInHorizontalRange, isCoordinateInVerticalRange } from "@/math/point-math";
|
||||
import { getClosestSnappingPoints } from "@/rendering/snapping";
|
||||
|
||||
const AMOUNT_OF_PIXELS = 1; // currently only 1 pixel grid supported
|
||||
|
||||
class GuideRenderer extends sprite {
|
||||
constructor( zCanvasInstance = null ) {
|
||||
super({ interactive: false });
|
||||
@@ -40,12 +42,42 @@ class GuideRenderer extends sprite {
|
||||
zCanvas?.addChild( this );
|
||||
}
|
||||
|
||||
setModes( drawGuides, drawPixelGrid ) {
|
||||
this.drawGuides = drawGuides;
|
||||
this.drawPixelGrid = drawPixelGrid;
|
||||
}
|
||||
|
||||
draw( ctx, viewport = null ) {
|
||||
if ( !this.canvas.guides || !this.canvas.draggingSprite ) {
|
||||
|
||||
/* grid */
|
||||
|
||||
if ( this.drawPixelGrid ) {
|
||||
const width = viewport?.width || this.canvas.getWidth();
|
||||
const height = viewport?.height || this.canvas.getHeight();
|
||||
|
||||
ctx.strokeStyle = "000";
|
||||
ctx.lineWidth = 1 / this.canvas.zoomFactor;
|
||||
|
||||
ctx.beginPath();
|
||||
for ( let x = 0; x < width; x += AMOUNT_OF_PIXELS ) {
|
||||
ctx.moveTo( x, 0 );
|
||||
ctx.lineTo( x, height );
|
||||
}
|
||||
for ( let y = 0; y < height; y += AMOUNT_OF_PIXELS ) {
|
||||
ctx.moveTo( 0, y );
|
||||
ctx.lineTo( width, y );
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
/* guides */
|
||||
|
||||
if ( !this.drawGuides || !this.canvas.guides || !this.canvas.draggingSprite ) {
|
||||
return;
|
||||
}
|
||||
const left = viewport?.left || 0;
|
||||
const top = viewport?.top || 0;
|
||||
|
||||
const left = viewport?.left || 0;
|
||||
const top = viewport?.top || 0;
|
||||
|
||||
// we can snap the currently draggingSprite against its edge and center
|
||||
const guides = getClosestSnappingPoints( this.canvas.draggingSprite, this.canvas.guides );
|
||||
|
||||
@@ -650,8 +650,8 @@ class LayerSprite extends ZoomableSprite {
|
||||
const destX = x - viewport.left;
|
||||
const destY = y - viewport.top;
|
||||
if ( this.isRotated()) {
|
||||
const tX = destX + ( width * .5 );
|
||||
const tY = destY + ( height * .5 );
|
||||
const tX = destX + ( width * HALF );
|
||||
const tY = destY + ( height * HALF );
|
||||
documentContext.translate( tX, tY );
|
||||
documentContext.rotate( this.layer.effects.rotation );
|
||||
documentContext.translate( -tX, -tY );
|
||||
@@ -684,7 +684,7 @@ function rotatePointerLists( pointers, layer, sourceWidth, sourceHeight ) {
|
||||
pointers.forEach( point => {
|
||||
// translate recorded pointer towards rotated point
|
||||
// and against layer position
|
||||
const p = translatePointerRotation( point.x - x, point.y - y, sourceWidth * 0.5, sourceHeight * 0.5, rotation );
|
||||
const p = translatePointerRotation( point.x - x, point.y - y, sourceWidth * HALF, sourceHeight * HALF, rotation );
|
||||
if ( mirrorX ) {
|
||||
p.x -= sourceWidth;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export default {
|
||||
},
|
||||
snapAlign : true,
|
||||
antiAlias : true,
|
||||
pixelGrid : false,
|
||||
},
|
||||
getters: {
|
||||
activeTool : state => state.activeTool,
|
||||
@@ -55,6 +56,7 @@ export default {
|
||||
cloneOptions : state => state.options[ ToolTypes.CLONE ],
|
||||
snapAlign : state => state.snapAlign,
|
||||
antiAlias : state => state.antiAlias,
|
||||
pixelGrid : state => state.pixelGrid,
|
||||
},
|
||||
mutations: {
|
||||
setActiveTool( state, { tool, document }) {
|
||||
@@ -86,6 +88,9 @@ export default {
|
||||
setAntiAlias( state, value ) {
|
||||
state.antiAlias = value;
|
||||
},
|
||||
setPixelGrid( state, value ) {
|
||||
state.pixelGrid = value;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user