How can I make a wxColorPickerCtrl narrower? - wxwidgets

wxColourPickerCtrl (WXMSW): Can it be narrower without looking awful?
I'm trying to save some horizontal space in a wxWidgets/DialogBlocks project. One place that I think I could save is in the width of a wxColorPickerCtrl, which seems to like to be 95 pixels wide.
That seems unnecessarily wide. However, when I set its width to something nicer, like 25px, I get something appallingly ugly.
Is there a (supported) way to get the control to narrow nicely?
It seems as though it might be possible, because I'm creating this in DialogBlocks, and when I insert the control in the GUI builder, the app shows me something very nice. However, it never renders it that way when I build.
I know the GUI builder is showing me a control and not a static image (au3info says it's a button, the same as what it says when I inspect the window created by the compiled, running app), so it seems like this ought to be possible. I just don't know how.
I have tried a few sensible (IMHO) style changes, but to no avail. The picker control just doesn't resize/clip the image that is on the rendered button.
Is there a way to make the control something very minimal, like a square? I have been unable to find any examples of this (outside of the DialogBlocks GUI builder)
ADDENDUM
I debugged my simple project and followed execution of the wxColourPickerCtrl constructor into generic\clrpickerg.cpp wxGenericColourButton::Create()
m_bitmap = wxBitmap( 60, 13 );
If I change the hardcoded width to, say, 13, the button looks like I want.
Is there a way to alter the underlying bitmap of this member object that's not exactly accessible?
HACKED SOLUTION
Taking the advice of the answer, I had to subclass both wxGenericColourButton (to change the bitmap) and wxColourPickerCtrl (to use the new button class). This will be sensitive to changes in the wxWidgets innards, but I couldn't find any better option.
class _wxGenericColourButton : public wxGenericColourButton {
public:
_wxGenericColourButton() {}
_wxGenericColourButton( wxWindow *parent,
wxWindowID id,
const wxColour& col = *wxBLACK,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxCLRBTN_DEFAULT_STYLE,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxColourPickerWidgetNameStr )
{
Create(parent,id,col,pos,size,style,validator,name);
}
bool Create( wxWindow *parent,
wxWindowID id,
const wxColour& col = *wxBLACK,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxCLRBTN_DEFAULT_STYLE,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxColourPickerWidgetNameStr )
{
// Generally taken from wxGenericColourButton::Create() [clrpickerg.cpp:44]
// Make the bitmap smaller by default. Enlarging the button should work fine.
m_bitmap = wxBitmap(13, 13);
// create this button
if (!wxBitmapButton::Create( parent, id, m_bitmap, pos,
size, style | wxBU_AUTODRAW, validator, name ))
{
wxFAIL_MSG( wxT("wxGenericColourButton creation failed") );
return false;
}
// and handle user clicks on it
Connect(GetId(), wxEVT_BUTTON,
wxCommandEventHandler(wxGenericColourButton::OnButtonClick),
NULL, this);
m_colour = col;
UpdateColour();
InitColourData();
return true;
}
};
class _wxColourPickerCtrl : public wxColourPickerCtrl {
public:
_wxColourPickerCtrl() {}
_wxColourPickerCtrl( wxWindow *parent, wxWindowID id,
const wxColour& col = *wxBLACK, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = wxCLRP_DEFAULT_STYLE,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxColourPickerCtrlNameStr)
{
Create(parent, id, col, pos, size, style, validator, name);
}
bool Create( wxWindow *parent, wxWindowID id,
const wxColour& col = *wxBLACK,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxCLRP_DEFAULT_STYLE,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxColourPickerCtrlNameStr)
{
// This code is generally taken from: wxColourPickerCtrl::Create() [clrpickercmn.cpp:51]
if (!wxPickerBase::CreateBase(parent, id, col.GetAsString(), pos, size,
style, validator, name))
return false;
// Use our "enhanced" _wxGenericColourButton instead of the one with hardcoded,
// undesirable behavior.
m_picker = new _wxGenericColourButton(this, wxID_ANY, col,
wxDefaultPosition, wxDefaultSize,
GetPickerStyle(style));
// Copied from clrpickercmn.cpp
wxPickerBase::PostCreation();
m_picker->Connect(wxEVT_COLOURPICKER_CHANGED,
wxColourPickerEventHandler(wxColourPickerCtrl::OnColourChange),
NULL, this);
return true;
}
};

Unfortunately there is no clean way to work around this bug. You could derive from wxGenericColourButton and change its m_bitmap and then call UpdateColour() but this would be using undocumented and subject to change API and is just generally ugly.
This really should be fixed in wxGenericColourButton itself, the bitmap should be computed from the provided size (and also updated when the size changes!) and it shouldn't be difficult to do. If you decide to try to do this, please don't hesitate to submit your patches to wxWidgets.

Related

How to export vuelayers map to png or jpeg?

How would I adapt #ghettovoice JSFiddle that saves a map to PDF to save the map to a JPEG or PNG? I have no idea how to attempt this problem so ideally if you know hoe to do it you can explain the logic behind it.
exportMap: function () {
var map = this.$refs.map
map.once('rendercomplete', function () {
var mapCanvas = document.createElement('canvas');
var size = map.getSize();
mapCanvas.width = size[0];
mapCanvas.height = size[1];
var mapContext = mapCanvas.getContext('2d');
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
var opacity = canvas.parentNode.style.opacity;
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
var transform = canvas.style.transform;
// Get the transform parameters from the style's transform matrix
var matrix = transform
.match(/^matrix\(([^(]*)\)$/)[1]
.split(',')
.map(Number);
// Apply the transform to the export map context
CanvasRenderingContext2D.prototype.setTransform.apply(
mapContext,
matrix
);
mapContext.drawImage(canvas, 0, 0);
}
}
);
if (navigator.msSaveBlob) {
// link download attribuute does not work on MS browsers
navigator.msSaveBlob(mapCanvas.msToBlob(), 'map.png');
} else {
var link = document.getElementById('image-download');
link.href = mapCanvas.toDataURL();
link.click();
}
});
map.renderSync();
}
The problem was a combination of missing dependencies (namely FileSaver.js and fakerator.js) and a cross origin server block (CORS block) (Browsers automatically prevent httpRequests to a different domain name unless the server allows it). The first one is fixed by installing the packages while the second one is resolved by setting the crossOrigin Attribute of the ImageWMSLayer to null in my case but possibly to 'Anonymous' for other sources. Hope this helped someone else :)

