Jump to content

Affine Transforms, line corners, beziers etc


aphillips

Recommended Posts

I found another bug. When you drag nubs of a polyline it draws nearby nubs as you drag so you can see what is happening. However, these nubs are drawn in untransformed coords, so if you have scaled, rotated etc then they are drawn in the wrong place.

As a few people want to use this patch I will fix the bugs and provide an update.

Link to comment
Share on other sites

Small bug,

Drawing Ellipse, when you wanna draw circle you hold shift key.

But it doesn't stay as circle. once I finished, if I let go of shift key, the shape turns back to ellipse. :?:

I was aware of this (and barkbark00 also mentioned it). The same happens for any constrained shape - square, line at fixed angles etc. (I consider it a feature since you might change your mind later :)

Its probably not that hard to fix, but I don't have time now. The workaround is to hold down shift when the shape is "finalised" in the bitmap - ie when you click outside the shape, select a different tool etc.

Link to comment
Share on other sites

I downloaded the updated version & it seems to run a lot slower than the older versoin. Even just moving the cursor across the canvas was sluggish. :?

dA

Son, someday you will make a girl happy for a short period of time. Then she'll leave you & be with men that are ten times

better than you can imagine. These men are called musicians. :D

Link to comment
Share on other sites

Sorry about the double post.

When I deselect from the Line / Curve tool, any adjustments I've made to the "nodes" are undone & the line becomes straight again.

dA

Son, someday you will make a girl happy for a short period of time. Then she'll leave you & be with men that are ten times

better than you can imagine. These men are called musicians. :D

Link to comment
Share on other sites

MattBlackLamb, the point of this ZIP that aphillips posted is not to make a perfect, bug-free "plugin" that you should actually be using a lot and relying on. It is merely to illustrate the feature's utility, get feedback, and spark up discussion on it.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

I was going to post the source code to my changes but I have posted code on the forums before and I know that Rick (or whoever) prefers to write the code himself (but is amenable to the ideas, of course). This is good since the low-level design of my code is not that good :) So I will just describe what I did. (I can still provide the source, of course.)

Line Corner Tool

----------------

The line corner toolbar drop-list was very easy to add. I just added the classes and code analogous to the Shape Draw drop-list (interior/outline/both).

In ShapeTool.RenderShape I just set the outline pen line join type like this:

outlinePen.LineJoin = AppEnvironment.LineJoin;

instead of:

outlinePen.LineJoin = LineJoin.MiterClipped;

NOTE: There are 4 line corners options provided by GDI+ but I only allowed 3 (Round, Bevel and Miter). I did not provide a MiterClipped option as this provides a sharp corner (like Miter) but reverts to flat (Bevel) when the MiterLimit is reached. This gives an ugly jump between sharp and flat corners when adjusting a corner near the MiterLimit, whereas the Miter option provides a smooth change from sharp to clipped at the MiterLimit.

I also considered, but did not provide an option for the user to set the MiterLimit value but instead hardcoded it at 2 (as before). Allowing larger values means that the way the outlineSaveRegion is calculated would have to change to allow for very sharp line corners that extend well beyond the line ends.

DoubleClick

-----------

I provided a DocumentDoubleClick event analogous to the DocumentClick event.

This allows the user to terminate entry of polygons and polylines by double-clicking.

Extra Nubs

----------

I provided several new nub shapes for scaling, rotating etc as well as for bezier nubs. I used the existing square nub for sharp corners on a polyline but added a round nub for smooth corners, and a diamond shape for bexier control points (also used for Line Tool).

One useful thing was to add some primitive shape drawing functions (DrawSquare, DrawCircle, DrawCross etc) to the MoveNubRendered class that are combined to create more complex nub shapes in OnRender (depending on the nub type of course).

Clicks

------

To allow for objects being created with multiple clicks I added a new (requiredClicks) member to the ShapeTool class to allow derived classes to say how many clicks they require. The meaning of the values are:

-1: unlimited (eg Polyline tool)

0: single click including click-drag-release drawing (eg Freeform Shape Tool)

1: 2 clicks (eg rectangle where user clicks for both corners)

2: 3 click (eg triangle)

etc

To terminate an unlimited shape (requiredClicks == -1) the user can double-click or press Enter.

The case of requireClicks == 0 is also handled specially. Only in this case are points accumulated (appended to the "points" array) while the mouse is being dragged - this allows all the points of a freeform shape to be retained. If requireClicks > 0 then the current mouse position replaces the last value of the "points" array rather than appending to it - in other words when the shape is finished the "points" array will only contain the actual points clicked.

