SwiftUI: Absolute positioning of text on screen - text

Am updating a compass view from UIKit to SwiftUI. The original looks like this:
But haven't worked out how to display the text, currently nothing:
This is the code that I am using:
struct DisplayDirections: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let outerRadius = 134.0
var farX = 0.0
var farY = 0.0
let offset = 16.0
var degree = 0.0
var radians = 0.0
for result in ["S", "SW", "W", "NW", "N", "NE", "E", "SE"] {
radians = (CGFloat(degree) * .pi / 180.0)
farX = outerRadius + cos(radians) * CGFloat(outerRadius) + offset
farY = outerRadius + sin(radians) * CGFloat(outerRadius) + offset
Text(result)
.font(.title)
.foregroundColor(.black)
.background(.red)
.position(x: farX, y: farY)
.rotationEffect(.degrees(degree))
.fixedSize()
degree = degree + 45.0
print(result) // Just to get some output so I know the loop is working
}
return path
}
}
What am I doing wrong?

Working, all thanks to Lorem Ipsum's hints. This code has to live in the main Stack and can't be called from Structs like the rest of the lines and circles that make up the clock face. The NSEW literals are put on with a similar construct to the degrees part which are listed below.
// Draw Degrees
ForEach(Array(stride(from: 0, to: 360, by: 10)), id: \.self) { index in
let degree: Double = Double.pi * 2 / Double(360)
let itemDegree = degree * Double(index)
VStack {
Text(String(index))
.font(.system(size: 8))
.foregroundColor(.black)
Spacer()
}
.rotationEffect(.radians(itemDegree))
}
.frame(width: 288, height: 288)

Related

how to send value more than 255 in texture

I have custom shader where i have to send a segmentation id associated to each pixel in (x,y) position.
Now i am sending that as a texture and getting that data as:
float segID = sampleTexture(persTexture, vPosition.xy).r;
and the segmentation id is encoded as "r" in a texture but the problem here is that since the type is float32, if in any case i have segmentation id more than 255, i can't send that with texture because i can't put this data in texture.
Is there any way i can solve this?
can i have storage buffer or any hack to this?
var uniforms = {
// some other uniforms
persTexture: { type: 't', value: new THREE.Texture() },
//some other uniforms
}
and data is put as:
var imageData = new Uint8Array(4 * persDatas[threshold].length)
for (var x = 0; x < persDatas[threshold].length; x++) {
var segID = Math.floor((255 * persDatas[threshold][x]) / response.data.max)
imageData[x * 4] = segID
imageData[x * 4 + 1] = segID
imageData[x * 4 + 2] = segID
imageData[x * 4 + 3] = 255
}
var texture = new THREE.DataTexture(imageData, 4104, 1856)
texture.needsUpdate = true
persTextures[threshold] = texture
uniforms.persTexture.value = texture
can anyone help on this please??

Processing: how to make box() appear solid (non-transparent) in 3d mode

