Align asymmetrical polygons in manim - centering

I've learned to draw (via verticies) a number of asymmetrical polygons in Manim. (if it matters, the shapes I'm concerned with are the values "longa", "brevis", "semibrevis" and "minima" in this chart: https://upload.wikimedia.org/wikipedia/commons/a/a5/MensuralNotation.JPG
)
Some of them are symmetrical shapes (like ♢ or □) which are easy to align and manipulate in manim. Some however are asymmetrical (like "p" or "d") but their center/axis-of-rotation/point-of-alignment are not the center of vertices but another point (like the center of the bowl of the "p" or "d").
I'd like to have a series of VMobjects like "p ♢ □ d" all align on their defined centers (for "p" and "d" I constructed the polygons so that their origins should be the center). I've tried overriding get_center() and trying to align objects to the ORIGIN but (even after performing a .shift()) all objects assigned to align_to, next_to, etc. and a VGroup surrounding the object all ignore the center.
Is there a way to more forcefully assign the center for an object?
(Manim community fork, latest as of March 10, 2022)

Most of the internal alignment methods (when using the Cairo renderer) use the get_critical_point method of Mobject to determine the special points of a mobject that should be used for alignment. If you'd like to pursue your override-approach, then I'd recommend overriding that one.
If it is really just the custom center you would like to override, then something like
def get_critical_point(self, direction):
if np.array_equal(direction, [0, 0, 0]):
return self.custom_center
return super().get_critical_point(direction)
might already work.
It is possible that this breaks some other things though, so I can't quite tell whether it would be better overall to just implement and use some sort of custom auxiliary functions for alignment and then work with those.

Related

Using vtkImageActor for adding mask to vtkImageViewer2