I also found I had to add a minimumClicks member to avoid things like creating a polygon with less than 3 points.

RenderShapeBecauseOfEvent

-------------------------

As the user can now change various shape options for all shapes derived from ShapeTool (while in scale/rotate/adjust modes) I added a RenderShapeBecauseOfEvent handler to this class. This is similar to the same handler for LineTool but also handles:

- ShapeDrawTypeChanged - since (unlike LineTool) some shapes can be filled

- LineJoinChanged - to handle new LineJoin control (above)

ShapeDrawMode

-------------

Before my changes when a shape tool was active the user could either be creating a shape or not creating one. Basically this was kept tracked of by the "mouseDown" private member. I expanded on this by adding the shapeDrawMode (enum) which could take these values:

NotDrawing, // finished/not yet started

Clicking, // drawing initial shape

Scale, // shape can be scaled in X, Y or both

Scaling, // shape is being scaled (handle is being dragged)

Rotate, // shape can be rotated or skewed or centre of rotation moved

Rotating, // one of the rotate/skew handles is being dragged

Adjust, // shape can be adjusted using original click points (+ beziers for polyline)

Adjusting, // one of the adjust handles is being dragged

LineTool

--------

The ShapeTool base class does a lot with nubs that previously only LineTool did. To avoid duplicating too much code I moved some of the code around. This stuff was a bit tricky.

I created new virtual functions that are overridden in LineTool:

- OnPulse

- UpdateAdjustNubs

I removed inCurveMode which is now handled by the base class ShapeDrawMode protected member.

Mouse and Keyboard

------------------

There was a lot of changes to OnStylusDown, OnStylusMove, OnMouseMove, OnStylusUp to handle various modes mentioned above. They are fairly straightforward bu I can provide the source code.

In the Scale/Rotate/Adjust modes the use can switch to the next mode by clicking inside the bounding box but not on a nub. I also allowed the use to click on the centre transform nub to switch to the next mode as long as they don't drag it more than 2 (screen) pixels. I needed to add som special flag variables that are set in the derived class constructors to disallow some modes - eg FreeformShapeTool does not allow Adjust mode.

I also added an OnDoubleClick handler to allow finishing of polygons/lines when in Clicking mode.

I also added an OnKeyPress handler to allow Enter to finish polygons/lines. Pressing Enter when scaling etc finalises the shape in the design, or pressing the Esc key removes the shape. Note that Esc key leaves the shape as it is and calls HistoryStack.StepBackward() to get rid of it - this make the change re-doable (Ctrl+Y) which seems to be the convention in PDN.

Transforms

----------

To handle the affine transforms (moving, scaling, rotating, skewing) I added a private System.Drawing.Drawing2D.Matrix member. This is initially I, but stores any manipulations of the transform nubs. This matrix is used to render the shape and is also needed when adjusting nubs after the shape has been transformed.

To render the shape properly I created CreateTransformedPath which is called in RenderShape (instead of CreateShapePath). All it does is call the derived CreateShapePath to get the point then transforms them using PdnGraphicsPath.Tranform(matrix). It also displays info in the status bar about the current transform being done if in Scaling/Rotating/Adjusting mode.

Care is needed to apply the matrix to screen points when checking if the mouse is over a nub, dragging nubs, drawing nubs in the correct position. You also need to invert the matrix (Matrix.Invert()) when converting screen coordiantes to transformed shapes coordinates.

One thing to watch is that Matrix.Rotate takes an angle in degrees rather than radians as expected. It is also counter-clockwise from the X axis rather than clockwise from the Y-axis (as is normal for angles in degrees).

Polygon Tool

------------

The polygon tool is almost identical to the FreeformShape tool except that requiredClicks is set to -1.

Polyline Tool

-------------

The Polyline tool started out very similar to the Line Tool. The main difference being that it had a set of nubs that varied depending on the number of line segments.

When I added the sharp and smooth segment joins it became even more tricky. First, I used the "points" array to store the line segements but also the bezier points - this required making sure that the number of points is always 1 modulus 3 (ie 4,7,10,13, ...) otherwise GDI+ AddBeziers call complains.

I also needed to add a new array to keep track of which joins were sharp and which were smooth.

Finally I also had to add some virtual functions to ShapeTool and override them in PolylineTool to allow the nubs to be drawn and dragged properly in "Adjust" and "Adjusting" modes.

Link to comment
Share on other sites

  • 1 month later...

It crashed... I used esc or enter or undo to delete a curve (nubs were still appearing), and it crashed. I can't reproduce it, but I could yesterday.