Best practice for removing Mesh objects in ThreeJS?

What are some common ways in ThreeJS to work with creating Meshes (consisting of Geometry and Materials) and then later deleting those objects?
My use case is to display lat/long points on a rotating 3D globe. Each point should be clickable and show further information. The points displayed should also be able to change based on data that is binded with Vue.
I currently use something like this but am running into memory leak issues:
var material = new THREE.MeshPhongMaterial({
color: 0xdc143c,
});
var cone = new THREE.Mesh(
new THREE.ConeBufferGeometry(radius, height, 8, 1, true),
material
);
cone.position.y = height * 0.5;
cone.rotation.x = Math.PI;
var sphere = new THREE.Mesh(
new THREE.SphereBufferGeometry(sphereRadius, 16, 8),
material
);
export default class Marker extends THREE.Object3D {
constructor() {
super();
this.name = "Marker";
this.add(cone, sphere);
}
destroy() {
this.remove(cone, sphere);
}
}
Are there libraries on top of Three that makes the management of Meshes/Materials/Geometry more simple to work with?
What I do is create a function in some utils file, and any time I need to dispose of a mesh, I pass the mesh in as the function parameter:
const removeMesh = (meshToRemove) => {
meshToRemove.visible = false;
scene.remove(meshToRemove);
meshToRemove.geometry.dispose();
meshToRemove.material.dispose();
meshToRemove= undefined;
}

Resize an already drawn rectangle paperjs