I'm trying to create layers of 3d boxes in Processing. I want them to appear solid, so that you can't see the boxes "behind" other boxes, but the way they're displaying makes them seem transparent; you can see the stroke of boxes behind other boxes. How do I make them appear solid?
// number of boxes
int numBox = 300;
// width of each box
int boxWidth = 30;
// number of boxes per row
float numPerRow;
void setup() {
size(800, 800, P3D);
pixelDensity(1);
colorMode(HSB, 360, 100, 100, 100);
background(40, 6, 85);
stroke(216, 0, 55);
smooth(4);
fill(0, 0, 90, 100);
numPerRow = width / boxWidth;
}
void draw() {
background(40, 6, 85);
translate((boxWidth / 2), 100);
rotateX(-PI/6);
rotateY(PI/8);
for (int i = 0; i < numBox; i++) {
drawBox(i);
if (i == numBox - 1) {
noLoop();
}
}
}
void drawBox(int i) {
if ((i % 2) == 0) {
pushMatrix();
translate(((boxWidth / 2) * i) % width, 20 * floor(i / (2 * numPerRow)));
translate(0, -((i % 30) / 2));
box(boxWidth, i % 30, boxWidth);
popMatrix();
};
}
Close-up of how the boxes are being displayed:
The issue is that the boxes are intersecting and the strokes of these intersecting boxes are what give the appearance of "see through".
I'm noticing you are using x and y translation, but not z.
If you don't plan to increase x, y spacing to avoid intersections, you can easily offset rows on the z axis so rows of boxes appear in front of each other.
Here's a slightly modified version of your code illustrating this idea:
// number of boxes
int numBox = 300;
// width of each box
int boxWidth = 30;
// number of boxes per row
float numPerRow;
void setup() {
size(800, 800, P3D);
pixelDensity(1);
colorMode(HSB, 360, 100, 100, 100);
background(40, 6, 85);
stroke(216, 0, 55);
smooth(4);
fill(0, 0, 90, 100);
numPerRow = width / boxWidth;
}
void draw() {
background(40, 6, 85);
translate((boxWidth / 2), 100);
if(mousePressed){
rotateX(map(mouseY, 0, height, -PI, PI));
rotateY(map(mouseX, 0, width, PI, -PI));
}else{
rotateX(-PI/6);
rotateY(PI/8);
}
for (int i = 0; i < numBox; i++) {
drawBox(i);
//if (i == numBox - 1) {
// noLoop();
//}
}
}
void drawBox(int i) {
if ((i % 2) == 0) {
pushMatrix();
float x = ((boxWidth / 2) * i) % width;
float y = 20 * floor(i / (2 * numPerRow));
float z = y * 1.5;
translate(x, y, z);
translate(0, -((i % 30) / 2));
box(boxWidth, i % 30, boxWidth);
popMatrix();
};
}
(Click and drag to rotate and observe the z offset.
Feel free to make z as interestersting as you need it it.
Nice composition and colours!
(framing (window size) could use some iteration/tweaking, but I'm guessing this is WIP))

How to connect 4 points on a plane so they do not fold on themselves(they always create a Quadrilateral)

I have created a small Raphael app to showcase my struggle.
I created four handles which can be moved. A 'sheet' is covering the entire screen except for the square between the 4 handles.
Whenever the handles are dragged the sheet is placed accordingly.
What ends up happening is that in certain situations, the sheet folds on itself.
It's best if you just see the fiddle. You'll get what I'm talking about
http://jsfiddle.net/8qtffq0s/
How can I avoid this?
Notice: The screen is white. The black part is the sheet, and the white part is a gap in the sheet and not the other way around.
//raphael object
var paper = Raphael(0, 0, 600, 600)
//create 4 handles
h1 = paper.circle(50, 50, 10).attr("fill","green")
h2 = paper.circle(300, 50, 10).attr("fill", "blue")
h3 = paper.circle(300, 300, 10).attr("fill", "yellow")
h4 = paper.circle(50, 300, 10).attr("fill", "red")
//create covering sheet
path = ["M", 0, 0, "L", 600, 0, 600, 600, 0, 600, 'z', "M", h1.attrs.cx, h1.attrs.cy,"L", h4.attrs.cx, h4.attrs.cy, h3.attrs.cx, h3.attrs.cy, h2.attrs.cx, h2.attrs.cy,'z']
sheet = paper.path(path).attr({ "fill": "black", "stroke": "white" }).toBack()
//keep starting position of each handle on dragStart
var startX,startY
function getPos(handle) {
startX= handle.attrs.cx
startY = handle.attrs.cy
}
//Redraw the sheet to match the new handle placing
function reDrawSheet() {
path = ["M", 0, 0, "L", 600, 0, 600, 600, 0, 600, 'z', "M", h1.attrs.cx, h1.attrs.cy, "L", h4.attrs.cx, h4.attrs.cy, h3.attrs.cx, h3.attrs.cy, h2.attrs.cx, h2.attrs.cy, 'z']
sheet.attr("path",path)
}
//enable handle dragging
h1.drag(function (dx, dy) {
this.attr("cx", startX + dx)
this.attr("cy", startY + dy)
reDrawSheet()
},
function () {
getPos(this)
})
h2.drag(function (dx, dy) {
this.attr("cx", startX + dx)
this.attr("cy", startY + dy)
reDrawSheet()
},
function () {
getPos(this)
})
h3.drag(function (dx, dy) {
this.attr("cx", startX + dx)
this.attr("cy", startY + dy)
reDrawSheet()
},
function () {
getPos(this)
})
h4.drag(function (dx, dy) {
this.attr("cx", startX + dx)
this.attr("cy", startY + dy)
reDrawSheet()
},
function () {
getPos(this)
})
Update: I improved the function "reDrawSheet" so now it can classify the points on the strings as top left, bottom left, bottom right, and top right
This solved many of my problems, but in some cases the sheet still folds on it self.
new fiddle: http://jsfiddle.net/1kj06co4/
new code:
function reDrawSheet() {
//c stands for coordinates
c = [{ x: h1.attrs.cx, y: h1.attrs.cy }, { x: h4.attrs.cx, y: h4.attrs.cy }, { x: h3.attrs.cx, y: h3.attrs.cy }, { x: h2.attrs.cx, y: h2.attrs.cy }]
//arrange the 4 points by height
c.sort(function (a, b) {
return a.y - b.y
})
//keep top 2 points
cTop = [c[0], c[1]]
//arrange them from left to right
cTop.sort(function (a, b) {
return a.x - b.x
})
//keep bottom 2 points
cBottom = [c[2], c[3]]
//arrange them from left to right
cBottom.sort(function (a, b) {
return a.x - b.x
})
//top left most point
tl = cTop[0]
//bottom left most point
bl = cBottom[0]
//top right most point
tr = cTop[1]
//bottom right most point
br = cBottom[1]
path = ["M", 0, 0, "L", 600, 0, 600, 600, 0, 600, 'z', "M", tl.x,tl.y, "L", bl.x,bl.y, br.x,br.y, tr.x,tr.y, 'z']
sheet.attr("path",path)
}
To make things super clear, this is what I'm trying to avoid:
Update 2:
I was able to avoid the vertices from crossing by checking which path out of the three possible paths is the shortest and choosing it.
To do so, I added a function that checks the distance between two points
function distance(a, b) {
return Math.sqrt(Math.pow(b.x - a.x, 2) + (Math.pow(b.y - a.y, 2)))
}
And altered the code like so:
function reDrawSheet() {
//c stands for coordinates
c = [{ x: h1.attrs.cx, y: h1.attrs.cy }, { x: h4.attrs.cx, y: h4.attrs.cy }, { x: h3.attrs.cx, y: h3.attrs.cy }, { x: h2.attrs.cx, y: h2.attrs.cy }]
//d stands for distance
d=distance
//get the distance of all possible paths
d1 = d(c[0], c[1]) + d(c[1], c[2]) + d(c[2], c[3]) + d(c[3], c[0])
d2 = d(c[0], c[2]) + d(c[2], c[3]) + d(c[3], c[1]) + d(c[1], c[0])
d3 = d(c[0], c[2]) + d(c[2], c[1]) + d(c[1], c[3]) + d(c[3], c[0])
//choose the shortest distance
if (d1 <= Math.min(d2, d3)) {
tl = c[0]
bl = c[1]
br = c[2]
tr = c[3]
}
else if (d2 <= Math.min(d1, d3)) {
tl = c[0]
bl = c[2]
br = c[3]
tr = c[1]
}
else if (d3 <= Math.min(d1, d2)) {
tl = c[0]
bl = c[2]
br = c[1]
tr = c[3]
}
path = ["M", 0, 0, "L", 600, 0, 600, 600, 0, 600, 'z', "M", tl.x,tl.y, "L", bl.x,bl.y, br.x,br.y, tr.x,tr.y, 'z']
sheet.attr("path",path)
}
Now the line does not cross itself like the image I attached about, but the sheet "flips" so everything turns black.
You can see the path is drawn correctly to connect the for points by the white stroke, but it does not leave a gap
new fiddle: http://jsfiddle.net/1kj06co4/1/
Picture of problem:
So... the trouble is to tell the inside from the outside.
You need the following functions:
function sub(a, b) {
return { x: a.x - b.x , y: a.y - b.y };
}
function neg(a) {
return { x: -a.x , y: -a.y };
}
function cross_prod(a, b) {
// 2D vecs, so z==0.
// Therefore, x and y components are 0.
// Return the only important result, z.
return (a.x*b.y - a.y*b.x);
}
And then you need to do the following once you've found tl,tr,br, and bl:
tlr = sub(tr,tl);
tbl = sub(bl,tl);
brl = sub(bl,br);
btr = sub(tr,br);
cropTL = cross_prod( tbl, tlr );
cropTR = cross_prod(neg(tlr),neg(btr));
cropBR = cross_prod( btr, brl );
cropBL = cross_prod(neg(brl),neg(tbl));
cwTL = cropTL > 0;
cwTR = cropTR > 0;
cwBR = cropBR > 0;
cwBL = cropBL > 0;
if (cwTL) {
tmp = tr;
tr = bl;
bl = tmp;
}
if (cwTR == cwBR && cwBR == cwBL && cwTR!= cwTL) {
tmp = tr;
tr = bl;
bl = tmp;
}
My version of the fiddle is here. :) http://jsfiddle.net/1kj06co4/39/

Cant able to drag set of elements

I am creating two elements (1. arrow shape and 2. dotted line) using path (Raphael and SVG) and I want to drag these two together but I am only able to drag it independently. Here is my code for this:
gaugeSvg = Raphael("gauge");
$(document).ready( function () {
redraw();
});
function redraw() {
//Add a Arrow and line
var rect = gaugeSvg.path('M 0 0 L 40 -34 L 40 -14 L 80 -14 L 80 14 L 40 14 L 40 34 Z');
rect.attr({
"stroke": "black",
"fill" : "black",
"enable" : "true",
}).translate(left + width, goalY);
var txt = gaugeSvg.path('M 0 0 L ' + width + " 0");
txt.attr({
"stroke": "black",
"stroke-width": 12,
"stroke-dasharray": "-",
"stroke-linecap": "round"
}).translate(left, goalY);
//Create a set so we can move the
//arrow and line at the same time
var g = gaugeSvg.set();
g.push(rect, txt);
// var g = gaugeSvg.set(rect, txt);
var me = this,
lx = 0,
ly = 0,
ox = 0,
oy = 0,
moveFnc = function(dx, dy) {
this.translate(dx-ox, dy-oy);
ox = dx;
oy = dy;
},
startFnc = function() {},
endFnc = function() {
ox = lx;
oy = ly;
};
g.drag(moveFnc, startFnc, endFnc);
}
I have'nt used Rapheal . But i have achieved this Drag functionlaity with SVG amd Javascript.
In order to move multiple elements, you need to group them. Means put these elements in 'g' group and then apply drag function on this 'g'. and once finished, you need to 'ungroup' them.
you can see demo here http://jsfiddle.net/rehankhalid/5t5pX/
var mainsvg = document.getElementsByTagName('svg')[0];
function mousemove(event) {
var svgXY = getSvgCordinates(event);// get current x,y w.r.t to your svg.
dx = svgXY.x - mx;// mx means x cordinates of mouse down
dy = svgXY.y - my;
draggroup.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');
}
function getSvgCordinates(event) {
var m = mainsvg.getScreenCTM();
var p = mainsvg.createSVGPoint();
var x, y;
x = event.pageX;
y = event.pageY;
p.x = x;
p.y = y;
p = p.matrixTransform(m.inverse());
x = p.x;
y = p.y;
x = parseFloat(x.toFixed(3));
y = parseFloat(y.toFixed(3));
return {x: x, y: y};
}

How can I recreate svg <line> elements using d34raphael?

I know raphael can create paths, but not lines. I know d3 can create both.
I would like to create a box and whisker chart, similar to this one, but horizontal instead of vertical. I have json data in the form:
{
"lowestValue":"53",
"lowerQuartile":"63",
"medianValue":"73",
"upperQuartile":"80",
"highestValue":"99",
"targetValue":"80"
},
...
How can I create a (or several) box and whisker plot(s) with d34raphael or with pure raphael, so that it will display properly in IE7/IE8?
Here is a picture of the end goal:
The path is such a similar primitive that it seems like it would be easy to recreate such a graph using raw Raphael (which seems increasingly to be my preference these days). Consider such a utility function as this:
function whisker( paper, x, y, width, height, data )
{
var x1 = x + data.lowestValue * width / 100, x2 = x + data.highestValue * width / 100;
var outer_range = paper.path( [ "M", x1, y + height * 0.25, "L", x1, y + height * 0.75, "M", x1, y + height / 2, "L", x2, y + height / 2, "M", x2, y + height / 4, "L", x2, y + height * 0.75 ] ).attr( { fill : 'none', stroke: 'gray' } );
var inner_range = paper.rect( x + ( width * data.lowerQuartile / 100 ), y, width * ( data.upperQuartile - data.lowerQuartile ) / 100, height, 0 ).attr( { fill: 'lightgray', stroke: 'black' } );
var median = paper.path( [ "M", x + width * data.medianValue / 100, y, "L", x + width * data.medianValue / 100, y + height ] ).attr( { fill: 'none', stroke: 'black' } );;
var target = paper.circle( x + ( width * data.targetValue / 100 ), y + height / 2, height / 4 ).attr( { fill: 'black' } );
}
The sixth parameter is simply your json data. You would need to increment the y value for each whisker, of course. Here's the code in action on my website.

Resources