circle inside a square in Smalltalk - geometry

I draw a circle inside a square in Smalltalk
But I want to reduce it size by 10% so i tried to write it like that:
initialize
| circleWidth circleHeight|
super initialize.
self label: ''.
self borderWidth: 0.
bounds := 0#0 corner: 70#70.
circle := CircleMorph new.
circleWidth := self bounds width*0.9.
circleHeight := self bounds height*0.9.
circle bounds: self bounds*0.9.
circle color: Color paleBuff.
circle borderWidth: 1.
self addMorphCentered: circle.
offColor := Color gray darker.
onColor := Color gray darker.
self useSquareCorners.
self turnOff
But the line:
circle bounds: self bounds*0.9.
has some problem when compiling "Message not understood rectangle >>*"
how can I do it?

Use the message scaleBy: scale to scale a rectangle.
So
circle bounds: self bounds*0.9.
becomes:
circle bounds: (self bounds scaleBy: 0.9).

Related

How to perform smoothstep antialiasing with rounded rectangles?

I'm drawing a rounded rectangle with a GLSL shader using the following pixel shader:
float roundedBoxSDF(vec2 center, vec2 size, float radius) {
return length(max(abs(center) - size + radius, 0.0)) - radius;
}
void main() {
float edgeSoftness = 1.0;
float distance = roundedBoxSDF(v_pos.xy - v_loc - (v_size / 2.0), v_size / 2.0, v_border_radius);
float smoothing = smoothstep(0.0, edgeSoftness * 2.0, distance);
outputColor = mix(vec4(0), v_color, 1.0 - smoothing);
}
and the corner looks like this at the moment:
Notice the black streak. I think it has to do with the vec4(0) in the final line, because if I change that to vec4(1, 1, 1, 0), then I get the following result:
Even more unsightly. What am I doing wrong? If I change edgeSoftness to 0.0, then it looks decent:
But it doesn't have any anti-aliasing. Is there any way to have an anti-aliased rounded rectangle without the unsightly edges? I'm not sure what I'm doing wrong.
The issue you see is because you are interpolating from solid base color to transparent black. Now, for the pixels in-between the interpolation, that are not yet fully transparent, the base color will still be slightly shifted towards black and you will get the results you see. What you likely want to do is keep the RGB values for the base color, and only change the alpha channel.
outputColor = vec4(v_color.rgb, mix(v_color.a, 0., smoothing));

How do I convert screen space to world space coords in Bevy using Camera2dComponents?

Using Res<Events<CursorMoved>> I can get the mouse position change in screen space coordinates (zero in bottom-left corner), e.g.
#[derive(Default)]
struct State {
cursor_moved_reader: EventReader<CursorMoved>,
}
fn set_mouse_pos(mut state: ResMut<State>, cursor_moved: Res<Events<CursorMoved>>) {
for event in state.cursor_moved_reader.iter(&cursor_moved) {
println!("cursor: {:?}", event.position);
}
}
The question now is if I set SpriteComponents' transform.translation to the cursor position when using a camera from Camera2dComponents::default() a sprite with position 0, 0 is rendered in the center of the screen. What's the idiomatic way of converting between the screen space mouse coords and the camera world space coords?
The previous answer seems to be on the right track, however the transformation is not fully implemented and actually overengineered. Stumbled on the same problem and figured that's what the camera transform is for! This works for me:
fn window_to_world(
position: Vec2,
window: &Window,
camera: &Transform,
) -> Vec3 {
// Center in screen space
let norm = Vec3::new(
position.x - window.width() / 2.,
position.y - window.height() / 2.,
0.,
);
// Apply camera transform
*camera * norm
// Alternatively:
//camera.mul_vec3(norm)
}
I had a very similar confusion when I first started hacking with bevy. Here's what I am currently using to translate from window to world coordinates (for a 2D game):
fn window_to_world(
window: &Window,
camera: &Transform,
position: &Vec2,
) -> Vec3 {
let center = camera.translation.truncate();
let half_width = (window.width() / 2.0) * camera.scale.x;
let half_height = (window.height() / 2.0) * camera.scale.y;
let left = center.x - half_width;
let bottom = center.y - half_height;
Vec3::new(
left + position.x * camera.scale.x,
bottom + position.y * camera.scale.y,
0.0, // I'm working in 2D
)
}
In bevy, the Window coordinate system has the origin (0, 0) at the bottom left, measured in pixels, and increasing as you go up the screen. The Camera's coordinates default to (0, 0) in the center of the window, measured in whichever unit makes the most sense for your game.
This function is limited because it doesn't take rotation of the camera into account, only the scale.