This text file was created because Paint.NET crashed.
Please e-mail this file to  so we can diagnose and fix the problem.

Application version: Paint.NET v3.10 (Final Release build 3.10.2816.41879)
Time of crash: 10/31/2007 7:03:33 PM
Application uptime: 01:06:35.6406250
OS Version: 5.1.2600.131072 Service Pack 2 Workstation x86
.NET Framework version: 2.0.50727.832 x86
Processor: 2x "Intel(R) Pentium(R) D CPU 2.80GHz" @ ~2799MHz (DEP, SSE, SSE2)
Physical memory: 959 MB
Tablet PC: no
Locale: pdnr.c: en-US, hklm: en-US, hkcu: en-US, cc: en-US, cuic: en-US

Exception details:
System.InvalidOperationException: NullHistoryMementos are not undoable
  at PaintDotNet.HistoryMementos.NullHistoryMemento.OnUndo()
  at PaintDotNet.HistoryMemento.PerformUndo()
  at PaintDotNet.HistoryStack.StepBackwardImpl()
  at PaintDotNet.HistoryStack.StepBackward()
  at PaintDotNet.Tools.ShapeTool.OnKeyPress(KeyPressEventArgs e)
  at PaintDotNet.AppWorkspace.DocumentKeyPress(Object sender, KeyPressEventArgs e)
  at PaintDotNet.DocumentView.OnDocumentKeyPress(KeyPressEventArgs e)
  at PaintDotNet.DocumentView.Panel_KeyPress(Object sender, KeyPressEventArgs e)
  at System.Windows.Forms.Control.OnKeyPress(KeyPressEventArgs e)
  at System.Windows.Forms.Control.ProcessKeyEventArgs(Message& m)
  at System.Windows.Forms.Control.ProcessKeyMessage(Message& m)
  at System.Windows.Forms.Control.WmKeyChar(Message& m)
  at System.Windows.Forms.Control.WndProc(Message& m)
  at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
  at PaintDotNet.SystemLayer.ScrollPanel.WndProc(Message& m)
  at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
  at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
------------------------------------------------------------------------------
This text file was created because Paint.NET crashed.
Please e-mail this file to  so we can diagnose and fix the problem.

Application version: Paint.NET v3.10 (Final Release build 3.10.2816.41879)
Time of crash: 10/31/2007 7:03:52 PM
Application uptime: 00:00:08.4531250
OS Version: 5.1.2600.131072 Service Pack 2 Workstation x86
.NET Framework version: 2.0.50727.832 x86
Processor: 2x "Intel(R) Pentium(R) D CPU 2.80GHz" @ ~2799MHz (DEP, SSE, SSE2)
Physical memory: 959 MB
Tablet PC: no
Locale: pdnr.c: en-US, hklm: en-US, hkcu: en-US, cc: en-US, cuic: en-US

Exception details:
System.InvalidOperationException: NullHistoryMementos are not undoable
  at PaintDotNet.HistoryMementos.NullHistoryMemento.OnUndo()
  at PaintDotNet.HistoryMemento.PerformUndo()
  at PaintDotNet.HistoryStack.StepBackwardImpl()
  at PaintDotNet.HistoryStack.StepBackward()
  at PaintDotNet.Tools.ShapeTool.OnKeyPress(KeyPressEventArgs e)
  at PaintDotNet.AppWorkspace.DocumentKeyPress(Object sender, KeyPressEventArgs e)
  at PaintDotNet.DocumentView.OnDocumentKeyPress(KeyPressEventArgs e)
  at PaintDotNet.DocumentView.Panel_KeyPress(Object sender, KeyPressEventArgs e)
  at System.Windows.Forms.Control.OnKeyPress(KeyPressEventArgs e)
  at System.Windows.Forms.Control.ProcessKeyEventArgs(Message& m)
  at System.Windows.Forms.Control.ProcessKeyMessage(Message& m)
  at System.Windows.Forms.Control.WmKeyChar(Message& m)
  at System.Windows.Forms.Control.WndProc(Message& m)
  at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
  at PaintDotNet.SystemLayer.ScrollPanel.WndProc(Message& m)
  at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
  at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
  at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
------------------------------------------------------------------------------

Link to comment
Share on other sites

Ok, well this was only supposed to be a technical example not something that was going to be officially or unofficially supported.

Blooper, you should delete it and go back to the official Paint.NET.

This is just going to cause more support trouble for me if I don't close this. When people start recommending this to all sorts of newcomers over in General Discussion, then it starts getting out of control ... auto-update is disabled in this, things crash, fingers get pointed, etc.

Thread Closed, download links removed

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...