I am developing on an application based on VTK and GDCM for viewing medical (DICOM) images.
The application has three windows that respectively show XY, YZ and XZ orientations (axial, coronal and sagittal). This is similar to the 2D views here. I use vtkImageViewer2 for this. The voxel values of the DICOM images are passed on to an instance of vtkImageData. The instance of the vtkImageData is the passed on the to three instances of vtkImageViewer2 (let's use imageViewerXY, imageViewerYZ and imageViewerXZ). The orientation of each instance of vtkImageViewer2 is then set using SetSliceOrientationToXY(), SetSliceOrientationToYZ() and SetSliceOrientationToXZ(). Without the mask, I can see the slices, couple the windows and scroll through the images perfectly fine.
To add the mask so that it is shown in the three views, I use vtkImageActor. For the XY view, which is the default view, this works fine. I update the instance of vtkImageActor, which I call maskActorXY based on the mouse events of XY window as follows:
int extent[6];
imageViewerXY->GetImageActor->GetDisplayExtent(extent);
maskActorXY->SetDisplayExtent(extent);
maskActorXY->Update();
imageViewerXY->GetRenerer->Render();
Now, when I do the same for the other two windows so that I can see the 3D mask in the other two orientations, for example for the YZ orientation,
imageViewerYZ->GetImageActor->GetDisplayExtent(extent);
maskActorYZ->SetDisplayExtent(extent);
maskActorYZ->Update();
imageViewerYZ->GetRenerer->Render();
I get an error message that traces to vtkImageData and accessing pixel values outside of the extent set for the mask actor.
I have a limited familiarity with VTK, but looking at the source code of vtkImageViewer2 (see UpdateDisplayExtent() on line 341), I don't understand why pixel values out side of the specified display extent are requested from my instances of vtkImageActor that represent the mask.
I found a solution. Since I am not familiar with VTK, I may not be able to provide a clear explanation. All that I needed were the following two lines for each mask to force its mappers to face the camera:
maskActorYZ->GetMapper()->SetAtFocalPointOn();
maskActorYZ->GetMapper()->SliceFacesCameraOn();
(see [vtkImageMapper3D][1] class.)

Kivy rotation during movement

I'm struggling with how to properly implement simultaneous movement and rotation using Kivy (in python, not kv lang). My goal is to rotate an object so it's facing its destination then move it towards the destination using Animation. Using the code below I typically get movement in relation to the angle rotated instead of in relation to my general playing area. For example the animation without rotation might move an image to point [1,1] whereas with rotation of 180* the animation is moving the image to [-1,-1]. The image is properly rotated in this scenario, meaning it's facing the right way but going the wrong way.
My understanding is that the push/pop matrix functions should provide the appropriate context for the animation rather than the rotated element context. Because the PopMatrix function is happening in Canvas.after it seems like this has no effect - my animation is completed before the original Matrix is restored.
I'm lacking some key piece of information here that's causing a lot of headache. Can someone explain why the code below causes an image to move to (-1,-1) rather than the (1,1) indicated, and how to properly implement this?
I threw this code together as an example, my game code is far more complex. That's why I'm hoping for an explanation rather than a direct solution for my code. Thanks.
with self.image.canvas.before:
PushMatrix()
self.rot = Rotate()
self.rot.axis = (0, 0, 1)
self.rot.origin=self.center
self.rot.angle=180
with self.image.canvas.after:
PopMatrix()
self.anim = Animation(pos = (1,1), duration=1)
self.anim.start(self)
self.image.pos = self.pos
self.image.size = self.size
In case others are interested in how to make this work consistently - I've found that setting origin and angle on each frame, along with binding the image widget to any pos change on it's parent will ensure the widget moves with its parent and in the proper direction. I implemented that like this:
Instantiate the image like this:
with self.image.canvas.before:
PushMatrix()
self.rot = Rotate()
self.rot.axis = (0, 0, 1)
self.rot.origin=self.center
self.rot.angle = 0
with self.image.canvas.after:
PopMatrix()
Bind it like this:
self.bind(pos = self.binding)
def binding(self, *args):
self.image.center = self.center
self.image.size = self.size
On each frame call a function that does similar to the below.
self.rot.origin = self.center
self.rot.angle = getangle()#you can use a set angle or generate a new angle every frame
Rotate effectively changes the coordinate system used by the entire canvas, so after you've rotated by 180 degrees the position [1, 1] really is in the opposite direction to what it was before, as far as any canvas instruction is concerned.
I don't know what self.image is (maybe an Image widget?), but presumably whatever you see is something like a Rectangle drawn on its canvas, whose pos and size match those of the widget. When you update that pos and size, the Rectangle is positioned according to the current coordinate system, which is in the rotated frame.
Thinking about it, I'm not sure if there's a neat way to combine Rotate instructions with Kivy's high level widget coordinates in quite this way. Of course you can work around it in various ways, such as by accounting for the rotation when setting the position of the Rectangle, but that's kind of fiddly, and inconvenient when working with prebuilt widgets. You can also look at what the Scatter widget does to enable arbitrary transformations.
If you just want to rotate by 180 degrees, you can instead adjust the image being displayed, either before displaying it or by adjusting the tex_coords of the Rectangle to change the displayed orientation. However, this won't work for arbitrary rotations, which it looks like you may want.

SwiftCharts cutting off points (clips bounds)

I am tweaking the Areas Example project from SwiftCharts and I am running into the issue where the point indicators are getting cut off like so:
In the github issues a found this and this other issue where the solutions were to set chartSettings.clipInnerFrame = false
However, when I do this the the area color awkward shape also becomes visible like so:
How can I get the whole shape of the points but all the other stuff?
You have to pass to the ChartPointsViewsLayer with the points clipViews: false (see example). ChartSettings.clipInnerFrame has to be true (i.e. the default). This way only the layer with the points is clipped.
Finally, if you want to clip the unclipped points layer (to limit the area outside of the chart where they are displayed), you can do it passing a custom rectangle, like here (this setting is admittedly not very intuitive).

How do I rotate or scale (transform) an SVG path relative to its center point?

I'm trying to rotate and scale shapes within an SVG around their center point. I've looked into several libraries, including Jquery, Greensock, D3, RaphaelJS, but I haven't been able to find any that provide a straightforward way to accomplish this. Each animates the shape from the origin point (which I understand is the default). I want to be able to spin a shape around its center point or scale it up or down from the center point.
Here are a couple examples using Greensock and D3 that illustrate the default behavior: http://jsbin.com/AHEXiPa/1/edit?html,js,output
Each of these examples bounce in and out from the top left as opposed to remaining stationary and expanding from the center of the triangle out in all directions.
Can one of the libraries I mentioned accomplish this, or is there another library or method I should consider?
Ideally, I need to be able to apply the animation/transform to an existing object in the DOM. D3 is good at this for instance, but Raphael seems to require converting an SVG to Raphael first prior to injecting it into the DOM.
Really its a case of pick the library that suits your needs, and then you will figure a way. As BigBadaboom says, if you do a search, there are lots of solutions.
To try and combine your questions, as sometimes the tricky bit is using an existing DOM object, I've included an example in Snap.svg. You can often do something similar in most libraries.
jsfiddle here Fiddle using your existing html.
s = Snap("#mySVGContainer1"); // create a canvas from existing svg
var triangle1 = s.select("#myShape1").transform("r90"); //select&transform existing object
p = Snap("#mySVGContainer2");
var triangle2 = p.select("#myShape2");
var bbox = triangle2.getBBox(); //bounding box, centre cx/cy
//rotate and scale with transform string (raphael/snap format)
triangle2.animate({ transform: "r180," + bbox.cx + ',' + bbox.cy + "s3,3," + bbox.cx + "," + bbox.cy }, 2000);
For rotations, as #Ian points out, you can specify the center of rotation. For other transformations, changes are defined relative to the path's (0,0) point.
The easiest way to get transformations to work relative to the path's center is to either:
Define the path so that it is centered around the (0,0) point; or
Wrap the path in a <g> element, and then translate it so it is centered on the (0,0) point of the <g> element's coordinate system.
Then, you can apply rotations, scales and transforms (on the <g> element, if using) and they will all be nicely centred.
The trickiest part is figuring out the "center" of an arbitrary shape. #Ian's approach of using the center of the bounding box will usually give decent results. If your shape is a polygon there are d3 functions you could use.
Example showing a shape moving with the mouse, rotating and changing scale, all centered around the center of the bounding box:
http://fiddle.jshell.net/LgfE3/
Edit: simplier jsfiddle
I've been looking for a long time, and will settle for the following.
1. Design your svg shape at coordinate x:0,y:0.
2. Identify by hand the center of rotation, by example, center = [ x:50,y:100].
3. Build a spinIt() function such :
function spinIt() {
needle.transition()
.duration(2000)
.attrTween("transform", tween);
function tween() {
return d3.interpolateString("rotate(-180, 50, 100)", "rotate(90, 50, 100)");
}
}
4. Use it on a triger:
svg.on("click", spinIt);
http://jsfiddle.net/SHF2M/79/

scaling d3.js projections

I'm trying to draw a map of NYC using d3. I've downloaded a bunch of shapefiles from http://www.nyc.gov/html/dcp/html/bytes/dwndistricts.shtml. I've converted them to geoJSON using http://converter.mygeodata.eu/, such that they're in WGS 84 (aka, I think, latitude and longitude).
I'm pretty sure the geoJSON is valid: I can plot it using matplotlib and it looks like NYC, and the numbers seem to be in a valid range. Here's my javascript:
var path = d3.geo.path()
d3.select("body")
.append("svg")
.attr("height", 1000)
.attr("width", 1000)
.append("g")
.selectAll("path")
.data(data.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke","black")
.style("stroke-width","1px")
This plots, as advertised, the Albers projection of NYC. Trouble is, I think, that the scale of the projection is chosen so that the US fits onto a nice webpage, making the paths for NYC a little squiggle at the right-hand-side of the screen.
What's the 'correct' way (lemme try to claim to be the first to say d3onic) to scale a geo.path() such that the extents of my lat/lon scale onto the width and height of my SVG?
(little disclaimer: apologies if I've missed something obvious, this is for a project I'm trying to complete at the extreme ends of the day)
First, you'll want to create a projection, and assign that to the d3.geo.path, so that you can customize the projection settings.
var albers = d3.geo.albers(),
path = d3.geo.path().projection(albers);
The default projection is d3.geo.albersUsa, which is actually a composite projection (with four different discontinuous areas) designed for showing the 48 states, Alaska, Hawaii and Puerto Rico. Ah, ethnocentrism. ;)
Use the albers example in the git repository to determine the correct projection settings interactively. The settings you need to set are:
the origin should be NYC's latitude and longitude (perhaps 73.98°, 40.71°)
the translate should be the center of your display area (so, if you're drawing something 960×500, you can use the default 480,250; this will be the pixel location of the origin)
the scale is some number that specifies how much to zoom-in; since you're drawing a city-scale map, you probably want a value more like 10000
Lastly, you'll need to pick some parallels. You can use the defaults provided by d3.geo.albers(), but there might be more suitable ones for NYC. Possibly check with the USGS, because they often publish standard parallels for different map areas.

Resources