Calculating points of a quadrilateral to emulate fluid in a container (in Swift)

I am trying to create a FluidView shape in SwiftUI which acts like a fluid in a container, such that when the device is at a particular angle, so too is the shape / 'fluid'. The shape also has a specific capacity, percentFilled, which indicates how much of the parent's view should be filled.
Using these constraints, the invariant for the class is
lines.area == rect.area * percentFilled
where lines is the quadrilateral and rect is the bounding rectangle. This invariant implies that the 'volume' of the shape remains constant for each percentFilled irrespective of the tilt angle.
Here is what I have so far:
/// A View made using a specified angle and amount to fill
/// - Invariant: The area of the view is exactly equal to the area of the rectangle of the parent view times `percentFilled`
struct FluidView: Shape {
var angle: CGFloat = 0.0
var percentFilled: CGFloat = 0
/// Creates a new FluidView
/// - Parameters:
/// - angle: A value in the range `0...1`. A value of `0` indicates the view is horizontal, and an angle of `1` indicates the view is vertical (horizontal if viewed as landscape)
/// - percentFilled: the amount of the view bounds to fill represented as a value in the range `0...1`. A value of `x` indicates that `x * 100`% of the parent view is covered by this view
init(angle: CGFloat = 0, percentFilled: CGFloat = 0) {
precondition(0...1 ~= angle)
precondition(0...1 ~= percentFilled)
self.angle = angle
self.percentFilled = percentFilled
}
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: rect.height * (1 - percentFilled))) // top left
let lines = [
(0, rect.height ), // bottom left
(rect.width * 1 / (1 + angle - percentFilled), rect.height ), // bottom right
(rect.width * 1 / (1 + angle - percentFilled), rect.height * (1 + angle - percentFilled)), // top right
(0, rect.height * (1 - angle - percentFilled)) // top left
].map { x, y in
// make sure no points exceed the bounds
CGPoint(x: min(rect.width, x), y: min(rect.height, y))
}
// invariant
assert(lines.area == rect.area * percentFilled)
path.addLines(lines)
return path
}
}
I feel like what I have currently is somewhat close to the goal, however the invariant fails. I believe that my y-coordinates are correct, however I think my calculations for the x-coordinates have to change, but I'm not sure to what they should change.
Any help would be really appreciated, thanks!
Try something like this,
struct FilledShape<S: Shape>: View {
let shape: S
#State var angle: Angle = .zero
#State var percentFull: CGFloat
var gradient: Gradient {
Gradient(stops: [Gradient.Stop(color: .red, location: 0),
Gradient.Stop(color: .red, location: percentFull),
Gradient.Stop(color: .blue, location: percentFull),
Gradient.Stop(color: .blue, location: 1)])
}
var body: some View {
shape.rotation(angle)
.fill(LinearGradient(gradient: gradient, startPoint: .bottom, endPoint: .top))
}
}
struct ContentView: View {
#State var angle: Angle = .degrees(30)
var body: some View {
FilledShape(shape: Rectangle(), angle: angle, percentFull: 0.3).frame(width: 100, height: 100)
}
}
Thing is, percent full is really the percent up along the y axis, not the percent of the area filled. You could use some kind of numeric method with GeometryReader to get the area and read the y-value at the appropriate filled area sum (or if you just use quadrilaterals it's easier). By brute force:
extension Shape {
func area(in box: CGRect) -> Int {
var area = 0
for x in 0..<Int(box.width) {
for y in 0..<Int(box.height) {
let point = CGPoint(x: x, y: y)
if self.contains(point) {
area += 1
}
}
}
return area
}
}
As a different approach, look into SpriteKit and SKPhysicsBody.

Draw a circle divided in two by chord

I want to create an svg like that:
So, a circumference divided into two parts by a chord. The two sections must have different colors.
I want to draw this object in SVG. I think I need to use the PATH tag, right? Or is there another way to do it?
What points do I need to draw the object? I'm a bit confused..
Yes. It is a <path> element that you will need.
Paths always start with an M (move) command. You'll also need an A (arc) command, and probably an L line command for the line that bisects the circle.
For the arc command, you just need to know the X and Y coordinates of the start and end points (B and C). Plus the radius of the circle. It is important to have accurate coordinates for the start and end points of an arc command. Small discrepancies can cause the position of the circle to move around quite a bit.
In the following demo, I have chosen to calculate the B and C positions based on their angle from the centre. Plus setting the path description attribute from code, allows me to document for you what each of the parameters are for.
// Radius of the circle
var radius = 80;
// Centre coordinate of the circle
var Ox = 100;
var Oy = 100;
// Angles of each point from which we calculate their X and Y coordinates.
// Here, 0 degrees is East, and angle increases in a clockwise direction.
var angleB = 285; // degrees
var angleC = 35;
var B = angleToCoords(angleB, Ox, Oy, radius);
var C = angleToCoords(angleC, Ox, Oy, radius);
// Get the "major segment" path element
var majorPath = document.getElementById("major");
// Set the path description for the "major segment"
majorPath.setAttribute("d", ['M', B.x, B.y, // Move to point B
'L', C.x, C.y, // Line to point C
'A', radius, radius, // X radius and Y radius of the arc
0, // ellipse angle
1, // large arc flag (1 indicates we want the larger of the two possible arcs between the points
1, // clockwise direction flag
B.x, B.y, // arc end point is back at point B
'Z'].join(" ")); // Z command closes the path
// Get the "minor segment" path element
var minorPath = document.getElementById("minor");
// Set the path description for the "minor segment"
minorPath.setAttribute("d", ['M', B.x, B.y, // Move to point B
'A', radius, radius, // X radius and Y radius of the arc
0, // ellipse angle
0, // large arc flag (0 indicates we want the smaller of the two possible arcs between the points
1, // clockwise direction flag
C.x, C.y, // arc end point is at point C
'L', B.x, B.y, // Line to point B
'Z'].join(" ")); // Z command closes the path
// Function to convert from an angle to an X and Y position
function angleToCoords(angleInDegrees, centreX, centreY, radius)
{
var angleInRadians = angleInDegrees * Math.PI / 180;
return {
'x': centreX + (radius * Math.cos(angleInRadians)),
'y': centreY + (radius * Math.sin(angleInRadians))
}
}
path {
stroke: black;
stroke-width: 1;
}
#major {
fill: #78dcdc;
}
#minor {
fill: #aaffaa;
}
<svg width="200" height="200">
<path id="major" d="" />
<path id="minor" d="" />
</svg>

