Leaflet-canvas-markers is extending L.circleMarker.
And therefor it should be possible to set stroke to the L.canvasMarker(?).
Does someone know what im doing wrong, or what changes i need to make in leaflet-canvas-marker package to make it work?
Here is a jsfiddle; click on the circleMarker, it gets a thicker stroke. but if you click the canvasMarker it does not get anything.
https://jsfiddle.net/tq7nf3j2/
let marker1 = L.canvasMarker([58.970471,5.730373], {
radius: 15,
img: {
url: 'img.png',
size: [30,30]
}
}).addTo(map);
marker1.on('click', function(e) {
e.target.setStyle({weight: 10})
})
Change the _updateImg(layer) function to:
_updateImg(layer) {
const { img } = layer.options;
const p = layer._point.round();
p.x += img.offset.x; p.y += img.offset.y;
if (img.rotate) {
this._ctx.save();
this._ctx.translate(p.x, p.y);
this._ctx.rotate(img.rotate * Math.PI / 180);
this._ctx.drawImage(img.el, -img.size[0] / 2, -img.size[1] / 2, img.size[0], img.size[1]);
this._ctx.restore();
} else {
if(layer.options.stroke && layer.options.weight > 0){
this._ctx.strokeStyle = layer.options.color;
this._ctx.lineWidth = layer.options.weight;
}
this._ctx.drawImage(img.el, p.x - img.size[0] / 2, p.y - img.size[1] / 2, img.size[0], img.size[1]);
if(layer.options.stroke && layer.options.weight > 0){
this._ctx.strokeRect(p.x - img.size[0] / 2, p.y - img.size[1] / 2, img.size[0], img.size[1]);
}
}
}
If you wan't that it works with rotate you have to copy it to the block above.
It's adding a rectangle with stroke behinde the image.
Also don't forgett to add weight: 0 to the image, because circleMarkers have a default stroke of 3.
let marker1 = L.canvasMarker([58.970471,5.730373], {
radius: 15,
img: {
url: 'https://register.geonorge.no/symbol/files/tilgjengelighet/sittegruppe_positiv_groenn.png',
size: [30,30]
},
weight: 0
}).addTo(map);
Example: https://jsfiddle.net/falkedesign/bLgd0opq/
Related
I am able to display epoch value to Date and Time strings along x-axis in QML Chartview Lineseries. But, I want to split the time and date into separate lines as shown in the attachment.
Can someone please help me on this.
Below is the sample code
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
property var lineSeries
property var x1Val : [1649736833, 1649740465, 1649744065, 1649747665, 1649751265, 1649754865, 1649758465, 1649762065, 1649765665, 1649769265]
property var y1Val : [0,1,2,3,4,5,6,7,8,9]
Component.onCompleted: {
for(var i = 0; i < 2; i++) {
lineSeries = chartView.createSeries(ChartView.SeriesTypeLine, "strend" + i)
lineSeries.axisX = axisx2
lineSeries.axisY = axisy2
for(var iLoop = 0; iLoop < x1Val.length; iLoop++) {
lineSeries.append(x1Val[iLoop] * 1000 + (i * 10000000), y1Val[iLoop])
}
}
}
ChartView {
x: 0
y: 0
width: 640
height: 480
id: chartView
DateTimeAxis {
id: axisx2
min: new Date(1649736833000)
max: new Date(1649779265000)
//format: "yyyy/MM/dd hh:mm"
format: "hh:mm yyyy/MM/dd"
}
ValueAxis {
id: axisy2
min: 0
max: 10
}
}
}
Expected Output
use the format like below. This aligns the time to center of the date.
format: " hh:mmyyyy/MM/dd"
//where   - spacing character in HTML
Thank you all for your help
I'm new to SwiftUI and I'm still working on my spinning wheel. For now I wanna show people the results on screen by Text("(Array[index])"). the question is i know the random rotation degrees but i don't know how to transfer it to that "result angles" index :P ,Any help I will appreciate it !
enter image description here
I use Path to draw the wheel:
var slices: Array<OneChance.OneChanceData> {
let sum = Double(newChoices.count)
var endDeg: Double = 0.0
var tempSlices: [OneChance.OneChanceData] = []
for (color, text) in zip(colors, newChoices) {
let degrees: Double = Double(360 / sum)
tempSlices.append(OneChance.OneChanceData(startAngle: Angle(degrees: endDeg), endAngle: Angle(degrees: endDeg + degrees), text: text, color: color))
endDeg += degrees
}
return tempSlices
}
The wheel view like:
ForEach(0..<chanceVM.newChoices.count) { i in
EachSliceView(chanceData: self.chanceVM.slices[i])
}
I use onTapGesture to get random wheel spinning degrees:
.onTapGesture {
self.chanceVM.showResult = false
self.chanceVM.rotateDegree = 0.0
playSound(sound: "wheelSpinning13s", type: "mp3")
self.chanceVM.allowToTapAgain = false
withAnimation(Animation.easeOut(duration: 13.0)) {
self.chanceVM.rotateDegree = Double.random(in: 5400.0...7200.0)
}
print("\(chanceVM.rotateDegree)")
print("\((Int(chanceVM.rotateDegree) % 360))")
print("\((Int(chanceVM.rotateDegree) % 360)/(360/chanceVM.newChoices.count))")
self.chanceVM.delayText()
self.chanceVM.delayTap()
}
So how can i get the index after random spinning degrees?
Attempting to run a compute kernel results in the following message:
Execution of the command buffer was aborted due to an error during execution. Internal Error (IOAF code 2067)
To get more specific information I query the command encoder's user info and manage to extract more details. I followed instructions from this video to yield the following message:
[Metal Diagnostics] __message__: MTLCommandBuffer execution failed: The commands
associated with the encoder were affected by an error, which may or may not have been
caused by the commands themselves, and failed to execute in full __:::__
__delegate_identifier__: GPUToolsDiagnostics
The breakpoint triggered by the API Validation and Shader Validation results in a record stack frame - not a GPU backtrace. The breakpoint does not indicate any new information apart from the above message.
I cannot find any reference to the mentioned IOAF code in documentation. The additional information printed reveals nothing of assistance. The kernel is quite divergent and I am speculating that may be causing the GPU to take too much time to complete. That may be to blame but I have nothing supporting this apart from a gut feeling.
Here is the thread setup for the group:
let threadExecutionWidth = pipeline.threadExecutionWidth
let threadgroupsPerGrid = MTLSize(width: (Int(pixelCount) + threadExecutionWidth - 1) / threadExecutionWidth, height: 1, depth: 1)
let threadsPerThreadgroup = MTLSize(width: threadExecutionWidth, height: 1, depth: 1)
commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
The GPU commands are being committed and waited upon for completion:
commandEncoder.endEncoding()
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
Here is my application side code in it's entirety:
import Metal
import Foundation
import simd
typealias Float4 = SIMD4<Float>
struct SimpleFileWriter {
var fileHandle: FileHandle
init(filePath: String, append: Bool = false) {
if !FileManager.default.fileExists(atPath: filePath) {
FileManager.default.createFile(atPath: filePath, contents: nil, attributes: nil)
}
fileHandle = FileHandle(forWritingAtPath: filePath)!
if !append {
fileHandle.truncateFile(atOffset: 0)
}
}
func write(content: String) {
fileHandle.seekToEndOfFile()
guard let data = content.data(using: String.Encoding.ascii) else {
fatalError("Could not convert \(content) to ascii data!")
}
fileHandle.write(data)
}
}
var imageWidth = 480
var imageHeight = 270
var sampleCount = 16
var bounceCount = 3
let device = MTLCreateSystemDefaultDevice()!
let library = try! device.makeDefaultLibrary(bundle: Bundle.module)
let primaryRayFunc = library.makeFunction(name: "ray_trace")!
let pipeline = try! device.makeComputePipelineState(function: primaryRayFunc)
var pixelData: [Float4] = (0..<(imageWidth * imageHeight)).map{ _ in Float4(0, 0, 0, 0)}
var pixelCount = UInt(pixelData.count)
let pixelDataBuffer = device.makeBuffer(bytes: &pixelData, length: Int(pixelCount) * MemoryLayout<Float4>.stride, options: [])!
let pixelDataMirrorPointer = pixelDataBuffer.contents().bindMemory(to: Float4.self, capacity: Int(pixelCount))
let pixelDataMirrorBuffer = UnsafeBufferPointer(start: pixelDataMirrorPointer, count: Int(pixelCount))
let commandQueue = device.makeCommandQueue()!
let commandBufferDescriptor = MTLCommandBufferDescriptor()
commandBufferDescriptor.errorOptions = MTLCommandBufferErrorOption.encoderExecutionStatus
let commandBuffer = commandQueue.makeCommandBuffer(descriptor: commandBufferDescriptor)!
let commandEncoder = commandBuffer.makeComputeCommandEncoder()!
commandEncoder.setComputePipelineState(pipeline)
commandEncoder.setBuffer(pixelDataBuffer, offset: 0, index: 0)
commandEncoder.setBytes(&pixelCount, length: MemoryLayout<Int>.stride, index: 1)
commandEncoder.setBytes(&imageWidth, length: MemoryLayout<Int>.stride, index: 2)
commandEncoder.setBytes(&imageHeight, length: MemoryLayout<Int>.stride, index: 3)
commandEncoder.setBytes(&sampleCount, length: MemoryLayout<Int>.stride, index: 4)
commandEncoder.setBytes(&bounceCount, length: MemoryLayout<Int>.stride, index: 5)
// We have to calculate the sum `pixelCount` times
// => amount of threadgroups is `resultsCount` / `threadExecutionWidth` (rounded up)
// because each threadgroup will process `threadExecutionWidth` threads
let threadExecutionWidth = pipeline.threadExecutionWidth;
let threadgroupsPerGrid = MTLSize(width: (Int(pixelCount) + threadExecutionWidth - 1) / threadExecutionWidth, height: 1, depth: 1)
// Here we set that each threadgroup should process `threadExecutionWidth` threads
// the only important thing for performance is that this number is a multiple of
// `threadExecutionWidth` (here 1 times)
let threadsPerThreadgroup = MTLSize(width: threadExecutionWidth, height: 1, depth: 1)
commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
commandEncoder.endEncoding()
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
if let error = commandBuffer.error as NSError? {
if let encoderInfo = error.userInfo[MTLCommandBufferEncoderInfoErrorKey] as? [MTLCommandBufferEncoderInfo] {
for info in encoderInfo {
print(info.label + info.debugSignposts.joined())
}
}
}
let sfw = SimpleFileWriter(filePath: "/Users/pprovins/Desktop/render.ppm")
sfw.write(content: "P3\n")
sfw.write(content: "\(imageWidth) \(imageHeight)\n")
sfw.write(content: "255\n")
for pixel in pixelDataMirrorBuffer {
sfw.write(content: "\(UInt8(pixel.x * 255)) \(UInt8(pixel.y * 255)) \(UInt8(pixel.z * 255)) ")
}
sfw.write(content: "\n")
Additionally, here is the shader being ran. I have not included all function definition for brevity's sake:
kernel void ray_trace(device float4 *result [[ buffer(0) ]],
const device uint& dataLength [[ buffer(1) ]],
const device int& imageWidth [[ buffer(2) ]],
const device int& imageHeight [[ buffer(3) ]],
const device int& samplesPerPixel [[ buffer(4) ]],
const device int& rayBounces [[ buffer (5)]],
const uint index [[thread_position_in_grid]]) {
if (index >= dataLength) {
return;
}
const float3 origin = float3(0.0);
const float aspect = float(imageWidth) / float(imageHeight);
const float3 vph = float3(0.0, 2.0, 0.0);
const float3 vpw = float3(2.0 * aspect, 0.0, 0.0);
const float3 llc = float3(-(vph / 2.0) - (vpw / 2.0) - float3(0.0, 0.0, 1.0));
float3 accumulatedColor = float3(0.0);
thread float seed = getSeed(index, index % imageWidth, index / imageWidth);
float row = float(index / imageWidth);
float col = float(index % imageWidth);
for (int aai = 0; aai < samplesPerPixel; ++aai) {
float ranX = fract(rand(seed));
float ranY = fract(rand(seed));
float u = (col + ranX) / float(imageWidth - 1);
float v = 1.0 - (row + ranY) / float(imageHeight - 1);
Ray r(origin, llc + u * vpw + v * vph - origin);
float3 color = float3(0.0);
HitRecord hr = {0.0, 0.0, false};
float attenuation = 1.0;
for (int bounceIndex = 0; bounceIndex < rayBounces; ++bounceIndex) {
testForHit(sceneDistance, r, hr);
if (hr.h) {
float3 target = hr.p + hr.n + random_f3_in_unit_sphere(seed);
attenuation *= 0.5;
r = Ray(hr.p, target - hr.p);
} else {
color = default_atmosphere_color(r) * attenuation;
break;
}
}
accumulatedColor += color / samplesPerPixel;
}
result[index] = float4(sqrt(accumulatedColor), 1.0);
}
Oddly enough, it occasionally shall run. Changing the number of samples to 16 or above will always results in the mention IOAF code. Less than 16 samples, the code will run ~25% of the time. The more samples, the more likely it is to results in the error code.
Is there anyway to get additional on IOAF code 2067?
Determining the error code with Metal API + Shader Validation was not possible.
By testing individual portions of the kernel, the particular error was narrowed down to a while loop that caused the GPU to hang.
The problem can essentially be boiled down to code that looks like:
while(true) {
// ad infinitum
}
or, in the case of the code above in the call to random_f3_in_unit_sphere(seed):
while(randNum(seed) < threshold) {
// the while loop is not "bounded"
// in any sense. Whoops.
++seed;
}
I have a trail on a map that the user can "follow" by mousing over a graph (time and speed). If the user zooms in a lot, part of the trail may not be visible. When the user wants to see the part of the trail that is not showing I use the panTo method...
The panTo method of leaflet is currently also centering. I don't want to center, I want the map to move just enough to show a point. (The problem with panTo is it causes excessive map scrolling and a harsh user experience.)
I have tried changing the bounds, but that has an (unwanted) side affect of sometimes zooming out.
Any way I can do a "minimal" panTo?
This is a (working but unpolished) solution; map is our own map wrapper utility class, lmap is a leaflet map object in typescript, and toxy() is a method to convert lat/longs to x/y values.
if (!this.lmap.getBounds().contains(latlng)) {
let target = this.map.toxy(latlng);
let nw = this.map.toxy(this.lmap.getBounds().getNorthWest());
let se = this.map.toxy(this.lmap.getBounds().getSouthEast());
let x = 0, y = 0;
let margin = 75;
if (target.y < nw.y)
y = (-1 * (nw.y - target.y)) - margin;
else if (target.y > se.y)
y = (target.y - se.y) + margin;
if (target.x < nw.x)
x = (-1 * (nw.x - target.x)) - margin;
else if (target.x > se.x)
x = (target.x - se.x) + margin;
this.lmap.panBy(new L.Point(x, y));
}
First, fetch the bounds of the map (measured in pixels from the CRS origin) with map.getPixelBounds(). Then, use map.project(latlng, map.getZoom()) to get the coordinates (in pixels from the CRS origin) of the point you're interested.
If you're confused about this "pixels from the CRS origin" thing, read the "Pixel Origin" section at http://leafletjs.com/examples/extending/extending-2-layers.html .
Once you have these pixel coordinates, it should be a simple matter of checking whether the point is inside the viewport, and if not, how far away on each direction it is.
http://jsfiddle.net/jcollin6/b131tobj/2/
Should give you what you want
function clamp(n,lower,upper) {
return Math.max(lower, Math.min(upper, n));
}
//Shamelessly stolen from https://gist.github.com/dwtkns/d5b9b60285b8b0067c53
function getNearestPointInPerimeter(l,t,w,h,x,y) {
var r = l+w,
b = t+h;
var x = clamp(x,l,r),
y = clamp(y,t,b);
var dl = Math.abs(x-l),
dr = Math.abs(x-r),
dt = Math.abs(y-t),
db = Math.abs(y-b);
var m = Math.min(dl,dr,dt,db);
return (m===dt) ? {x: x, y: t} :
(m===db) ? {x: x, y: b} :
(m===dl) ? {x: l, y: y} : {x: r, y: y};
}
L.ExtendedMap = L.Map.extend({
panInside: function (latlng, pad, options) {
var padding = pad ? pad : 0;
var pixelBounds = this.getPixelBounds();
var center = this.getCenter();
var pixelCenter = this.project(center);
var pixelPoint = this.project(latlng);
var sw = pixelBounds.getBottomLeft();
var ne = pixelBounds.getTopRight();
var topLeftPoint = L.point(sw.x + padding, ne.y + padding);
var bottomRightPoint = L.point( ne.x - padding, sw.y - padding);
var paddedBounds = L.bounds(topLeftPoint, bottomRightPoint);
if (!paddedBounds.contains(pixelPoint)) {
this._enforcingBounds = true;
var nearestPoint = getNearestPointInPerimeter(
sw.x + padding,
ne.y + padding,
ne.x - sw.x - padding * 2,
sw.y - ne.y - padding * 2,
pixelPoint.x,
pixelPoint.y
);
var nearestPixelPoint = L.point(nearestPoint.x,nearestPoint.y)
var diffPixelPoint = nearestPixelPoint.subtract(pixelPoint);
var newPixelCenter = pixelCenter.subtract(diffPixelPoint);
var newCenter = this.unproject(newPixelCenter);
if (!center.equals(newCenter)) {
this.panTo(newCenter, options);
}
this._enforcingBounds = false;
}
return this;
}
});
Use this way
map.panTo([lat, lng]);
map.setZoom(Zoom);
When i give two nodes in my game a physics body, they stop doing what i want them to do. I have two bars at the bottom of the scene,one starts on the left, and the other is on the right, and they continuously go to meet at the middle then move back to there starting spot, like a floor opening up. Without a physics body, they do exactly this, but when i give them a physics body,when they both meet in the middle, they stop there and don't move back out.
Here are the bars:
bar.position = CGPoint(x:-self.size.width * 0.2, y:(self.size.height * 0.03));
bar.physicsBody = SKPhysicsBody(texture: text, size: text.size())
bar.physicsBody?.categoryBitMask = PhysicsCategory.bar
bar.physicsBody?.contactTestBitMask = PhysicsCategory.ball
bar.physicsBody?.collisionBitMask = PhysicsCategory.bar
self.addChild(bar)
bar1.position = CGPoint(x:self.size.width * 1.2, y:(self.size.height * 0.03));
bar1.physicsBody = SKPhysicsBody(texture: text, size: text.size())
bar1.physicsBody?.categoryBitMask = PhysicsCategory.bar
bar1.physicsBody?.contactTestBitMask = PhysicsCategory.ball
bar1.physicsBody?.collisionBitMask = PhysicsCategory.bar
self.addChild(bar1)
And this is how they move. This is in the update method:
if (bar.position.x == -self.size.width * 0.2)
{
let move = SKAction.moveToX(self.size.width * 0.012, duration: 1);
bar.runAction(move);
} else if (bar.position.x == self.size.width * 0.012) {
let move = SKAction.moveToX(-self.size.width * 0.2, duration: 1)
bar.runAction(move)
}
if (bar1.position.x == self.size.width * 1.2)
{
let move = SKAction.moveToX(self.size.width * 0.988, duration: 1);
bar1.runAction(move);
} else if (bar1.position.x == self.size.width * 0.988) {
let move = SKAction.moveToX(self.size.width * 1.2, duration: 1)
bar1.runAction(move)
}
What could be causing them to stick together at the middle when i give them a physics body.