I am creating a rectangle in a canvas using paper JS. The following is code for drawing the rectangle in vue.js.
created () {
const toolDrag = event => {
let trackingRect = new paper.Path.Rectangle(event.downPoint, event.point)
trackingRect.strokeColor = new paper.Color('#2661D8')
trackingRect.strokeColor.alpha = 0.7
trackingRect.strokeWidth = this.strokeWidth
trackingRect.removeOn({
drag: true,
up: true
})
}
// Finalise rectangle properties and draw.
let $this = this;
const toolUp = event => {
let newRect = new paper.Path.Rectangle(event.downPoint, event.point)
newRect.strokeColor = new paper.Color(this.getColor().stroke)
newRect.fillColor = new paper.Color(this.getColor().fill)
newRect.strokeWidth = this.strokeWidth
newRect.selected = true;
// Custom data attribute:
newRect.data.type = 'rectangle'
newRect.data.class = ''
// Flag the annotation has been edited and the changes are not saved
this.flagAnnotationEdits()
}
this.toolRect = new paper.Tool();
this.toolRect.onMouseDrag = toolDrag;
this.toolRect.onMouseUp = toolUp;
},
Now I want to allow user to resize this drawn rectangle by dragging any corner of the rectangle, but I am kind of stuck and unable to understand how to do this.
I have seen solutions for resizing a rectangle by changing bounds, but could not find solution for my use case. Any help is appreciated.
There are many ways to achieve what you want.
One of the simplest that I found is to recreate the rectangle, each time you drag one of its corners.
This way, you only have to know the dragged corner position and the opposite corner position and you can easily create the proper rectangle.
Here is a sketch demonstrating a possible implementation.
I think that you should easily be able to transpose it to your specific case.
// This settings controls the handles size.
const HANDLES_RADIUS = 5;
// This flag allow us to know when we are dragging an handle.
let dragging = false;
// This will store the offset from the handle position to the
// mouse down point. This allow a better drag UX.
let offset;
// This will store the point representing the opposite rectangle corner from
// the one being dragged.
let oppositePoint;
// We start by creating a rectangle in the middle of the canvas.
// This rectangle will be replaced each time a corner is dragged.
let rectangle = createRectangle(view.center - 50, view.center + 50);
// Then, we create an handle for each of the corners.
// These will be used to modify the rectangle shape.
const handles = rectangle.segments.map((segment, index) => new Path.Rectangle({
from: segment.point - HANDLES_RADIUS,
to: segment.point + HANDLES_RADIUS,
fillColor: 'orange',
// We store the segment index bound to this specific handle in the custom
// data object. This will allow us to know, when an handle is clicked,
// which segment is concerned by the event.
data: { segmentIndex: index },
// On mouse down on an handle...
onMouseDown: function(event) {
// ...get and store the opposite segment point.
// We will use it later to redraw the rectangle.
const oppositeSegmentIndex = (this.data.segmentIndex + 2) % 4;
oppositePoint = rectangle.segments[oppositeSegmentIndex].point;
// Store the offset.
offset = event.point - rectangle.segments[this.data.segmentIndex].point;
// Activate dragging state.
dragging = true;
}
}));
// On mouse move...
function onMouseMove(event) {
// ...don't do nothing if we are not dragging an handle.
if (!dragging) {
return;
}
// Get the new corner position by applying the offset to the event point.
const activePoint = event.point + offset;
// Recreate the rectangle with the new corner.
rectangle.remove();
rectangle = createRectangle(oppositePoint, activePoint);
// For each corner...
rectangle.segments.forEach((segment, index) => {
// ...place an handle...
handles[index].position = segment.point;
// ...store the potentially new segment <=> corner bound.
handles[index].data.segmentIndex = index;
});
}
// On mouse up...
function onMouseUp() {
// Disable dragging state
dragging = false;
}
//
// HELPERS
//
// This method is used to avoid duplicating the rectangle instantiation code.
function createRectangle(from, to) {
return new Path.Rectangle({
from,
to,
strokeColor: 'orange'
});
}

Updating texture and geometry in three.js

I'm trying to figure out if there is a way of getting a callback once a texture has been updated? I'm swapping the geometry in the scene but sharing the same changing underlying texture based on a canvas and I'm noticing random corruption that eventually disappears as the program runs. I think this is as a result of thrashing that is causing the geometry changes to become unsynchronized with the texture updates.
So I realize the established mechanism is to set is needsUpdate = true on the texture and material, but can I know when that update has been applied so that I can add the new geometry to the scene? I think I am adding the geometry before that update actually gets to the gpu and using the old texture for random frames. Is this possible, or does the texture update definitely get processed before the scene is drawn?
If you can point me in the right direction it would be much appreciated.
Additionally I've added an update event listener to the material, which gets called frequently, but I still end up with the same issues relating to the texture update happening after the geometry has been updated.
Here is an example of what I mean, some code removed. The general idea is to share the same texture / material among many generated objects. Update the canvas, and then add the new object into the scene. This for the most part works under chrome, but occasionally glitches where a frame has the previous texture applied.
var material, texture, texture_canvas, context;
var frames = [];
var total_frames = 24;
var last_frame = undefined;
var current_frame = 0;
var scene;
function init() {
// initialize scene, etc
scene = new THREE.Scene();
// Set up render etc, omitted
texture_canvas = document.createElement('canvas');
texture_canvas.height = 1024;
texgture_canvas.width = 1024;
context = texture_canvas.getContext( '2d' );
context.fillStyle = '#000000';
context.fillRect( 0, 0,
texture_canvas.width,
texture_canvas.height );
texture = new THREE.Texture( this.texture_canvas );
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.generateMipmaps = false;
material = new THREE.MeshLambertMaterial({ map: texture } );
for (var i=0; i<total_frames; i++) {
var mesh = new THREE.Mesh( generate_geometry(), material );
frames.push(mesh);
}
}
function animate() {
if (last_frame) {
scene.remove(frames[last_frame]);
}
// Update / generate new canvas
context.drawImage( generate_image(), 0, 0 );
texture.needsUpdate = true;
material.needsUpdate = true;
frames[current_frame].uvsNeedUpdate = true;
scene.add(frames[current_frame % total_frames]);
last_frame = current_frame;
current_frame++;
requestAnimationFrame( animate );
}

EaselJs shape hitTest unable to set alpha or visiblity