OpenTK circle rotation

I'm working on my first project using openTk. I'm creating virtual arcball for 3D model rotation. It works fine, but I need to add circle which won't rotate with model. This circle should visualize arcball.
My code to achieve rotation is:
private void SetCamera()
{
GL.MatrixMode(MatrixMode.Modelview);
Matrix4 scale = Matrix4.Scale(magnification / diameter);
Matrix4 translation1 = Matrix4.CreateTranslation(-center);
Matrix4 rotation = Matrix4.CreateFromAxisAngle(axisOfRotation, angleOfRotation*(float)numericSensitivity.Value);
Matrix4 translation2 = Matrix4.CreateTranslation(0.0f, 0.0f, -1.5f);
if (rotationChanged)
{
oldRotation *= rotation;
rotationChanged = false;
}
modelview = translation1 * scale * oldRotation * translation2;
GL.LoadMatrix(ref modelview);
}
So I would like to ask if there is some way how to draw circle, which wil be unaffected by this rotattion (will be on same position on a screen).
If I understand your question correctly, then all you need to do is set the modelview matrix back to the identity before you draw your circle. You can easily do that using the PushMatrix() and PopMatrix() functions. Something like this:
//Draw normal things
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadIdentity();
//Draw un-rotated circle
GL.PopMatrix();
PushMatrix() saves the current matrix onto a stack, and PopMatrix() pops the top matrix off of that stack. This means PopMatrix() will take you back to your normal rotated frame of reference after you're done with the circle.

Resources