I'm trying to understand the SVG Path commands. However I am confused by the statement made regarding the Move command in the SVG Path reference
The SVG path reference states the following for Move command
M (uppercase) indicates that absolute coordinates will follow;
So if the current point is say (100, 100), then the command M 200 200 would move the current point to (200, 200).
m (lowercase) indicates that relative coordinates will follow.
So if the current point is say (100, 100), then the command m 200 200 would move the current point to (300, 300).
If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit lineto commands. Hence, implicit lineto commands will be relative if the moveto is relative, and absolute if the moveto is absolute.
So if the current point is (100, 100), then the command M 200 200 300 300 400 300 will first move the current point to (200,200) then draw a line to (300,300) followed by line to 400, 400.
Whereas if the current point is (100, 100), then the command m 200 200 300 300 400 400 will first move the current point to (300,300) then draw a line to (600,600) followed by line to 1000, 1000.
Now here comes the confusing part
"If a relative moveto (m) appears as the first element of the path, then it is treated as a pair of absolute coordinates. In this case, subsequent pairs of coordinates are treated as relative even though the initial moveto is interpreted as an absolute moveto."
Based on the above statement, in the previous scenario where the current point is (100, 100) then the command m 200 200 300 300 400 400 should have moved the current point to (200,200) then draw a line to (500,500) followed by line to 900, 900.
Is my understanding correct? What does the last statement really indicate?
Based on the above statement, in the previous scenario where the
current point is (100, 100) then the command m 200 200 300 300 400
400 should have moved the current point to (200,200) then draw a line
to (500,500) followed by line to 900, 900.
Is my understanding correct? What does the last statement really
indicate?
No. The current point cannot be 100,100 to invoke that. It's a bit confusing from how it's described but when that contingency comes about the current point is 0,0 the origin. Where both absolute and relative points have the same effect.
If you initialize x and y at first to be 0,0 then everything else will work itself out.
Related
SVG's path defines several commands (M, m, L, l, z, etc). However I sometime see missing/blank commands, e.g.
m 0,0 20,0 0,20 -20,0 z
It would appear that no command is a line command but I can't find this documented anywhere. Is this the case?
This is documented at several places in the SVG specification
https://www.w3.org/TR/SVG11/single-page.html#paths-PathData
The command letter can be eliminated on subsequent commands if the same command is used multiple times in a row (e.g., you can drop the second "L" in "M 100 200 L 200 100 L -100 -200" and use "M 100 200 L 200 100 -100 -200" instead).
and also under the explanation of the M command:
If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit lineto commands.
I'm trying to cut up a long SVG path generated by inkscape into several smaller paths. Specifically, I am cutting up this path:
"m 42.333333,13.895833 c 0,21.166668 21.166666,19.843751 21.166666,19.843751 h 9.260417 c 0,0 19.84375,-11.906251 13.229166,7.9375 -6.614583,19.84375 -13.229166,31.75 -33.072916,21.166667 C 33.072916,52.260417 31.75,13.895833 31.75,13.895833"
The output I generate at the moment are the following paths:
"M 42.333333,13.895833 c 0,21.166668 21.166666,19.843751 21.166666,19.843751"
"M 63.499999,33.739584 l 9.260417,0"
"M 72.760416,33.739584 c 0,0 19.84375,-11.906251 13.229166,7.9375 -6.614583,19.84375 -13.229166,31.75 -33.072916,21.166667"
"M 39.6875,54.906251 c -6.61458400000001,-2.645834 -7.93750000000001,-41.010418 -7.93750000000001,-41.010418"
This is not correct. You can see the difference between input and output here.
As you can see, almost all points are correct, except that the fourth path of the output does not start at the same point that the corresponding part of the original path does. This is probably caused by some error in my understanding of how SVG path coordinates exactly work.
I arrived at the starting point (39.6875,54.906251) for the fourth path as follows:
We start at (42.33333,13.895833). We then curve to relative position (21.166666,19.843751), so that gives us absolute position (42.33333+21.166666,13.895833+19.843751) = (63.499996,33.739584).
The path is then extended with a horizontal line with relative x coordinate 9.260417. So that gives new absolute position (63.499996 + 9.260417, 33.739584 + 0 ) = (72.760413,33.739584). We then curve to relative position (-33.072916,21.166667). Giving the start position for the fourth path as (72.760413-33.072916,33.739584+21.166667) = (39.6875,54.906251) (with some rounding).
Why is this wrong?
I figured it out myself.
The problem was caused due to the third curve actually being a polybezier consisting of 2 cubic curves. The coordinates of that second cubic bezier should be relative to its starting point, rather than the starting point of the complete polybezier.
I am trying to draw a clock that works. I am using a 600x600 form. I cant' figure out how to place the oval in the center of the form or how to add the minutes or the seconds tick marks inside the oval. I tried dash but couldn't get it to look right. Any suggestions. Thanks in advance.
This is what I have done so far:
from tkinter import *
canvas_width = 600
canvas_height = 600
master = Tk()
w = Canvas(master, width = canvas_width, height = canvas_height)
w.pack()
oval = w.create_oval(75,75,500,500)
minline = w.create_line(150,150,300,300)
mainloop()
The center of a drawn shape is the middle of the two points specified when it is drawn. Currently, the middle point of your shape (draw from 75, 75 to 500, 500) is 237.5, so, if you want the middle of it to be the middle of your page, and keep the 75, 75 coordinate, you would have to make the other one 525, 525 to completely mirror the first.
As for drawing the shape, you'll need some math in python, so I would first suggest doing an image as the background for the clock, so that less objects are drawn. But, if you must do it without other images, you must first import the math library.
import math
Now, for a mathematic principle: Any point on the circle of radius r can be expressed as the point (r*cosθ), (r*sinθ), where θ is the angle from the center to the point. The reason this is important is that you want each line on the side of the clock face to be pointing towards the center of the circle. To do that, we need the two points to draw the line on that together point towards the center, and fortunately for us this means that both points on the line are on different circles (our circle and one within it) but are at the same angle from the center.
Since we want 12 hour points around the circle, and 4 minute points between each of those (so 60 points in total), and 360 degrees in a circle (so 1 point for every 6 degrees), we will need a for loop that goes through that.
for angle in range(0, 360, 6):
Then we'll want 3 constants: One for the radius of the exterior circle (for the points to begin from), one for an interior circle (for the minute points to add at), and one for an even more interior circle (for the hour points to end at). We'll also want it to choose the more interior radius only every 30 degrees (because it appears every 5 points, and there are 6 degrees between them).
radius_out = 225
radius_in = 0 #temporary value
if (angle % 30) == 0: #the % symbol checks for remainder
radius_in = 210
else:
radius_in = 220
Now, for the conversion into radians (As math in python needs radians for sin and cos):
radians = (angle / 180) * math.pi
Next off, assigning the coordinates to variables so it's easier to read.
x_out = (radius_out * math.cos(radians)) + 300
y_out = (radius_out * math.sin(radians)) + 300
x_in = (radius_in * math.cos(radians)) + 300
y_in = (radius_in * math.sin(radians)) + 300
#the (+ 300) moves each point from a relative center of 0,0 to 300,300
And finally we assign it to a list so we can access it later if we need to. Make sure to define this list earlier outside of the for loop.
coords.append( w.create_line(x_out, y_out, x_in, y_in) )
This should give you your clock lines.
NOTE: Due to the way tkinter assigns x and y coordinates, this will draw lines from the 3 hour line clockwise back to it.
Hope this was helpful! If there is anything you don't understand, comment it below.
EDIT - Thanks for all the answers everyone. I think I accidentally led you slightly wrong as the square in the picture below should be a rectangle (I see most of you are referencing squares which seems like it would make my life a lot easier). Also, the x/y lines could go in any direction, so the red dot won't always be at the top y boundary. I was originally going for a y = mx + b solution, but then I got stuck trying to figure out how I know whether to plug in the x or the y (one of them has to be known, obviously).
I have a very simple question (I think) that I'm currently struggling with for some reason. I'm trying to have a type of minimap in my game which shows symbols around the perimeter of the view, pointing towards objectives off-screen.
Anyway, I'm trying to find the value of the red point (while the black borders and everything in green is known):
It seems like simple trigonometry, but for some reason I can't wrap my head around it. I just need to find the "new" x value from the green point to the red point, then I can utilize basic math to get the red point, but how I go about finding that new x is puzzling me.
Thanks in advance!
scale = max(abs(x), abs(y))
x = x / scale
y = y / scale
This is the simple case, for a square from (-1, -1) to (1, 1). If you want a different sized square, multiply the coordinates by sidelen / 2.
If you want a rectangle instead of a square, use the following formula. (This is another solution to the arbitrarily-sized square version)
scale = max(abs(x) / (width / 2), abs(y) / (height / 2))
x = x / scale
y = y / scale
Let's call the length of one side of the square l. The slope of the line is -y/x. That means, if you move along the line and rise a distance y toward the top of the square, then you'll move a distance x to the left. But since the green point is at the center of the square, you can rise only l/2. You can express this as a ratio:
-y -l/2
——— = ———
x d
Where d is the distance you'll move to the left. Solving for d, we have
d = xl/2y
So if the green dot is at (0, 0), the red dot is at (-l/2, xl/2y).
All you need is the angle and the width of the square w.
If the green dot is at (0,0), then the angle is a = atan(y/x), the y-coordinate of the dot is w/2, and therefore the x-coordinate of the dot is tan(1/a) * (w/2). Note that tan(1/a) == pi/2 - tan(a), or in other words the angle you really want to plug into tan is the one outside the box.
Edit: yes, this can be done without trig, too. All you need is to interpolate the x-coordinate of the dot on the line. So you know the y-coordinate is w/2, then the x-coordinate is (w/2) * x/y. But, be careful which quadrant of the square you're working with. That formula is only valid for -y<x<y, otherwise you want to reverse x and y.
The docs say that calling CTTypesetterCreateLine is the same as calling CTTypesetterCreateLineWithOffset with offset set to 0.0, but the description of what offset means is rather lacking: "The line position offset."
I've tried providing different values to it and it doesn't seem to have any impact on the typographic bounds or image bounds of the resulting CTLineRef, nor does it seem to affect the result of drawing the line using CTLineDraw. Can anyone clue me in as to the purpose of this extra parameter?
The offset is a tab offset. It doesn't apply to the line as a whole, but to the first tab-stop.
From http://lists.apple.com/archives/Coretext-dev/2011/Feb/msg00021.html
You create a string containing a tab like "A[tab]B" with Tab position at 200.
When you create a line with offset Zero and draw it at (x, y), it will appear like this.
A B
(x,y) (x+200,y)
When you create a line with offset 50 and draw it at (x + 50, y) (← you need to adjust X coordinate yourself), it will appear like this,
A B
(x+50,y) (x+200,y)
Note that "B" remains at the same position even though the line starts at a different position. If you were passing offset 0 and draw it at (x + 50, y), it would have been like the following.
A B
(x+50,y) (x+250,y)