From the sample I created
I am unable to set the circle's alpha / visiblity when mousedown draw line. But I am able to console log it when its detect hitTest.. Is there advise in this?
Below is the block of codes:
var canvas, stage;
var drawingCanvas;
var oldPt;
var oldMidPt;
var title;
var color;
var stroke;
var colors;
var index;
var rect, circle1;
var currMidPt;
$(document).ready(function() {
init();
});
function init() {
canvas = document.getElementById("canvas");
index = 0;
colors = ["#828b20", "#b0ac31", "#cbc53d", "#fad779", "#f9e4ad", "#faf2db", "#563512", "#9b4a0b", "#d36600", "#fe8a00", "#f9a71f"];
//check to see if we are running in a browser with touch support
stage = new createjs.Stage(canvas);
stage.autoClear = false;
stage.enableDOMEvents(true);
createjs.Touch.enable(stage);
createjs.Ticker.setFPS(24);
createjs.Ticker.on("tick", tick);
drawingCanvas = new createjs.Shape();
stage.addEventListener("stagemousedown", handleMouseDown);
stage.addEventListener("stagemouseup", handleMouseUp);
title = new createjs.Text("Click and Drag to draw", "36px Arial", "#777777");
title.x = 300;
title.y = 200;
rect = new createjs.Shape();
rect.graphics.beginFill("#000").drawRect(0, 0, stage.canvas.width, stage.canvas.height);
var container = new createjs.Container();
container.x = 0;
container.y = 0;
stage.addChild(container, title);
stage.addChild(drawingCanvas);
circle1 = new createjs.Shape();
circle1.graphics.beginFill("#990000").drawCircle(120,120,40);
container.addChild(circle1);
stage.update();
}
function handleMouseDown(event) {
if (!event.primary) { return; }
if (stage.contains(title)) {
stage.clear();
stage.removeChild(title);
}
color = colors[(index++) % colors.length];
stroke = Math.random() * 30 + 10 | 0;
oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
oldMidPt = oldPt.clone();
stage.addEventListener("stagemousemove", handleMouseMove);
}
function handleMouseMove(event) {
if (!event.primary) { return; }
var midPt = new createjs.Point(oldPt.x + stage.mouseX >> 1, oldPt.y + stage.mouseY >> 1);
drawingCanvas.graphics.clear().setStrokeStyle(stroke, 'round', 'round').beginStroke(color).moveTo(midPt.x, midPt.y).curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPt.x;
oldMidPt.y = midPt.y;
currMidPt = midPt;
if(circle1.hitTest(currMidPt.x, currMidPt.y)) {
console.log('test');
circle1.alpha = 0.6;
circle1.visible = false;
}
stage.update();
}
function tick(event) {
// console.log(ndgmr.checkPixelCollision(drawingCanvas,circle1,0,false));
stage.update(event);
}
function handleMouseUp(event) {
if (!event.primary) { return; }
stage.removeEventListener("stagemousemove", handleMouseMove);
}
The main reason this doesn't work is because you are using a stage that never clears itself. This gives you the benefit of a "paint brush", since it just adds new curves as you draw, but the circle can never be removed, since it is painted on to the canvas. If you change the alpha, it just draws on top of the current circle. This is also why your circle gets all aliased, as it constantly draws on top of itself, multiplying the alpha.
Here is a quick edit that shows the circle moving its x position instead of adjusting the alpha. Any time you roll over the original position it will move 10 pixels to the right.
http://codepen.io/lannymcnie/pen/redrgo?editors=0010
The hitTest code you have used is not correct though, since it always checks the x/y position of the pen against the local coordinates of the circle -- this means the position of the circle doesn't matter. To fix this, you need to find the local coordinates instead:
currMidPt = circle1.globalToLocal(midPt.x, midPt.y);
Here is an updated fiddle showing that behaviour: http://codepen.io/lannymcnie/pen/grejVg?editors=0010
However, to get the effect you are probably looking for, you have to take a different approach. Instead of using the stage auto-clear, use a cached Shape, and update the cache when you draw. This will provide the paint effect, but let the rest of the stage get updated as usual.
// Remove this!
stage.autoClear = false;
// Cache the drawingCanvas once after creating it
drawingCanvas = new createjs.Shape();
drawingCanvas.cache(0,0,canvas.width,canvas.height);
// After drawing, update the cache. This is at the end of the mousemove function.
// Use "source-over" to apply the new contents on top, instead of clearing the cache.
drawingCanvas.updateCache("source-over");
stage.update();
I also made a few other changes:
Removed the Ticker listener that updates the stage. Your mousemove already updates the stage when the contents change. If you want to reintroduce the ticker update for other content, then remove the stage.update() calls everywhere else, as they are redundant.
Moved the drawingCanvas below the circle/container. This just makes sure the circle is always visible for the demo
Here is a final demo with these changes:
http://codepen.io/lannymcnie/pen/NNYLPX?editors=0010
Hope that helps!